diff mbox series

[v2,01/12] cfg80211: regulatory: extend regulatory support for S1G

Message ID 20220830022017.51017-2-kieran.frewen@morsemicro.com (mailing list archive)
State Superseded
Delegated to: Johannes Berg
Headers show
Series Additional Support for 802.11ah (S1G) | expand

Commit Message

Kieran Frewen Aug. 30, 2022, 2:20 a.m. UTC
Extend the S1G regulatory information to support all regulatory
domains. An reg_s1g.h file is included containing structs with key
regulatory class information. These structs were required to ensure
the right combination of information was available to a series of
functions which support the mapping between frequencies, bandwidths,
and channels.

Signed-off-by: Kieran Frewen <kieran.frewen@morsemicro.com>
---
 net/wireless/Makefile  |   2 +-
 net/wireless/reg.c     |  60 ++++++++---
 net/wireless/reg_s1g.c | 232 +++++++++++++++++++++++++++++++++++++++++
 net/wireless/reg_s1g.h |  55 ++++++++++
 net/wireless/util.c    |  35 ++++++-
 5 files changed, 370 insertions(+), 14 deletions(-)
 create mode 100755 net/wireless/reg_s1g.c
 create mode 100644 net/wireless/reg_s1g.h

Comments

kernel test robot Aug. 30, 2022, 4:52 a.m. UTC | #1
Hi Kieran,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on wireless/main]
[also build test WARNING on linus/master v6.0-rc3 next-20220829]
[cannot apply to wireless-next/main]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Kieran-Frewen/Additional-Support-for-802-11ah-S1G/20220830-102217
base:   https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless.git main
config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20220830/202208301259.DiwKm3rQ-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/7c0e7a45e22911c6e1b16ecdce1a4d6022fd66ee
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Kieran-Frewen/Additional-Support-for-802-11ah-S1G/20220830-102217
        git checkout 7c0e7a45e22911c6e1b16ecdce1a4d6022fd66ee
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k SHELL=/bin/bash net/wireless/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> net/wireless/util.c:137:5: warning: no previous prototype for 'ieee80211_s1g_channel_to_freq_khz' [-Wmissing-prototypes]
     137 | u32 ieee80211_s1g_channel_to_freq_khz(int chan)
         |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--
   net/wireless/reg.c: In function 'handle_channel_custom.constprop':
>> net/wireless/reg.c:2597:28: warning: 'center_freq_khz' is used uninitialized [-Wuninitialized]
    2597 |                 reg_rule = freq_reg_info_regd(center_freq_khz, regd, bw);
         |                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   net/wireless/reg.c:2586:17: note: 'center_freq_khz' was declared here
    2586 |         u32 bw, center_freq_khz;
         |                 ^~~~~~~~~~~~~~~


vim +/ieee80211_s1g_channel_to_freq_khz +137 net/wireless/util.c

   136	
 > 137	u32 ieee80211_s1g_channel_to_freq_khz(int chan)
   138	{
   139		u32 base = ieee80211_s1g_base_freq(chan);
   140	
   141		if (!base)
   142			return 0;
   143	
   144		return (base + chan * 500);
   145	}
   146	EXPORT_SYMBOL(ieee80211_s1g_channel_to_freq_khz);
   147
