License cleanup: add SPDX GPL-2.0 license identifier to files with no license
Many source files in the tree are missing licensing information, which
makes it harder for compliance tools to determine the correct license.
By default all files without license information are under the default
license of the kernel, which is GPL version 2.
Update the files which contain no license information with the 'GPL-2.0'
SPDX license identifier. The SPDX identifier is a legally binding
shorthand, which can be used instead of the full boiler plate text.
This patch is based on work done by Thomas Gleixner and Kate Stewart and
Philippe Ombredanne.
How this work was done:
Patches were generated and checked against linux-4.14-rc6 for a subset of
the use cases:
- file had no licensing information it it.
- file was a */uapi/* one with no licensing information in it,
- file was a */uapi/* one with existing licensing information,
Further patches will be generated in subsequent months to fix up cases
where non-standard license headers were used, and references to license
had to be inferred by heuristics based on keywords.
The analysis to determine which SPDX License Identifier to be applied to
a file was done in a spreadsheet of side by side results from of the
output of two independent scanners (ScanCode & Windriver) producing SPDX
tag:value files created by Philippe Ombredanne. Philippe prepared the
base worksheet, and did an initial spot review of a few 1000 files.
The 4.13 kernel was the starting point of the analysis with 60,537 files
assessed. Kate Stewart did a file by file comparison of the scanner
results in the spreadsheet to determine which SPDX license identifier(s)
to be applied to the file. She confirmed any determination that was not
immediately clear with lawyers working with the Linux Foundation.
Criteria used to select files for SPDX license identifier tagging was:
- Files considered eligible had to be source code files.
- Make and config files were included as candidates if they contained >5
lines of source
- File already had some variant of a license header in it (even if <5
lines).
All documentation files were explicitly excluded.
The following heuristics were used to determine which SPDX license
identifiers to apply.
- when both scanners couldn't find any license traces, file was
considered to have no license information in it, and the top level
COPYING file license applied.
For non */uapi/* files that summary was:
SPDX license identifier # files
---------------------------------------------------|-------
GPL-2.0 11139
and resulted in the first patch in this series.
If that file was a */uapi/* path one, it was "GPL-2.0 WITH
Linux-syscall-note" otherwise it was "GPL-2.0". Results of that was:
SPDX license identifier # files
---------------------------------------------------|-------
GPL-2.0 WITH Linux-syscall-note 930
and resulted in the second patch in this series.
- if a file had some form of licensing information in it, and was one
of the */uapi/* ones, it was denoted with the Linux-syscall-note if
any GPL family license was found in the file or had no licensing in
it (per prior point). Results summary:
SPDX license identifier # files
---------------------------------------------------|------
GPL-2.0 WITH Linux-syscall-note 270
GPL-2.0+ WITH Linux-syscall-note 169
((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) 21
((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) 17
LGPL-2.1+ WITH Linux-syscall-note 15
GPL-1.0+ WITH Linux-syscall-note 14
((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) 5
LGPL-2.0+ WITH Linux-syscall-note 4
LGPL-2.1 WITH Linux-syscall-note 3
((GPL-2.0 WITH Linux-syscall-note) OR MIT) 3
((GPL-2.0 WITH Linux-syscall-note) AND MIT) 1
and that resulted in the third patch in this series.
- when the two scanners agreed on the detected license(s), that became
the concluded license(s).
- when there was disagreement between the two scanners (one detected a
license but the other didn't, or they both detected different
licenses) a manual inspection of the file occurred.
- In most cases a manual inspection of the information in the file
resulted in a clear resolution of the license that should apply (and
which scanner probably needed to revisit its heuristics).
- When it was not immediately clear, the license identifier was
confirmed with lawyers working with the Linux Foundation.
- If there was any question as to the appropriate license identifier,
the file was flagged for further research and to be revisited later
in time.
In total, over 70 hours of logged manual review was done on the
spreadsheet to determine the SPDX license identifiers to apply to the
source files by Kate, Philippe, Thomas and, in some cases, confirmation
by lawyers working with the Linux Foundation.
Kate also obtained a third independent scan of the 4.13 code base from
FOSSology, and compared selected files where the other two scanners
disagreed against that SPDX file, to see if there was new insights. The
Windriver scanner is based on an older version of FOSSology in part, so
they are related.
Thomas did random spot checks in about 500 files from the spreadsheets
for the uapi headers and agreed with SPDX license identifier in the
files he inspected. For the non-uapi files Thomas did random spot checks
in about 15000 files.
In initial set of patches against 4.14-rc6, 3 files were found to have
copy/paste license identifier errors, and have been fixed to reflect the
correct identifier.
Additionally Philippe spent 10 hours this week doing a detailed manual
inspection and review of the 12,461 patched files from the initial patch
version early this week with:
- a full scancode scan run, collecting the matched texts, detected
license ids and scores
- reviewing anything where there was a license detected (about 500+
files) to ensure that the applied SPDX license was correct
- reviewing anything where there was no detection but the patch license
was not GPL-2.0 WITH Linux-syscall-note to ensure that the applied
SPDX license was correct
This produced a worksheet with 20 files needing minor correction. This
worksheet was then exported into 3 different .csv files for the
different types of files to be modified.
These .csv files were then reviewed by Greg. Thomas wrote a script to
parse the csv files and add the proper SPDX tag to the file, in the
format that the file expected. This script was further refined by Greg
based on the output to detect more types of files automatically and to
distinguish between header and source .c files (which need different
comment types.) Finally Greg ran the script using the .csv files to
generate the patches.
Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org>
Reviewed-by: Philippe Ombredanne <pombredanne@nexb.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-01 14:07:57 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2009-08-07 15:22:35 +00:00
|
|
|
/*
|
|
|
|
* 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>
|
2014-09-03 12:24:58 +00:00
|
|
|
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
2022-04-12 20:09:59 +00:00
|
|
|
* Copyright 2018-2022 Intel Corporation
|
2009-08-07 15:22:35 +00:00
|
|
|
*/
|
|
|
|
|
2011-11-30 15:56:32 +00:00
|
|
|
#include <linux/export.h>
|
2020-08-26 14:31:39 +00:00
|
|
|
#include <linux/bitfield.h>
|
2009-08-07 15:22:35 +00:00
|
|
|
#include <net/cfg80211.h>
|
|
|
|
#include "core.h"
|
2012-06-27 14:19:42 +00:00
|
|
|
#include "rdev-ops.h"
|
2009-08-07 15:22:35 +00:00
|
|
|
|
2019-08-18 14:35:17 +00:00
|
|
|
static bool cfg80211_valid_60g_freq(u32 freq)
|
|
|
|
{
|
|
|
|
return freq >= 58320 && freq <= 70200;
|
|
|
|
}
|
|
|
|
|
2012-11-08 22:14:50 +00:00
|
|
|
void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
|
|
|
|
struct ieee80211_channel *chan,
|
|
|
|
enum nl80211_channel_type chan_type)
|
2010-11-13 00:31:23 +00:00
|
|
|
{
|
2012-11-08 22:14:50 +00:00
|
|
|
if (WARN_ON(!chan))
|
|
|
|
return;
|
2010-11-13 00:31:23 +00:00
|
|
|
|
2012-11-08 22:14:50 +00:00
|
|
|
chandef->chan = chan;
|
2020-04-02 01:18:03 +00:00
|
|
|
chandef->freq1_offset = chan->freq_offset;
|
2012-11-08 22:14:50 +00:00
|
|
|
chandef->center_freq2 = 0;
|
2019-08-18 14:35:17 +00:00
|
|
|
chandef->edmg.bw_config = 0;
|
|
|
|
chandef->edmg.channels = 0;
|
2012-08-27 09:49:39 +00:00
|
|
|
|
2012-11-08 22:14:50 +00:00
|
|
|
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;
|
2010-11-13 00:31:23 +00:00
|
|
|
case NL80211_CHAN_HT40PLUS:
|
2012-11-08 22:14:50 +00:00
|
|
|
chandef->width = NL80211_CHAN_WIDTH_40;
|
|
|
|
chandef->center_freq1 = chan->center_freq + 10;
|
2010-11-17 21:34:37 +00:00
|
|
|
break;
|
2010-11-13 00:31:23 +00:00
|
|
|
case NL80211_CHAN_HT40MINUS:
|
2012-11-08 22:14:50 +00:00
|
|
|
chandef->width = NL80211_CHAN_WIDTH_40;
|
|
|
|
chandef->center_freq1 = chan->center_freq - 10;
|
2010-11-17 21:34:37 +00:00
|
|
|
break;
|
2010-11-13 00:31:23 +00:00
|
|
|
default:
|
2012-11-08 22:14:50 +00:00
|
|
|
WARN_ON(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_chandef_create);
|
|
|
|
|
2019-08-18 14:35:17 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-09-08 19:03:06 +00:00
|
|
|
static 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_5:
|
|
|
|
mhz = 5;
|
|
|
|
break;
|
|
|
|
case NL80211_CHAN_WIDTH_10:
|
|
|
|
mhz = 10;
|
|
|
|
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;
|
2022-02-14 16:29:54 +00:00
|
|
|
case NL80211_CHAN_WIDTH_320:
|
|
|
|
mhz = 320;
|
|
|
|
break;
|
2020-09-08 19:03:06 +00:00
|
|
|
default:
|
|
|
|
WARN_ON_ONCE(1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return mhz;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
|
|
|
|
{
|
|
|
|
return nl80211_chan_width_to_mhz(c->width);
|
|
|
|
}
|
|
|
|
|
2012-11-22 15:59:45 +00:00
|
|
|
bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
|
2012-11-08 22:14:50 +00:00
|
|
|
{
|
2020-09-08 19:03:06 +00:00
|
|
|
u32 control_freq, oper_freq;
|
|
|
|
int oper_width, control_width;
|
2012-11-08 22:14:50 +00:00
|
|
|
|
|
|
|
if (!chandef->chan)
|
|
|
|
return false;
|
|
|
|
|
2020-04-24 10:01:04 +00:00
|
|
|
if (chandef->freq1_offset >= 1000)
|
|
|
|
return false;
|
|
|
|
|
2012-11-08 22:14:50 +00:00
|
|
|
control_freq = chandef->chan->center_freq;
|
|
|
|
|
|
|
|
switch (chandef->width) {
|
2013-05-16 11:00:28 +00:00
|
|
|
case NL80211_CHAN_WIDTH_5:
|
|
|
|
case NL80211_CHAN_WIDTH_10:
|
2012-11-08 22:14:50 +00:00
|
|
|
case NL80211_CHAN_WIDTH_20:
|
|
|
|
case NL80211_CHAN_WIDTH_20_NOHT:
|
2020-04-02 01:18:03 +00:00
|
|
|
if (ieee80211_chandef_to_khz(chandef) !=
|
|
|
|
ieee80211_channel_to_khz(chandef->chan))
|
2012-11-08 22:14:50 +00:00
|
|
|
return false;
|
|
|
|
if (chandef->center_freq2)
|
|
|
|
return false;
|
|
|
|
break;
|
2020-10-05 16:51:22 +00:00
|
|
|
case NL80211_CHAN_WIDTH_1:
|
2020-09-08 19:03:06 +00:00
|
|
|
case NL80211_CHAN_WIDTH_2:
|
|
|
|
case NL80211_CHAN_WIDTH_4:
|
|
|
|
case NL80211_CHAN_WIDTH_8:
|
|
|
|
case NL80211_CHAN_WIDTH_16:
|
2020-10-05 16:51:22 +00:00
|
|
|
if (chandef->chan->band != NL80211_BAND_S1GHZ)
|
|
|
|
return false;
|
|
|
|
|
2020-09-08 19:03:06 +00:00
|
|
|
control_freq = ieee80211_channel_to_khz(chandef->chan);
|
|
|
|
oper_freq = ieee80211_chandef_to_khz(chandef);
|
|
|
|
control_width = nl80211_chan_width_to_mhz(
|
|
|
|
ieee80211_s1g_channel_width(
|
|
|
|
chandef->chan));
|
|
|
|
oper_width = cfg80211_chandef_get_width(chandef);
|
|
|
|
|
|
|
|
if (oper_width < 0 || control_width < 0)
|
|
|
|
return false;
|
|
|
|
if (chandef->center_freq2)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (control_freq + MHZ_TO_KHZ(control_width) / 2 >
|
|
|
|
oper_freq + MHZ_TO_KHZ(oper_width) / 2)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (control_freq - MHZ_TO_KHZ(control_width) / 2 <
|
|
|
|
oper_freq - MHZ_TO_KHZ(oper_width) / 2)
|
|
|
|
return false;
|
|
|
|
break;
|
2012-11-08 22:14:50 +00:00
|
|
|
case NL80211_CHAN_WIDTH_80P80:
|
|
|
|
if (!chandef->center_freq2)
|
|
|
|
return false;
|
2012-12-13 23:19:08 +00:00
|
|
|
/* 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;
|
2012-11-08 22:14:50 +00:00
|
|
|
break;
|
2021-11-29 13:32:43 +00:00
|
|
|
default:
|
2012-11-08 22:14:50 +00:00
|
|
|
if (chandef->center_freq2)
|
|
|
|
return false;
|
|
|
|
break;
|
2021-11-29 13:32:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (chandef->width) {
|
|
|
|
case NL80211_CHAN_WIDTH_5:
|
|
|
|
case NL80211_CHAN_WIDTH_10:
|
|
|
|
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:
|
|
|
|
/* all checked above */
|
2012-11-08 22:14:50 +00:00
|
|
|
break;
|
2022-02-14 16:29:54 +00:00
|
|
|
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;
|
2021-11-29 13:32:43 +00:00
|
|
|
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;
|
2012-11-08 22:14:50 +00:00
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-10-21 07:50:45 +00:00
|
|
|
/* channel 14 is only for IEEE 802.11b */
|
|
|
|
if (chandef->center_freq1 == 2484 &&
|
|
|
|
chandef->width != NL80211_CHAN_WIDTH_20_NOHT)
|
|
|
|
return false;
|
|
|
|
|
2019-08-18 14:35:17 +00:00
|
|
|
if (cfg80211_chandef_is_edmg(chandef) &&
|
|
|
|
!cfg80211_edmg_chandef_valid(chandef))
|
|
|
|
return false;
|
|
|
|
|
2012-11-08 22:14:50 +00:00
|
|
|
return true;
|
|
|
|
}
|
2012-11-22 15:59:45 +00:00
|
|
|
EXPORT_SYMBOL(cfg80211_chandef_valid);
|
2012-11-08 22:14:50 +00:00
|
|
|
|
|
|
|
static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
|
2022-02-14 16:29:54 +00:00
|
|
|
u32 *pri40, u32 *pri80, u32 *pri160)
|
2012-11-08 22:14:50 +00:00
|
|
|
{
|
|
|
|
int tmp;
|
|
|
|
|
|
|
|
switch (c->width) {
|
|
|
|
case NL80211_CHAN_WIDTH_40:
|
|
|
|
*pri40 = c->center_freq1;
|
|
|
|
*pri80 = 0;
|
2022-02-14 16:29:54 +00:00
|
|
|
*pri160 = 0;
|
2012-11-08 22:14:50 +00:00
|
|
|
break;
|
|
|
|
case NL80211_CHAN_WIDTH_80:
|
|
|
|
case NL80211_CHAN_WIDTH_80P80:
|
2022-02-14 16:29:54 +00:00
|
|
|
*pri160 = 0;
|
2012-11-08 22:14:50 +00:00
|
|
|
*pri80 = c->center_freq1;
|
|
|
|
/* n_P20 */
|
|
|
|
tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
|
|
|
|
/* n_P40 */
|
|
|
|
tmp /= 2;
|
|
|
|
/* freq_P40 */
|
|
|
|
*pri40 = c->center_freq1 - 20 + 40 * tmp;
|
|
|
|
break;
|
|
|
|
case NL80211_CHAN_WIDTH_160:
|
2022-02-14 16:29:54 +00:00
|
|
|
*pri160 = c->center_freq1;
|
2012-11-08 22:14:50 +00:00
|
|
|
/* n_P20 */
|
|
|
|
tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
|
|
|
|
/* n_P40 */
|
|
|
|
tmp /= 2;
|
|
|
|
/* freq_P40 */
|
|
|
|
*pri40 = c->center_freq1 - 60 + 40 * tmp;
|
|
|
|
/* n_P80 */
|
|
|
|
tmp /= 2;
|
|
|
|
*pri80 = c->center_freq1 - 40 + 80 * tmp;
|
|
|
|
break;
|
2022-02-14 16:29:54 +00:00
|
|
|
case NL80211_CHAN_WIDTH_320:
|
|
|
|
/* n_P20 */
|
|
|
|
tmp = (150 + c->chan->center_freq - c->center_freq1) / 20;
|
|
|
|
/* n_P40 */
|
|
|
|
tmp /= 2;
|
|
|
|
/* freq_P40 */
|
|
|
|
*pri40 = c->center_freq1 - 140 + 40 * tmp;
|
|
|
|
/* n_P80 */
|
|
|
|
tmp /= 2;
|
|
|
|
*pri80 = c->center_freq1 - 120 + 80 * tmp;
|
|
|
|
/* n_P160 */
|
|
|
|
tmp /= 2;
|
|
|
|
*pri160 = c->center_freq1 - 80 + 160 * tmp;
|
|
|
|
break;
|
2012-11-08 22:14:50 +00:00
|
|
|
default:
|
|
|
|
WARN_ON_ONCE(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct cfg80211_chan_def *
|
|
|
|
cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
|
|
|
|
const struct cfg80211_chan_def *c2)
|
|
|
|
{
|
2022-02-14 16:29:54 +00:00
|
|
|
u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80, c1_pri160, c2_pri160;
|
2012-11-08 22:14:50 +00:00
|
|
|
|
|
|
|
/* If they are identical, return */
|
|
|
|
if (cfg80211_chandef_identical(c1, c2))
|
|
|
|
return c1;
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2013-05-16 11:00:28 +00:00
|
|
|
/*
|
|
|
|
* can't be compatible if one of them is 5 or 10 MHz,
|
|
|
|
* but they don't have the same width.
|
|
|
|
*/
|
|
|
|
if (c1->width == NL80211_CHAN_WIDTH_5 ||
|
|
|
|
c1->width == NL80211_CHAN_WIDTH_10 ||
|
|
|
|
c2->width == NL80211_CHAN_WIDTH_5 ||
|
|
|
|
c2->width == NL80211_CHAN_WIDTH_10)
|
|
|
|
return NULL;
|
|
|
|
|
2012-11-08 22:14:50 +00:00
|
|
|
if (c1->width == NL80211_CHAN_WIDTH_20_NOHT ||
|
|
|
|
c1->width == NL80211_CHAN_WIDTH_20)
|
|
|
|
return c2;
|
|
|
|
|
|
|
|
if (c2->width == NL80211_CHAN_WIDTH_20_NOHT ||
|
|
|
|
c2->width == NL80211_CHAN_WIDTH_20)
|
|
|
|
return c1;
|
|
|
|
|
2022-02-14 16:29:54 +00:00
|
|
|
chandef_primary_freqs(c1, &c1_pri40, &c1_pri80, &c1_pri160);
|
|
|
|
chandef_primary_freqs(c2, &c2_pri40, &c2_pri80, &c2_pri160);
|
2012-11-08 22:14:50 +00:00
|
|
|
|
|
|
|
if (c1_pri40 != c2_pri40)
|
|
|
|
return NULL;
|
|
|
|
|
2022-02-14 16:29:54 +00:00
|
|
|
if (c1->width == NL80211_CHAN_WIDTH_40)
|
|
|
|
return c2;
|
|
|
|
|
|
|
|
if (c2->width == NL80211_CHAN_WIDTH_40)
|
|
|
|
return c1;
|
|
|
|
|
|
|
|
if (c1_pri80 != c2_pri80)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (c1->width == NL80211_CHAN_WIDTH_80 &&
|
|
|
|
c2->width > NL80211_CHAN_WIDTH_80)
|
|
|
|
return c2;
|
|
|
|
|
|
|
|
if (c2->width == NL80211_CHAN_WIDTH_80 &&
|
|
|
|
c1->width > NL80211_CHAN_WIDTH_80)
|
|
|
|
return c1;
|
|
|
|
|
|
|
|
WARN_ON(!c1_pri160 && !c2_pri160);
|
|
|
|
if (c1_pri160 && c2_pri160 && c1_pri160 != c2_pri160)
|
2012-11-08 22:14:50 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (c1->width > c2->width)
|
|
|
|
return c1;
|
|
|
|
return c2;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_chandef_compatible);
|
|
|
|
|
2013-02-08 17:16:19 +00:00
|
|
|
static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq,
|
|
|
|
u32 bandwidth,
|
|
|
|
enum nl80211_dfs_state dfs_state)
|
|
|
|
{
|
|
|
|
struct ieee80211_channel *c;
|
|
|
|
u32 freq;
|
|
|
|
|
|
|
|
for (freq = center_freq - bandwidth/2 + 10;
|
|
|
|
freq <= center_freq + bandwidth/2 - 10;
|
|
|
|
freq += 20) {
|
|
|
|
c = ieee80211_get_channel(wiphy, freq);
|
|
|
|
if (!c || !(c->flags & IEEE80211_CHAN_RADAR))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
c->dfs_state = dfs_state;
|
|
|
|
c->dfs_state_entered = jiffies;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cfg80211_set_dfs_state(struct wiphy *wiphy,
|
|
|
|
const struct cfg80211_chan_def *chandef,
|
|
|
|
enum nl80211_dfs_state dfs_state)
|
|
|
|
{
|
|
|
|
int width;
|
|
|
|
|
|
|
|
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
width = cfg80211_chandef_get_width(chandef);
|
|
|
|
if (width < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq1,
|
|
|
|
width, dfs_state);
|
|
|
|
|
|
|
|
if (!chandef->center_freq2)
|
|
|
|
return;
|
|
|
|
cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq2,
|
|
|
|
width, dfs_state);
|
|
|
|
}
|
|
|
|
|
2013-11-05 13:48:47 +00:00
|
|
|
static u32 cfg80211_get_start_freq(u32 center_freq,
|
|
|
|
u32 bandwidth)
|
|
|
|
{
|
|
|
|
u32 start_freq;
|
|
|
|
|
2020-04-02 01:18:03 +00:00
|
|
|
bandwidth = MHZ_TO_KHZ(bandwidth);
|
|
|
|
if (bandwidth <= MHZ_TO_KHZ(20))
|
2013-11-05 13:48:47 +00:00
|
|
|
start_freq = center_freq;
|
|
|
|
else
|
2020-04-02 01:18:03 +00:00
|
|
|
start_freq = center_freq - bandwidth / 2 + MHZ_TO_KHZ(10);
|
2013-11-05 13:48:47 +00:00
|
|
|
|
|
|
|
return start_freq;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 cfg80211_get_end_freq(u32 center_freq,
|
|
|
|
u32 bandwidth)
|
|
|
|
{
|
|
|
|
u32 end_freq;
|
|
|
|
|
2020-04-02 01:18:03 +00:00
|
|
|
bandwidth = MHZ_TO_KHZ(bandwidth);
|
|
|
|
if (bandwidth <= MHZ_TO_KHZ(20))
|
2013-11-05 13:48:47 +00:00
|
|
|
end_freq = center_freq;
|
|
|
|
else
|
2020-04-02 01:18:03 +00:00
|
|
|
end_freq = center_freq + bandwidth / 2 - MHZ_TO_KHZ(10);
|
2013-11-05 13:48:47 +00:00
|
|
|
|
|
|
|
return end_freq;
|
|
|
|
}
|
|
|
|
|
2013-02-08 17:16:19 +00:00
|
|
|
static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
|
|
|
|
u32 center_freq,
|
|
|
|
u32 bandwidth)
|
|
|
|
{
|
|
|
|
struct ieee80211_channel *c;
|
2013-05-16 11:00:28 +00:00
|
|
|
u32 freq, start_freq, end_freq;
|
|
|
|
|
2013-11-05 13:48:47 +00:00
|
|
|
start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
|
|
|
|
end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
|
2013-02-08 17:16:19 +00:00
|
|
|
|
2020-04-02 01:18:03 +00:00
|
|
|
for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
|
|
|
|
c = ieee80211_get_channel_khz(wiphy, freq);
|
2013-02-08 17:16:19 +00:00
|
|
|
if (!c)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (c->flags & IEEE80211_CHAN_RADAR)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
|
2014-02-18 09:40:36 +00:00
|
|
|
const struct cfg80211_chan_def *chandef,
|
|
|
|
enum nl80211_iftype iftype)
|
2013-02-08 17:16:19 +00:00
|
|
|
{
|
|
|
|
int width;
|
2014-02-18 09:40:36 +00:00
|
|
|
int ret;
|
2013-02-08 17:16:19 +00:00
|
|
|
|
|
|
|
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2014-02-18 09:40:36 +00:00
|
|
|
switch (iftype) {
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
|
|
width = cfg80211_chandef_get_width(chandef);
|
|
|
|
if (width < 0)
|
|
|
|
return -EINVAL;
|
2013-02-08 17:16:19 +00:00
|
|
|
|
2014-02-18 09:40:36 +00:00
|
|
|
ret = cfg80211_get_chans_dfs_required(wiphy,
|
2020-04-02 01:18:03 +00:00
|
|
|
ieee80211_chandef_to_khz(chandef),
|
|
|
|
width);
|
2014-02-18 09:40:36 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
else if (ret > 0)
|
|
|
|
return BIT(chandef->width);
|
2013-02-08 17:16:19 +00:00
|
|
|
|
2014-02-18 09:40:36 +00:00
|
|
|
if (!chandef->center_freq2)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ret = cfg80211_get_chans_dfs_required(wiphy,
|
2020-04-02 01:18:03 +00:00
|
|
|
MHZ_TO_KHZ(chandef->center_freq2),
|
|
|
|
width);
|
2014-02-18 09:40:36 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
else if (ret > 0)
|
|
|
|
return BIT(chandef->width);
|
2013-02-08 17:16:19 +00:00
|
|
|
|
2014-02-18 09:40:36 +00:00
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_STATION:
|
2014-11-03 09:33:18 +00:00
|
|
|
case NL80211_IFTYPE_OCB:
|
2014-02-18 09:40:36 +00:00
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
|
|
|
case NL80211_IFTYPE_MONITOR:
|
|
|
|
case NL80211_IFTYPE_AP_VLAN:
|
|
|
|
case NL80211_IFTYPE_P2P_DEVICE:
|
2016-09-20 14:31:13 +00:00
|
|
|
case NL80211_IFTYPE_NAN:
|
2014-02-18 09:40:36 +00:00
|
|
|
break;
|
2020-11-09 09:57:47 +00:00
|
|
|
case NL80211_IFTYPE_WDS:
|
2014-05-15 10:05:39 +00:00
|
|
|
case NL80211_IFTYPE_UNSPECIFIED:
|
2014-02-18 09:40:36 +00:00
|
|
|
case NUM_NL80211_IFTYPES:
|
|
|
|
WARN_ON(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2013-02-08 17:16:19 +00:00
|
|
|
}
|
2013-08-28 11:41:28 +00:00
|
|
|
EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
|
2013-02-08 17:16:19 +00:00
|
|
|
|
2013-11-05 13:48:48 +00:00
|
|
|
static int cfg80211_get_chans_dfs_usable(struct wiphy *wiphy,
|
|
|
|
u32 center_freq,
|
|
|
|
u32 bandwidth)
|
|
|
|
{
|
|
|
|
struct ieee80211_channel *c;
|
|
|
|
u32 freq, start_freq, end_freq;
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
|
|
|
|
end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2020-04-02 01:18:03 +00:00
|
|
|
for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
|
|
|
|
c = ieee80211_get_channel_khz(wiphy, freq);
|
2013-11-05 13:48:48 +00:00
|
|
|
if (!c)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (c->flags & IEEE80211_CHAN_DISABLED)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (c->flags & IEEE80211_CHAN_RADAR) {
|
|
|
|
if (c->dfs_state == NL80211_DFS_UNAVAILABLE)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (c->dfs_state == NL80211_DFS_USABLE)
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
|
|
|
|
const struct cfg80211_chan_def *chandef)
|
|
|
|
{
|
|
|
|
int width;
|
|
|
|
int r1, r2 = 0;
|
|
|
|
|
|
|
|
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
width = cfg80211_chandef_get_width(chandef);
|
|
|
|
if (width < 0)
|
|
|
|
return false;
|
|
|
|
|
2020-04-02 01:18:03 +00:00
|
|
|
r1 = cfg80211_get_chans_dfs_usable(wiphy,
|
|
|
|
MHZ_TO_KHZ(chandef->center_freq1),
|
|
|
|
width);
|
2013-11-05 13:48:48 +00:00
|
|
|
|
|
|
|
if (r1 < 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
switch (chandef->width) {
|
|
|
|
case NL80211_CHAN_WIDTH_80P80:
|
|
|
|
WARN_ON(!chandef->center_freq2);
|
|
|
|
r2 = cfg80211_get_chans_dfs_usable(wiphy,
|
2020-04-02 01:18:03 +00:00
|
|
|
MHZ_TO_KHZ(chandef->center_freq2),
|
|
|
|
width);
|
2013-11-05 13:48:48 +00:00
|
|
|
if (r2 < 0)
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
WARN_ON(chandef->center_freq2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (r1 + r2 > 0);
|
|
|
|
}
|
|
|
|
|
2017-02-27 11:34:33 +00:00
|
|
|
/*
|
|
|
|
* Checks if center frequency of chan falls with in the bandwidth
|
|
|
|
* range of chandef.
|
|
|
|
*/
|
|
|
|
bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef,
|
2022-04-14 14:50:57 +00:00
|
|
|
struct ieee80211_channel *chan,
|
|
|
|
bool primary_only)
|
2017-02-27 11:34:33 +00:00
|
|
|
{
|
|
|
|
int width;
|
2017-10-12 09:23:04 +00:00
|
|
|
u32 freq;
|
2017-02-27 11:34:33 +00:00
|
|
|
|
2022-04-14 14:50:57 +00:00
|
|
|
if (!chandef->chan)
|
|
|
|
return false;
|
|
|
|
|
2017-02-27 11:34:33 +00:00
|
|
|
if (chandef->chan->center_freq == chan->center_freq)
|
|
|
|
return true;
|
|
|
|
|
2022-04-14 14:50:57 +00:00
|
|
|
if (primary_only)
|
|
|
|
return false;
|
|
|
|
|
2017-02-27 11:34:33 +00:00
|
|
|
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)
|
|
|
|
{
|
2022-04-14 14:50:57 +00:00
|
|
|
unsigned int link;
|
2017-02-27 11:34:33 +00:00
|
|
|
|
|
|
|
ASSERT_WDEV_LOCK(wdev);
|
|
|
|
|
|
|
|
switch (wdev->iftype) {
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
2022-04-14 14:50:57 +00:00
|
|
|
for_each_valid_link(wdev, link) {
|
|
|
|
if (wdev->links[link].ap.beacon_interval)
|
|
|
|
return true;
|
|
|
|
}
|
2017-02-27 11:34:33 +00:00
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
2022-04-14 14:50:57 +00:00
|
|
|
if (wdev->u.ibss.ssid_len)
|
|
|
|
return true;
|
2017-02-27 11:34:33 +00:00
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
2022-04-14 14:50:57 +00:00
|
|
|
if (wdev->u.mesh.id_len)
|
|
|
|
return true;
|
2017-02-27 11:34:33 +00:00
|
|
|
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:
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_UNSPECIFIED:
|
2020-11-09 09:57:47 +00:00
|
|
|
case NL80211_IFTYPE_WDS:
|
2017-02-27 11:34:33 +00:00
|
|
|
case NUM_NL80211_IFTYPES:
|
|
|
|
WARN_ON(1);
|
|
|
|
}
|
|
|
|
|
2022-04-14 14:50:57 +00:00
|
|
|
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;
|
2017-02-27 11:34:33 +00:00
|
|
|
}
|
|
|
|
|
2017-02-27 11:34:35 +00:00
|
|
|
static bool cfg80211_is_wiphy_oper_chan(struct wiphy *wiphy,
|
|
|
|
struct ieee80211_channel *chan)
|
2017-02-27 11:34:33 +00:00
|
|
|
{
|
|
|
|
struct wireless_dev *wdev;
|
|
|
|
|
|
|
|
list_for_each_entry(wdev, &wiphy->wdev_list, list) {
|
|
|
|
wdev_lock(wdev);
|
|
|
|
if (!cfg80211_beaconing_iface_active(wdev)) {
|
|
|
|
wdev_unlock(wdev);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-04-14 14:50:57 +00:00
|
|
|
if (cfg80211_wdev_on_sub_chan(wdev, chan, false)) {
|
2017-02-27 11:34:33 +00:00
|
|
|
wdev_unlock(wdev);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
wdev_unlock(wdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2013-11-05 13:48:48 +00:00
|
|
|
|
2021-11-16 14:03:36 +00:00
|
|
|
static bool
|
|
|
|
cfg80211_offchan_chain_is_active(struct cfg80211_registered_device *rdev,
|
|
|
|
struct ieee80211_channel *channel)
|
|
|
|
{
|
2021-11-29 13:11:24 +00:00
|
|
|
if (!rdev->background_radar_wdev)
|
2021-11-16 14:03:36 +00:00
|
|
|
return false;
|
|
|
|
|
2021-11-29 13:11:24 +00:00
|
|
|
if (!cfg80211_chandef_valid(&rdev->background_radar_chandef))
|
2021-11-16 14:03:36 +00:00
|
|
|
return false;
|
|
|
|
|
2022-04-14 14:50:57 +00:00
|
|
|
return cfg80211_is_sub_chan(&rdev->background_radar_chandef, channel,
|
|
|
|
false);
|
2021-11-16 14:03:36 +00:00
|
|
|
}
|
|
|
|
|
2017-02-27 11:34:35 +00:00
|
|
|
bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
|
|
|
|
struct ieee80211_channel *chan)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
|
|
|
|
ASSERT_RTNL();
|
|
|
|
|
|
|
|
if (!(chan->flags & IEEE80211_CHAN_RADAR))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
|
|
|
|
if (!reg_dfs_domain_same(wiphy, &rdev->wiphy))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan))
|
|
|
|
return true;
|
2021-11-16 14:03:36 +00:00
|
|
|
|
|
|
|
if (cfg80211_offchan_chain_is_active(rdev, chan))
|
|
|
|
return true;
|
2017-02-27 11:34:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-11-06 12:55:53 +00:00
|
|
|
static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
|
|
|
|
u32 center_freq,
|
|
|
|
u32 bandwidth)
|
2012-11-08 22:14:50 +00:00
|
|
|
{
|
|
|
|
struct ieee80211_channel *c;
|
2013-05-16 11:00:28 +00:00
|
|
|
u32 freq, start_freq, end_freq;
|
2018-03-26 13:36:32 +00:00
|
|
|
bool dfs_offload;
|
|
|
|
|
|
|
|
dfs_offload = wiphy_ext_feature_isset(wiphy,
|
|
|
|
NL80211_EXT_FEATURE_DFS_OFFLOAD);
|
2013-05-16 11:00:28 +00:00
|
|
|
|
2013-11-05 13:48:47 +00:00
|
|
|
start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
|
|
|
|
end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
|
2012-11-08 22:14:50 +00:00
|
|
|
|
2013-11-06 12:55:53 +00:00
|
|
|
/*
|
|
|
|
* Check entire range of channels for the bandwidth.
|
|
|
|
* If any channel in between is disabled or has not
|
|
|
|
* had gone through CAC return false
|
|
|
|
*/
|
2020-04-02 01:18:03 +00:00
|
|
|
for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
|
|
|
|
c = ieee80211_get_channel_khz(wiphy, freq);
|
2013-02-08 17:16:19 +00:00
|
|
|
if (!c)
|
|
|
|
return false;
|
|
|
|
|
2013-11-06 12:55:53 +00:00
|
|
|
if (c->flags & IEEE80211_CHAN_DISABLED)
|
|
|
|
return false;
|
|
|
|
|
2018-03-26 13:36:32 +00:00
|
|
|
if ((c->flags & IEEE80211_CHAN_RADAR) &&
|
|
|
|
(c->dfs_state != NL80211_DFS_AVAILABLE) &&
|
|
|
|
!(c->dfs_state == NL80211_DFS_USABLE && dfs_offload))
|
2013-02-08 17:16:19 +00:00
|
|
|
return false;
|
2013-11-06 12:55:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
|
|
|
|
const struct cfg80211_chan_def *chandef)
|
|
|
|
{
|
|
|
|
int width;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
width = cfg80211_chandef_get_width(chandef);
|
|
|
|
if (width < 0)
|
|
|
|
return false;
|
|
|
|
|
2020-04-02 01:18:03 +00:00
|
|
|
r = cfg80211_get_chans_dfs_available(wiphy,
|
|
|
|
MHZ_TO_KHZ(chandef->center_freq1),
|
2013-11-06 12:55:53 +00:00
|
|
|
width);
|
|
|
|
|
|
|
|
/* If any of channels unavailable for cf1 just return */
|
|
|
|
if (!r)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
switch (chandef->width) {
|
|
|
|
case NL80211_CHAN_WIDTH_80P80:
|
|
|
|
WARN_ON(!chandef->center_freq2);
|
|
|
|
r = cfg80211_get_chans_dfs_available(wiphy,
|
2020-04-02 01:18:03 +00:00
|
|
|
MHZ_TO_KHZ(chandef->center_freq2),
|
|
|
|
width);
|
2016-07-17 18:55:27 +00:00
|
|
|
break;
|
2013-11-06 12:55:53 +00:00
|
|
|
default:
|
|
|
|
WARN_ON(chandef->center_freq2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-02-21 18:46:13 +00:00
|
|
|
static unsigned int cfg80211_get_chans_dfs_cac_time(struct wiphy *wiphy,
|
|
|
|
u32 center_freq,
|
|
|
|
u32 bandwidth)
|
|
|
|
{
|
|
|
|
struct ieee80211_channel *c;
|
|
|
|
u32 start_freq, end_freq, freq;
|
|
|
|
unsigned int dfs_cac_ms = 0;
|
|
|
|
|
|
|
|
start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
|
|
|
|
end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
|
|
|
|
|
2020-04-02 01:18:03 +00:00
|
|
|
for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
|
|
|
|
c = ieee80211_get_channel_khz(wiphy, freq);
|
2014-02-21 18:46:13 +00:00
|
|
|
if (!c)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (c->flags & IEEE80211_CHAN_DISABLED)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!(c->flags & IEEE80211_CHAN_RADAR))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (c->dfs_cac_ms > dfs_cac_ms)
|
|
|
|
dfs_cac_ms = c->dfs_cac_ms;
|
|
|
|
}
|
|
|
|
|
|
|
|
return dfs_cac_ms;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
|
|
|
|
const struct cfg80211_chan_def *chandef)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
|
|
|
t1 = cfg80211_get_chans_dfs_cac_time(wiphy,
|
2020-04-02 01:18:03 +00:00
|
|
|
MHZ_TO_KHZ(chandef->center_freq1),
|
2014-02-21 18:46:13 +00:00
|
|
|
width);
|
|
|
|
|
|
|
|
if (!chandef->center_freq2)
|
|
|
|
return t1;
|
|
|
|
|
|
|
|
t2 = cfg80211_get_chans_dfs_cac_time(wiphy,
|
2020-04-02 01:18:03 +00:00
|
|
|
MHZ_TO_KHZ(chandef->center_freq2),
|
2014-02-21 18:46:13 +00:00
|
|
|
width);
|
|
|
|
|
|
|
|
return max(t1, t2);
|
|
|
|
}
|
2013-11-06 12:55:53 +00:00
|
|
|
|
|
|
|
static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
|
|
|
|
u32 center_freq, u32 bandwidth,
|
|
|
|
u32 prohibited_flags)
|
|
|
|
{
|
|
|
|
struct ieee80211_channel *c;
|
|
|
|
u32 freq, start_freq, end_freq;
|
2013-02-08 17:16:19 +00:00
|
|
|
|
2013-11-06 12:55:53 +00:00
|
|
|
start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
|
|
|
|
end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
|
|
|
|
|
2020-04-02 01:18:03 +00:00
|
|
|
for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
|
|
|
|
c = ieee80211_get_channel_khz(wiphy, freq);
|
2013-11-06 12:55:53 +00:00
|
|
|
if (!c || c->flags & prohibited_flags)
|
2012-11-08 22:14:50 +00:00
|
|
|
return false;
|
2010-11-13 00:31:23 +00:00
|
|
|
}
|
|
|
|
|
2012-11-08 22:14:50 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-08-18 14:35:17 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2012-11-22 15:59:45 +00:00
|
|
|
bool cfg80211_chandef_usable(struct wiphy *wiphy,
|
|
|
|
const struct cfg80211_chan_def *chandef,
|
|
|
|
u32 prohibited_flags)
|
2012-11-08 22:14:50 +00:00
|
|
|
{
|
2012-11-22 15:59:45 +00:00
|
|
|
struct ieee80211_sta_ht_cap *ht_cap;
|
|
|
|
struct ieee80211_sta_vht_cap *vht_cap;
|
2019-08-18 14:35:17 +00:00
|
|
|
struct ieee80211_edmg *edmg_cap;
|
2014-12-11 21:48:55 +00:00
|
|
|
u32 width, control_freq, cap;
|
2022-02-14 16:29:54 +00:00
|
|
|
bool ext_nss_cap, support_80_80 = false, support_320 = false;
|
|
|
|
const struct ieee80211_sband_iftype_data *iftd;
|
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
|
int i;
|
2012-11-08 22:14:50 +00:00
|
|
|
|
2012-11-22 15:59:45 +00:00
|
|
|
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
|
|
|
return false;
|
2012-11-08 22:14:50 +00:00
|
|
|
|
2012-11-22 15:59:45 +00:00
|
|
|
ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
|
|
|
|
vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
|
2019-08-18 14:35:17 +00:00
|
|
|
edmg_cap = &wiphy->bands[chandef->chan->band]->edmg_cap;
|
2021-05-24 03:36:24 +00:00
|
|
|
ext_nss_cap = __le16_to_cpu(vht_cap->vht_mcs.tx_highest) &
|
|
|
|
IEEE80211_VHT_EXT_NSS_BW_CAPABLE;
|
2019-08-18 14:35:17 +00:00
|
|
|
|
|
|
|
if (edmg_cap->channels &&
|
|
|
|
!cfg80211_edmg_usable(wiphy,
|
|
|
|
chandef->edmg.channels,
|
|
|
|
chandef->edmg.bw_config,
|
|
|
|
chandef->chan->hw_value,
|
|
|
|
edmg_cap))
|
|
|
|
return false;
|
2012-11-08 22:14:50 +00:00
|
|
|
|
2012-11-22 15:59:45 +00:00
|
|
|
control_freq = chandef->chan->center_freq;
|
2010-11-13 00:31:23 +00:00
|
|
|
|
2012-11-08 22:14:50 +00:00
|
|
|
switch (chandef->width) {
|
2020-06-02 06:22:47 +00:00
|
|
|
case NL80211_CHAN_WIDTH_1:
|
|
|
|
width = 1;
|
|
|
|
break;
|
|
|
|
case NL80211_CHAN_WIDTH_2:
|
|
|
|
width = 2;
|
|
|
|
break;
|
|
|
|
case NL80211_CHAN_WIDTH_4:
|
|
|
|
width = 4;
|
|
|
|
break;
|
|
|
|
case NL80211_CHAN_WIDTH_8:
|
|
|
|
width = 8;
|
|
|
|
break;
|
|
|
|
case NL80211_CHAN_WIDTH_16:
|
|
|
|
width = 16;
|
|
|
|
break;
|
2013-05-16 11:00:28 +00:00
|
|
|
case NL80211_CHAN_WIDTH_5:
|
|
|
|
width = 5;
|
|
|
|
break;
|
|
|
|
case NL80211_CHAN_WIDTH_10:
|
2014-04-15 12:37:55 +00:00
|
|
|
prohibited_flags |= IEEE80211_CHAN_NO_10MHZ;
|
2013-05-16 11:00:28 +00:00
|
|
|
width = 10;
|
|
|
|
break;
|
2012-11-08 22:14:50 +00:00
|
|
|
case NL80211_CHAN_WIDTH_20:
|
2020-05-28 19:34:40 +00:00
|
|
|
if (!ht_cap->ht_supported &&
|
|
|
|
chandef->chan->band != NL80211_BAND_6GHZ)
|
2012-11-22 15:59:45 +00:00
|
|
|
return false;
|
2020-08-23 22:36:59 +00:00
|
|
|
fallthrough;
|
2012-11-22 15:59:45 +00:00
|
|
|
case NL80211_CHAN_WIDTH_20_NOHT:
|
2014-04-15 12:37:55 +00:00
|
|
|
prohibited_flags |= IEEE80211_CHAN_NO_20MHZ;
|
2012-11-08 22:14:50 +00:00
|
|
|
width = 20;
|
|
|
|
break;
|
|
|
|
case NL80211_CHAN_WIDTH_40:
|
|
|
|
width = 40;
|
2020-05-28 19:34:40 +00:00
|
|
|
if (chandef->chan->band == NL80211_BAND_6GHZ)
|
|
|
|
break;
|
2012-11-22 15:59:45 +00:00
|
|
|
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;
|
2012-11-08 22:14:50 +00:00
|
|
|
break;
|
|
|
|
case NL80211_CHAN_WIDTH_80P80:
|
2020-08-26 14:31:39 +00:00
|
|
|
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) ||
|
2021-05-24 03:36:24 +00:00
|
|
|
(ext_nss_cap &&
|
|
|
|
u32_get_bits(cap, IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) > 1);
|
2020-08-26 14:31:39 +00:00
|
|
|
if (chandef->chan->band != NL80211_BAND_6GHZ && !support_80_80)
|
2012-11-22 15:59:45 +00:00
|
|
|
return false;
|
2020-08-23 22:36:59 +00:00
|
|
|
fallthrough;
|
2012-11-22 15:59:45 +00:00
|
|
|
case NL80211_CHAN_WIDTH_80:
|
2012-12-12 16:50:39 +00:00
|
|
|
prohibited_flags |= IEEE80211_CHAN_NO_80MHZ;
|
2012-11-08 22:14:50 +00:00
|
|
|
width = 80;
|
2020-05-28 19:34:40 +00:00
|
|
|
if (chandef->chan->band == NL80211_BAND_6GHZ)
|
|
|
|
break;
|
|
|
|
if (!vht_cap->vht_supported)
|
|
|
|
return false;
|
2012-11-08 22:14:50 +00:00
|
|
|
break;
|
|
|
|
case NL80211_CHAN_WIDTH_160:
|
2020-05-28 19:34:40 +00:00
|
|
|
prohibited_flags |= IEEE80211_CHAN_NO_160MHZ;
|
|
|
|
width = 160;
|
|
|
|
if (chandef->chan->band == NL80211_BAND_6GHZ)
|
|
|
|
break;
|
2012-11-22 15:59:45 +00:00
|
|
|
if (!vht_cap->vht_supported)
|
|
|
|
return false;
|
2014-12-11 21:48:55 +00:00
|
|
|
cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
|
|
|
|
if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ &&
|
2020-08-26 14:31:39 +00:00
|
|
|
cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ &&
|
2021-05-24 03:36:24 +00:00
|
|
|
!(ext_nss_cap &&
|
|
|
|
(vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK)))
|
2012-11-22 15:59:45 +00:00
|
|
|
return false;
|
2012-11-08 22:14:50 +00:00
|
|
|
break;
|
2022-02-14 16:29:54 +00:00
|
|
|
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 (i = 0; i < sband->n_iftype_data; i++) {
|
|
|
|
iftd = &sband->iftype_data[i];
|
|
|
|
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;
|
2012-11-08 22:14:50 +00:00
|
|
|
default:
|
|
|
|
WARN_ON_ONCE(1);
|
2010-11-13 00:31:23 +00:00
|
|
|
return false;
|
2012-08-27 09:49:39 +00:00
|
|
|
}
|
2012-11-08 22:14:50 +00:00
|
|
|
|
2012-12-12 16:50:39 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2012-11-22 15:59:45 +00:00
|
|
|
|
2012-12-04 19:49:42 +00:00
|
|
|
if (width > 20)
|
|
|
|
prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
|
|
|
|
|
2013-05-16 11:00:28 +00:00
|
|
|
/* 5 and 10 MHz are only defined for the OFDM PHY */
|
|
|
|
if (width < 20)
|
|
|
|
prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
|
|
|
|
|
|
|
|
|
2020-04-02 01:18:03 +00:00
|
|
|
if (!cfg80211_secondary_chans_ok(wiphy,
|
|
|
|
ieee80211_chandef_to_khz(chandef),
|
2012-11-22 15:59:45 +00:00
|
|
|
width, prohibited_flags))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!chandef->center_freq2)
|
|
|
|
return true;
|
2020-04-02 01:18:03 +00:00
|
|
|
return cfg80211_secondary_chans_ok(wiphy,
|
|
|
|
MHZ_TO_KHZ(chandef->center_freq2),
|
2012-11-22 15:59:45 +00:00
|
|
|
width, prohibited_flags);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_chandef_usable);
|
|
|
|
|
2022-04-14 14:50:57 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-02-23 07:13:01 +00:00
|
|
|
/*
|
2015-05-06 13:28:31 +00:00
|
|
|
* 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
|
2014-02-23 07:13:01 +00:00
|
|
|
* associated to an AP on the same channel or on the same UNII band
|
|
|
|
* (assuming that the AP is an authorized master).
|
2015-05-06 13:28:31 +00:00
|
|
|
* In addition allow operation on a channel on which indoor operation is
|
2014-02-23 07:13:03 +00:00
|
|
|
* allowed, iff we are currently operating in an indoor environment.
|
2014-02-23 07:13:01 +00:00
|
|
|
*/
|
2015-05-06 13:28:31 +00:00
|
|
|
static bool cfg80211_ir_permissive_chan(struct wiphy *wiphy,
|
|
|
|
enum nl80211_iftype iftype,
|
2014-02-23 07:13:01 +00:00
|
|
|
struct ieee80211_channel *chan)
|
|
|
|
{
|
2015-04-27 13:52:16 +00:00
|
|
|
struct wireless_dev *wdev;
|
2015-05-06 13:28:31 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2014-02-23 07:13:01 +00:00
|
|
|
|
2021-01-22 15:19:43 +00:00
|
|
|
lockdep_assert_held(&rdev->wiphy.mtx);
|
2014-02-23 07:13:01 +00:00
|
|
|
|
2016-08-03 20:45:50 +00:00
|
|
|
if (!IS_ENABLED(CONFIG_CFG80211_REG_RELAX_NO_IR) ||
|
2014-02-23 07:13:03 +00:00
|
|
|
!(wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR))
|
|
|
|
return false;
|
|
|
|
|
2015-05-06 13:28:31 +00:00
|
|
|
/* 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;
|
|
|
|
|
2014-02-23 07:13:03 +00:00
|
|
|
if (regulatory_indoor_allowed() &&
|
|
|
|
(chan->flags & IEEE80211_CHAN_INDOOR_ONLY))
|
|
|
|
return true;
|
|
|
|
|
2015-05-06 13:28:31 +00:00
|
|
|
if (!(chan->flags & IEEE80211_CHAN_IR_CONCURRENT))
|
2014-02-23 07:13:01 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generally, it is possible to rely on another device/driver to allow
|
2015-05-06 13:28:31 +00:00
|
|
|
* the IR concurrent relaxation, however, since the device can further
|
2014-02-23 07:13:01 +00:00
|
|
|
* 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.
|
|
|
|
*/
|
2016-05-03 13:52:04 +00:00
|
|
|
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
|
2022-04-14 14:50:57 +00:00
|
|
|
bool ret;
|
2014-02-23 07:13:01 +00:00
|
|
|
|
2015-04-27 13:52:16 +00:00
|
|
|
wdev_lock(wdev);
|
2022-04-14 14:50:57 +00:00
|
|
|
ret = cfg80211_ir_permissive_check_wdev(iftype, wdev, chan);
|
2015-04-27 13:52:16 +00:00
|
|
|
wdev_unlock(wdev);
|
2014-02-23 07:13:01 +00:00
|
|
|
|
2022-04-14 14:50:57 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2014-02-23 07:13:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-07-08 12:41:44 +00:00
|
|
|
static bool _cfg80211_reg_can_beacon(struct wiphy *wiphy,
|
|
|
|
struct cfg80211_chan_def *chandef,
|
|
|
|
enum nl80211_iftype iftype,
|
|
|
|
bool check_no_ir)
|
2012-11-22 15:59:45 +00:00
|
|
|
{
|
|
|
|
bool res;
|
2013-11-06 12:55:53 +00:00
|
|
|
u32 prohibited_flags = IEEE80211_CHAN_DISABLED |
|
|
|
|
IEEE80211_CHAN_RADAR;
|
2012-11-22 15:59:45 +00:00
|
|
|
|
2015-07-08 12:41:44 +00:00
|
|
|
trace_cfg80211_reg_can_beacon(wiphy, chandef, iftype, check_no_ir);
|
2014-02-23 07:13:01 +00:00
|
|
|
|
2015-07-08 12:41:44 +00:00
|
|
|
if (check_no_ir)
|
2014-02-23 07:13:01 +00:00
|
|
|
prohibited_flags |= IEEE80211_CHAN_NO_IR;
|
2012-11-08 22:14:50 +00:00
|
|
|
|
2014-05-15 10:05:39 +00:00
|
|
|
if (cfg80211_chandef_dfs_required(wiphy, chandef, iftype) > 0 &&
|
2013-11-06 12:55:53 +00:00
|
|
|
cfg80211_chandef_dfs_available(wiphy, chandef)) {
|
|
|
|
/* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */
|
|
|
|
prohibited_flags = IEEE80211_CHAN_DISABLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = cfg80211_chandef_usable(wiphy, chandef, prohibited_flags);
|
2012-11-08 22:14:50 +00:00
|
|
|
|
|
|
|
trace_cfg80211_return_bool(res);
|
|
|
|
return res;
|
2010-11-13 00:31:23 +00:00
|
|
|
}
|
2015-07-08 12:41:44 +00:00
|
|
|
|
|
|
|
bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
|
|
|
|
struct cfg80211_chan_def *chandef,
|
|
|
|
enum nl80211_iftype iftype)
|
|
|
|
{
|
|
|
|
return _cfg80211_reg_can_beacon(wiphy, chandef, iftype, true);
|
|
|
|
}
|
2012-11-08 20:25:48 +00:00
|
|
|
EXPORT_SYMBOL(cfg80211_reg_can_beacon);
|
2010-11-13 00:31:23 +00:00
|
|
|
|
2015-07-08 12:41:44 +00:00
|
|
|
bool cfg80211_reg_can_beacon_relax(struct wiphy *wiphy,
|
|
|
|
struct cfg80211_chan_def *chandef,
|
|
|
|
enum nl80211_iftype iftype)
|
|
|
|
{
|
2021-01-22 15:19:43 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2015-07-08 12:41:44 +00:00
|
|
|
bool check_no_ir;
|
|
|
|
|
2021-01-22 15:19:43 +00:00
|
|
|
lockdep_assert_held(&rdev->wiphy.mtx);
|
2015-07-08 12:41:44 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
check_no_ir = !cfg80211_ir_permissive_chan(wiphy, iftype,
|
|
|
|
chandef->chan);
|
|
|
|
|
|
|
|
return _cfg80211_reg_can_beacon(wiphy, chandef, iftype, check_no_ir);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_reg_can_beacon_relax);
|
|
|
|
|
2012-06-06 06:18:22 +00:00
|
|
|
int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
|
2012-11-08 20:25:48 +00:00
|
|
|
struct cfg80211_chan_def *chandef)
|
2009-12-23 12:15:41 +00:00
|
|
|
{
|
2012-06-06 06:18:22 +00:00
|
|
|
if (!rdev->ops->set_monitor_channel)
|
2009-12-23 12:15:41 +00:00
|
|
|
return -EOPNOTSUPP;
|
2012-06-29 10:47:03 +00:00
|
|
|
if (!cfg80211_has_monitors_only(rdev))
|
|
|
|
return -EBUSY;
|
2009-12-23 12:15:41 +00:00
|
|
|
|
2012-11-08 20:25:48 +00:00
|
|
|
return rdev_set_monitor_channel(rdev, chandef);
|
2009-08-07 15:22:35 +00:00
|
|
|
}
|
2012-06-29 10:47:00 +00:00
|
|
|
|
2021-06-18 10:41:39 +00:00
|
|
|
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);
|
2022-04-14 14:50:57 +00:00
|
|
|
|
|
|
|
struct cfg80211_chan_def *wdev_chandef(struct wireless_dev *wdev,
|
|
|
|
unsigned int link_id)
|
|
|
|
{
|
2022-06-27 10:49:03 +00:00
|
|
|
/*
|
|
|
|
* We need to sort out the locking here - in some cases
|
|
|
|
* where we get here we really just don't care (yet)
|
|
|
|
* about the valid links, but in others we do. But we
|
|
|
|
* get here with various driver cases, so we cannot
|
|
|
|
* easily require the wdev mutex.
|
|
|
|
*/
|
|
|
|
if (link_id || wdev->valid_links & BIT(0)) {
|
|
|
|
ASSERT_WDEV_LOCK(wdev);
|
|
|
|
WARN_ON(!(wdev->valid_links & BIT(link_id)));
|
|
|
|
}
|
2022-04-14 14:50:57 +00:00
|
|
|
|
|
|
|
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);
|
2023-01-31 00:12:24 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
};
|
|
|
|
|
|
|
|
bool cfg80211_valid_disable_subchannel_bitmap(u16 *bitmap,
|
|
|
|
const struct cfg80211_chan_def *chandef)
|
|
|
|
{
|
|
|
|
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:
|
|
|
|
*bitmap = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!*bitmap)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* check if primary channel is punctured */
|
|
|
|
if (*bitmap & (u16)BIT((chandef->chan->center_freq - start_freq) / 20))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (i = 0; i < per_bw_puncturing[idx].len; i++)
|
|
|
|
if (per_bw_puncturing[idx].valid_values[i] == *bitmap)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_valid_disable_subchannel_bitmap);
|