kernel test robot Aug. 30, 2022, 9:31 a.m. UTC | #2
Hi Kieran,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on wireless/main]
[also build test WARNING on linus/master v6.0-rc3 next-20220830]
[cannot apply to wireless-next/main]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Kieran-Frewen/Additional-Support-for-802-11ah-S1G/20220830-102217
base:   https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless.git main
config: arm-colibri_pxa270_defconfig (https://download.01.org/0day-ci/archive/20220830/202208301755.3ARl5XbS-lkp@intel.com/config)
compiler: clang version 16.0.0 (https://github.com/llvm/llvm-project c7df82e4693c19e3fd2e25c83eb04d9deb7b7b59)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install arm cross compiling tool for clang build
        # apt-get install binutils-arm-linux-gnueabi
        # https://github.com/intel-lab-lkp/linux/commit/7c0e7a45e22911c6e1b16ecdce1a4d6022fd66ee
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Kieran-Frewen/Additional-Support-for-802-11ah-S1G/20220830-102217
        git checkout 7c0e7a45e22911c6e1b16ecdce1a4d6022fd66ee
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=arm SHELL=/bin/bash net/wireless/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> net/wireless/util.c:137:5: warning: no previous prototype for function 'ieee80211_s1g_channel_to_freq_khz' [-Wmissing-prototypes]
   u32 ieee80211_s1g_channel_to_freq_khz(int chan)
       ^
   net/wireless/util.c:137:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   u32 ieee80211_s1g_channel_to_freq_khz(int chan)
   ^
   static 
   1 warning generated.
--
>> net/wireless/reg.c:2597:33: warning: variable 'center_freq_khz' is uninitialized when used here [-Wuninitialized]
                   reg_rule = freq_reg_info_regd(center_freq_khz, regd, bw);
                                                 ^~~~~~~~~~~~~~~
   net/wireless/reg.c:2586:25: note: initialize the variable 'center_freq_khz' to silence this warning
           u32 bw, center_freq_khz;
                                  ^
                                   = 0
   1 warning generated.


vim +/ieee80211_s1g_channel_to_freq_khz +137 net/wireless/util.c

   136	
 > 137	u32 ieee80211_s1g_channel_to_freq_khz(int chan)
   138	{
   139		u32 base = ieee80211_s1g_base_freq(chan);
   140	
   141		if (!base)
   142			return 0;
   143	
   144		return (base + chan * 500);
   145	}
   146	EXPORT_SYMBOL(ieee80211_s1g_channel_to_freq_khz);
   147
Dan Carpenter Sept. 2, 2022, 8:44 a.m. UTC | #3
Hi Kieran,

https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Kieran-Frewen/Additional-Support-for-802-11ah-S1G/20220830-102217
base:   https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless.git main
config: csky-randconfig-m031-20220901 (https://download.01.org/0day-ci/archive/20220902/202209020018.S33cD0dC-lkp@intel.com/config)
compiler: csky-linux-gcc (GCC) 12.1.0

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

smatch warnings:
net/wireless/reg.c:2597 handle_channel_custom() error: uninitialized symbol 'center_freq_khz'.

vim +/center_freq_khz +2597 net/wireless/reg.c

1fa25e413659f9 Luis R. Rodriguez 2009-01-22  2578  static void handle_channel_custom(struct wiphy *wiphy,
fdc9d7b2863ce6 Johannes Berg     2012-12-03  2579  				  struct ieee80211_channel *chan,
c4b9d655e445a8 Ganapathi Bhat    2019-12-20  2580  				  const struct ieee80211_regdomain *regd,
c4b9d655e445a8 Ganapathi Bhat    2019-12-20  2581  				  u32 min_bw)
1fa25e413659f9 Luis R. Rodriguez 2009-01-22  2582  {
038659e7c6b385 Luis R. Rodriguez 2009-05-02  2583  	u32 bw_flags = 0;
1fa25e413659f9 Luis R. Rodriguez 2009-01-22  2584  	const struct ieee80211_reg_rule *reg_rule = NULL;
1fa25e413659f9 Luis R. Rodriguez 2009-01-22  2585  	const struct ieee80211_power_rule *power_rule = NULL;
934f4c7dd3a544 Thomas Pedersen   2020-04-01  2586  	u32 bw, center_freq_khz;
7c0e7a45e22911 Kieran Frewen     2022-08-30  2587  	bool is_s1g = chan->band == NL80211_BAND_S1GHZ;
1fa25e413659f9 Luis R. Rodriguez 2009-01-22  2588  
7c0e7a45e22911 Kieran Frewen     2022-08-30  2589  	if (is_s1g) {
7c0e7a45e22911 Kieran Frewen     2022-08-30  2590  		bw = MHZ_TO_KHZ(16);
7c0e7a45e22911 Kieran Frewen     2022-08-30  2591  		min_bw = MHZ_TO_KHZ(1);
7c0e7a45e22911 Kieran Frewen     2022-08-30  2592  	} else {
7c0e7a45e22911 Kieran Frewen     2022-08-30  2593  		bw = MHZ_TO_KHZ(20);
7c0e7a45e22911 Kieran Frewen     2022-08-30  2594  	}
7c0e7a45e22911 Kieran Frewen     2022-08-30  2595  
7c0e7a45e22911 Kieran Frewen     2022-08-30  2596  	for (; bw >= min_bw; bw = bw / 2) {
934f4c7dd3a544 Thomas Pedersen   2020-04-01 @2597  		reg_rule = freq_reg_info_regd(center_freq_khz, regd, bw);

Never initialized.

4edd56981c8fbb Matthias May      2015-07-17  2598  		if (!IS_ERR(reg_rule))
4edd56981c8fbb Matthias May      2015-07-17  2599  			break;
4edd56981c8fbb Matthias May      2015-07-17  2600  	}
1fa25e413659f9 Luis R. Rodriguez 2009-01-22  2601  
a7ee7d44b57c9a Johannes Berg     2020-02-21  2602  	if (IS_ERR_OR_NULL(reg_rule)) {
934f4c7dd3a544 Thomas Pedersen   2020-04-01  2603  		pr_debug("Disabling freq %d.%03d MHz as custom regd has no rule that fits it\n",
934f4c7dd3a544 Thomas Pedersen   2020-04-01  2604  			 chan->center_freq, chan->freq_offset);
db8dfee57d37d2 Arik Nemtsov      2014-12-15  2605  		if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) {
db8dfee57d37d2 Arik Nemtsov      2014-12-15  2606  			chan->flags |= IEEE80211_CHAN_DISABLED;
db8dfee57d37d2 Arik Nemtsov      2014-12-15  2607  		} else {
cc493e4f5296f4 Luis R. Rodriguez 2013-11-06  2608  			chan->orig_flags |= IEEE80211_CHAN_DISABLED;
cc493e4f5296f4 Luis R. Rodriguez 2013-11-06  2609  			chan->flags = chan->orig_flags;
db8dfee57d37d2 Arik Nemtsov      2014-12-15  2610  		}
1fa25e413659f9 Luis R. Rodriguez 2009-01-22  2611  		return;
1fa25e413659f9 Luis R. Rodriguez 2009-01-22  2612  	}
1fa25e413659f9 Luis R. Rodriguez 2009-01-22  2613  
1fa25e413659f9 Luis R. Rodriguez 2009-01-22  2614  	power_rule = &reg_rule->power_rule;
1aeb135f84fe40 Michal Sojka      2015-11-23  2615  	bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan);
1fa25e413659f9 Luis R. Rodriguez 2009-01-22  2616  
2e18b38fc8fb03 Arik Nemtsov      2014-11-16  2617  	chan->dfs_state_entered = jiffies;
c7ab508190aee6 Arik Nemtsov      2014-11-16  2618  	chan->dfs_state = NL80211_DFS_USABLE;
c7ab508190aee6 Arik Nemtsov      2014-11-16  2619  
c7ab508190aee6 Arik Nemtsov      2014-11-16  2620  	chan->beacon_found = false;
db8dfee57d37d2 Arik Nemtsov      2014-12-15  2621  
db8dfee57d37d2 Arik Nemtsov      2014-12-15  2622  	if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
db8dfee57d37d2 Arik Nemtsov      2014-12-15  2623  		chan->flags = chan->orig_flags | bw_flags |
db8dfee57d37d2 Arik Nemtsov      2014-12-15  2624  			      map_regdom_flags(reg_rule->flags);
db8dfee57d37d2 Arik Nemtsov      2014-12-15  2625  	else
038659e7c6b385 Luis R. Rodriguez 2009-05-02  2626  		chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
db8dfee57d37d2 Arik Nemtsov      2014-12-15  2627  
1fa25e413659f9 Luis R. Rodriguez 2009-01-22  2628  	chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
279f0f55249820 Felix Fietkau     2012-10-17  2629  	chan->max_reg_power = chan->max_power =
279f0f55249820 Felix Fietkau     2012-10-17  2630  		(int) MBM_TO_DBM(power_rule->max_eirp);
2e18b38fc8fb03 Arik Nemtsov      2014-11-16  2631  
2e18b38fc8fb03 Arik Nemtsov      2014-11-16  2632  	if (chan->flags & IEEE80211_CHAN_RADAR) {
2e18b38fc8fb03 Arik Nemtsov      2014-11-16  2633  		if (reg_rule->dfs_cac_ms)
2e18b38fc8fb03 Arik Nemtsov      2014-11-16  2634  			chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
2e18b38fc8fb03 Arik Nemtsov      2014-11-16  2635  		else
2e18b38fc8fb03 Arik Nemtsov      2014-11-16  2636  			chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
2e18b38fc8fb03 Arik Nemtsov      2014-11-16  2637  	}
2e18b38fc8fb03 Arik Nemtsov      2014-11-16  2638  
2e18b38fc8fb03 Arik Nemtsov      2014-11-16  2639  	chan->max_power = chan->max_reg_power;
1fa25e413659f9 Luis R. Rodriguez 2009-01-22  2640  }
diff mbox series

Patch

diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index 527ae669f6f7..10cc0db5a935 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -10,7 +10,7 @@  obj-$(CONFIG_WEXT_PROC) += wext-proc.o
 obj-$(CONFIG_WEXT_SPY) += wext-spy.o
 obj-$(CONFIG_WEXT_PRIV) += wext-priv.o
 
-cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
+cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o reg_s1g.o scan.o nl80211.o
 cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o
 cfg80211-y += pmsr.o
 cfg80211-$(CONFIG_OF) += of.o
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index c7383ede794f..de68e356927d 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -62,6 +62,7 @@ 
 #include "reg.h"
 #include "rdev-ops.h"
 #include "nl80211.h"
+#include "reg_s1g.h"
 
 /*
  * Grace period we give before making sure all current interfaces reside on
@@ -1737,26 +1738,54 @@  static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd
 		 * the largest bandwidth which cleanly divides the freq_range.
 		 */
 		int edge_offset;
-		int ch_bw = max_bandwidth_khz;
+		int ch_bw, freq_end, freq_start, class_idx = -1;
+		unsigned int i;
+		const struct s1g_oper_class *oper = reg_s1g_get_oper_class(regd->alpha2);
+
+		if (!oper) {
+			bw_flags |= IEEE80211_CHAN_DISABLED;
+			return bw_flags;
+		}
+
+		for (i = 0; i < oper->class_count; i++) {
+			if (center_freq_khz >= oper->class[i].start_freq &&
+			    center_freq_khz <= oper->class[i].end_freq) {
+				class_idx = i;
+				break;
+			}
+		}
+
+		if (class_idx == -1) {
+			bw_flags |= IEEE80211_CHAN_DISABLED;
+			return bw_flags;
+		}
+
+		ch_bw = oper->class[class_idx].max_bw_khz;
+		freq_start = oper->class[class_idx].start_freq;
+		freq_end = oper->class[class_idx].end_freq;
 
 		while (ch_bw) {
-			edge_offset = (center_freq_khz - ch_bw / 2) -
-				      freq_range->start_freq_khz;
-			if (edge_offset % ch_bw == 0) {
-				switch (KHZ_TO_MHZ(ch_bw)) {
-				case 1:
+			if (oper->class[class_idx].align_to_end)
+				edge_offset = freq_end -
+					(center_freq_khz - (ch_bw) / 2);
+			else
+				edge_offset = (center_freq_khz - (ch_bw) / 2) -
+					freq_start;
+			if (edge_offset % (ch_bw) == 0) {
+				switch (ch_bw) {
+				case MHZ_TO_KHZ(1):
 					bw_flags |= IEEE80211_CHAN_1MHZ;
 					break;
-				case 2:
+				case MHZ_TO_KHZ(2):
 					bw_flags |= IEEE80211_CHAN_2MHZ;
 					break;
-				case 4:
+				case MHZ_TO_KHZ(4):
 					bw_flags |= IEEE80211_CHAN_4MHZ;
 					break;
-				case 8:
+				case MHZ_TO_KHZ(8):
 					bw_flags |= IEEE80211_CHAN_8MHZ;
 					break;
-				case 16:
+				case MHZ_TO_KHZ(16):
 					bw_flags |= IEEE80211_CHAN_16MHZ;
 					break;
 				default:
@@ -2555,9 +2584,16 @@  static void handle_channel_custom(struct wiphy *wiphy,
 	const struct ieee80211_reg_rule *reg_rule = NULL;
 	const struct ieee80211_power_rule *power_rule = NULL;
 	u32 bw, center_freq_khz;
+	bool is_s1g = chan->band == NL80211_BAND_S1GHZ;
 
-	center_freq_khz = ieee80211_channel_to_khz(chan);
-	for (bw = MHZ_TO_KHZ(20); bw >= min_bw; bw = bw / 2) {
+	if (is_s1g) {
+		bw = MHZ_TO_KHZ(16);
+		min_bw = MHZ_TO_KHZ(1);
+	} else {
+		bw = MHZ_TO_KHZ(20);
+	}
+
+	for (; bw >= min_bw; bw = bw / 2) {
 		reg_rule = freq_reg_info_regd(center_freq_khz, regd, bw);
 		if (!IS_ERR(reg_rule))
 			break;
diff --git a/net/wireless/reg_s1g.c b/net/wireless/reg_s1g.c
new file mode 100755
index 000000000000..8529c65dd6c8
--- /dev/null
+++ b/net/wireless/reg_s1g.c
@@ -0,0 +1,232 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+#include "reg_s1g.h"
+
+/* The following channel lists have been retrieved from
+ * IEEE Std 802.11-2020 Table E-5
+ */
+static const u8 us_supported_channels[] = {
+	1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+	14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+	24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+	34, 35, 37, 38, 39, 40, 41, 42, 43, 44,
+	45, 46, 47, 48, 49, 50, 51
+};
+
+static const u8 eu_supported_channels_863[] = {
+	1, 3, 5, 7, 9
+};
+
+static const u8 eu_supported_channels_901_4[] = {
+	33, 35
+};
+
+static const u8 jp_supported_channels[] = {
+	1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21
+};
+
+static const u8 kr_supported_channels[] = {
+	1, 2, 3, 5, 6, 7, 8, 9, 10, 11
+};
+
+static const u8 sg_supported_channels_863[] = {
+	7, 9, 10, 11
+};
+
+static const u8 sg_supported_channels_902[] = {
+	37, 38, 39, 40, 41, 42, 43, 45
+};
+
+static const u8 au_nz_supported_channels[] = {
+	27, 29, 30, 31, 32, 33, 34, 35, 37, 38, 39,
+	40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
+	51
+};
+
+
+/* The following s1g_oper_class structs are taken from
+ * IEEE Std 802.11-2020 Table E-5
+ */
+static const struct s1g_oper_class country_class_au = {
+	.cc = "AU",
+	.class_count = 2,
+	.class = {
+	{
+		.band_start = 902000,
+		.start_freq = 915000,
+		.end_freq = 920000,
+		.max_bw_khz = MHZ_TO_KHZ(4),
+		.align_to_end = false,
+		.supported_chan = au_nz_supported_channels,
+		.n_supported_chan = sizeof(au_nz_supported_channels),
+	},
+	{
+		.band_start = 902000,
+		.start_freq = 920000,
+		.end_freq = 928000,
+		.max_bw_khz = MHZ_TO_KHZ(8),
+		.align_to_end = true,
+		.supported_chan = NULL,
+		.n_supported_chan = 0,
+	}
+	},
+};
+
+static const struct s1g_oper_class country_class_nz = {
+	.cc = "NZ",
+	.class_count = 2,
+	.class = {
+	{
+		.band_start = 902000,
+		.start_freq = 915000,
+		.end_freq = 924000,
+		.max_bw_khz = MHZ_TO_KHZ(8),
+		.align_to_end = false,
+		.supported_chan = au_nz_supported_channels,
+		.n_supported_chan = sizeof(au_nz_supported_channels),
+	},
+	{
+		.band_start = 902000,
+		.start_freq = 924000,
+		.end_freq = 928000,
+		.max_bw_khz = MHZ_TO_KHZ(8),
+		.align_to_end = false,
+		.supported_chan = NULL,
+		.n_supported_chan = 0,
+	}
+	},
+};
+
+static const struct s1g_oper_class country_class_us = {
+	.cc = "US",
+	.class_count = 3,
+	.class = {
+	{
+		.band_start = 902000,
+		.start_freq = 902000,
+		.end_freq = 904000,
+		.max_bw_khz = MHZ_TO_KHZ(16),
+		.align_to_end = false,
+		.supported_chan = us_supported_channels,
+		.n_supported_chan = sizeof(us_supported_channels),
+	},
+	{
+		.band_start = 902000,
+		.start_freq = 920000,
+		.end_freq = 928000,
+		.max_bw_khz = MHZ_TO_KHZ(16),
+		.align_to_end = false,
+		.supported_chan = NULL,
+		.n_supported_chan = 0,
+	},
+	{
+		.band_start = 902000,
+		.start_freq = 904000,
+		.end_freq = 920000,
+		.max_bw_khz = MHZ_TO_KHZ(16),
+		.align_to_end = false,
+		.supported_chan = NULL,
+		.n_supported_chan = 0,
+	}
+	},
+};
+
+static const struct s1g_oper_class country_class_sg = {
+	.cc = "SG",
+	.class_count = 2,
+	.class = {
+	{
+		.band_start = 863000,
+		.start_freq = 866000,
+		.end_freq = 869000,
+		.max_bw_khz = MHZ_TO_KHZ(2),
+		.align_to_end = true,
+		.supported_chan = sg_supported_channels_863,
+		.n_supported_chan = sizeof(sg_supported_channels_863),
+	},
+	{
+		.band_start = 902000,
+		.start_freq = 920000,
+		.end_freq = 925000,
+		.max_bw_khz = MHZ_TO_KHZ(4),
+		.align_to_end = false,
+		.supported_chan = sg_supported_channels_902,
+		.n_supported_chan = sizeof(sg_supported_channels_902),
+	},
+	},
+};
+
+static const struct s1g_oper_class country_class_kr = {
+	.cc = "KR",
+	.class_count = 1,
+	.class = {
+	{
+		.band_start = 917500,
+		.start_freq = 917500,
+		.end_freq = 923500,
+		.max_bw_khz = MHZ_TO_KHZ(4),
+		.align_to_end = true,
+		.supported_chan = kr_supported_channels,
+		.n_supported_chan = sizeof(kr_supported_channels),
+	}
+	},
+};
+
+static const struct s1g_oper_class country_class_eu = {
+	.cc = "EU",
+	.class_count = 1,
+	.class = {
+	{
+		.band_start = 863000,
+		.start_freq = 863000,
+		.end_freq = 868000,
+		.max_bw_khz = MHZ_TO_KHZ(1),
+		.align_to_end = false,
+		.supported_chan = eu_supported_channels_863,
+		.n_supported_chan = sizeof(eu_supported_channels_863),
+	},
+	{
+		.band_start = 901400,
+		.start_freq = 917400,
+		.end_freq = 919400,
+		.max_bw_khz = MHZ_TO_KHZ(1),
+		.align_to_end = false,
+		.supported_chan = eu_supported_channels_901_4,
+		.n_supported_chan = sizeof(eu_supported_channels_901_4),
+	}
+	},
+};
+
+static const struct s1g_oper_class country_class_jp = {
+	.cc = "JP",
+	.class_count = 1,
+	.class = {
+	{
+		.band_start = 916500,
+		.start_freq = 916500,
+		.end_freq = 927500,
+		.max_bw_khz = MHZ_TO_KHZ(1),
+		.align_to_end = false,
+		.supported_chan = jp_supported_channels,
+		.n_supported_chan = sizeof(jp_supported_channels),
+	}
+	},
+};
+
+const struct s1g_oper_class *reg_s1g_get_oper_class(const char *cc)
+{
+	if (!strcmp(cc, "EU"))
+		return &country_class_eu;
+	if (!strcmp(cc, "SG"))
+		return &country_class_sg;
+	if (!strcmp(cc, "US"))
+		return &country_class_us;
+	if (!strcmp(cc, "AU"))
+		return &country_class_au;
+	if (!strcmp(cc, "KR"))
+		return &country_class_kr;
+	if (!strcmp(cc, "JP"))
+		return &country_class_jp;
+	if (!strcmp(cc, "NZ"))
+		return &country_class_nz;
+	return NULL;
+}
diff --git a/net/wireless/reg_s1g.h b/net/wireless/reg_s1g.h
new file mode 100644
index 000000000000..3d7b1f810ed8
--- /dev/null
+++ b/net/wireless/reg_s1g.h
@@ -0,0 +1,55 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __NET_WIRELESS_REG_S1G_H
+#define __NET_WIRELESS_REG_S1G_H
+
+#include "reg.h"
+
+/**
+ * Struct cca_class
+ *
+ * The specifics of a cca level classification used to validate bw/frequency
+ *	combinations in a regulatory domain
+ * @band_start: The channel starting frequency (kHz) for that CCA classification
+ * @start_freq: The start of the valid frequency (kHz) range for the CCA
+ *	classification
+ * @end_freq: The end of the valid frequency (kHz) range for the CCA
+ *	classification
+ * @max_bw_khz: The maximum valid bandwidth for the CCA classification
+ * @align_to_end: True if the maximum valid bandwidth for the range is aligned
+ *	to the end_freq
+ * @supported_chan: A list of supported channel indexes
+ * @n_supported_chan: A count of the supported channels for this CCA
+ *	classification
+ */
+struct s1g_cca_classification {
+	u32 band_start;
+	u32 start_freq;
+	u32 end_freq;
+	u32 max_bw_khz;
+	bool align_to_end;
+	const u8 *supported_chan;
+	u8 n_supported_chan;
+};
+
+
+/**
+
+ * Struct s1g_oper_class
+ *
+ * An aggregated view of the operating classes for a single regulatory
+ * domain
+ * @cc: country code (2-character code)
+ * @class_count: The number of CCA level classifications that exist
+ *	within that country
+ * @class: The specifics of a CCA level classification within a regulatory
+ *	domain.
+ */
+struct s1g_oper_class {
+	char cc[3];
+	int class_count;
+	struct s1g_cca_classification class[];
+};
+
+const struct s1g_oper_class *reg_s1g_get_oper_class(const char *cc);
+
+#endif /*__NET_WIRELESS_REG_S1G_H */
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 2c127951764a..aeccb6ce595c 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -22,6 +22,7 @@ 
 #include <linux/nospec.h>
 #include "core.h"
 #include "rdev-ops.h"
+#include "reg_s1g.h"
 
 
 const struct ieee80211_rate *
@@ -72,6 +73,27 @@  u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
 }
 EXPORT_SYMBOL(ieee80211_mandatory_rates);
 
+static u32 ieee80211_s1g_base_freq(int chan)
+{
+	const struct ieee80211_regdomain *regd  = rtnl_dereference(cfg80211_regdomain);
+	const struct s1g_oper_class *oper = reg_s1g_get_oper_class(regd->alpha2);
+	u8 i, j, index = 0;
+
+	if (!oper)
+		return 0;
+
+	if (oper->class_count > 1)
+		for (i = 0; i < oper->class_count; i++)
+			for (j = 0; j < oper->class[i].n_supported_chan; j++)
+				if (oper->class[i].supported_chan[j] == chan) {
+					index = i;
+					goto out;
+				}
+	return 0; /* not supported */
+out:
+	return oper->class[index].band_start;
+}
+
 u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band)
 {
 	/* see 802.11 17.3.8.3.2 and Annex J
@@ -104,7 +126,7 @@  u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band)
 			return MHZ_TO_KHZ(56160 + chan * 2160);
 		break;
 	case NL80211_BAND_S1GHZ:
-		return 902000 + chan * 500;
+		return ieee80211_s1g_base_freq(chan) + chan * 500;
 	default:
 		;
 	}
@@ -112,6 +134,17 @@  u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band)
 }
 EXPORT_SYMBOL(ieee80211_channel_to_freq_khz);
 
+u32 ieee80211_s1g_channel_to_freq_khz(int chan)
+{
+	u32 base = ieee80211_s1g_base_freq(chan);
+
+	if (!base)
+		return 0;
+
+	return (base + chan * 500);
+}
+EXPORT_SYMBOL(ieee80211_s1g_channel_to_freq_khz);
+
 enum nl80211_chan_width
 ieee80211_s1g_channel_width(const struct ieee80211_channel *chan)
 {