wifi: cfg80211: add a function to parse UHR DBE

Add a function that takes the DBE information and parses it
into an existing chandef that should hold the BSS channel.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260515141209.4eb1490f5cc6.I3ca9421f1fe4c31073846b1b62017f12c75889de@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg
2026-05-15 14:12:12 +03:00
parent 7a8a3ff281
commit c62675c217
3 changed files with 326 additions and 6 deletions

View File

@@ -1190,6 +1190,16 @@ int cfg80211_chandef_add_npca(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef,
const struct ieee80211_uhr_npca_info *npca);
/**
* cfg80211_chandef_add_dbe - parse and add DBE information to chandef
* @chandef: the chandef to expand
* @dbe: the DBE information, must be size-checked if not %NULL
*
* Returns: 0 for success, a negative error code otherwise
*/
int cfg80211_chandef_add_dbe(struct cfg80211_chan_def *chandef,
const struct ieee80211_uhr_dbe_info *dbe);
/**
* nl80211_send_chandef - sends the channel definition.
* @msg: the msg to send channel definition

View File

@@ -619,6 +619,136 @@ int cfg80211_chandef_add_npca(struct wiphy *wiphy,
}
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,

View File

@@ -2,13 +2,18 @@
/*
* KUnit tests for channel helper functions
*
* Copyright (C) 2023-2024 Intel Corporation
* Copyright (C) 2023-2024, 2026 Intel Corporation
*/
#include <net/cfg80211.h>
#include <kunit/test.h>
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
static struct ieee80211_channel chan_2ghz_1 = {
.band = NL80211_BAND_2GHZ,
.center_freq = 2412,
};
static struct ieee80211_channel chan_6ghz_1 = {
.band = NL80211_BAND_6GHZ,
.center_freq = 5955,
@@ -215,14 +220,189 @@ static void test_chandef_compat(struct kunit *test)
KUNIT_EXPECT_PTR_EQ(test, ret, expect);
}
static struct kunit_case chandef_compat_test_cases[] = {
static const struct chandef_dbe_case {
const char *desc;
struct cfg80211_chan_def c;
u8 dbe[3];
bool fails;
u16 cf1;
} chandef_dbe_cases[] = {
{
.desc = "non-HT failure",
.c = {
.width = NL80211_CHAN_WIDTH_20_NOHT,
.chan = &chan_6ghz_1,
.center_freq1 = 5955,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_40,
.fails = true,
},
{
.desc = "2.4 GHz fails",
.c = {
.width = NL80211_CHAN_WIDTH_20,
.chan = &chan_2ghz_1,
.center_freq1 = 2412,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_40,
.fails = true,
},
{
.desc = "DBE narrower",
.c = {
.width = NL80211_CHAN_WIDTH_320,
.chan = &chan_6ghz_1,
.center_freq1 = 5955 + 10 + 20 + 40 + 80,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_160,
.fails = true,
},
{
.desc = "DBE to 320-1",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1,
.cf1 = 6425,
},
{
.desc = "DBE to 320-2",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_2,
.cf1 = 6585,
},
{
.desc = "bad disabled subchannel bitmap - not enough in BSS (1)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0001,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0 */
.fails = true,
},
{
.desc = "bad disabled subchannel bitmap - too much in BSS (1)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0001,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0x0300 */
.dbe[2] = 0x03,
.fails = true,
},
{
.desc = "bad disabled subchannel bitmap - not enough in BSS (2)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0001,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_2 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0 */
.fails = true,
},
{
.desc = "bad disabled subchannel bitmap - too much in BSS (2)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0001,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_2 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0x03 */
.dbe[1] = 0x03,
.fails = true,
},
{
.desc = "bad disabled subchannel bitmap - bad bitmap",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0001,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0x1100 */
.dbe[2] = 0x11,
.fails = true,
},
{
.desc = "good disabled subchannel bitmap (1)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0003,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0x0300 */
.dbe[2] = 0x03,
.cf1 = 6425,
},
{
.desc = "good disabled subchannel bitmap (2)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0003,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_2 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0x0003 */
.dbe[1] = 0x03,
.cf1 = 6585,
},
};
KUNIT_ARRAY_PARAM_DESC(chandef_dbe, chandef_dbe_cases, desc)
static void test_chandef_dbe(struct kunit *test)
{
const struct chandef_dbe_case *params = test->param_value;
struct cfg80211_chan_def c = params->c;
int ret;
KUNIT_EXPECT_EQ(test, cfg80211_chandef_valid(&params->c), true);
ret = cfg80211_chandef_add_dbe(&c, (void *)params->dbe);
KUNIT_EXPECT_EQ(test, ret != 0, params->fails);
if (params->fails)
return;
KUNIT_EXPECT_EQ(test, c.center_freq1, params->cf1);
}
static struct kunit_case chandef_test_cases[] = {
KUNIT_CASE_PARAM(test_chandef_compat, chandef_compat_gen_params),
KUNIT_CASE_PARAM(test_chandef_dbe, chandef_dbe_gen_params),
{}
};
static struct kunit_suite chandef_compat = {
.name = "cfg80211-chandef-compat",
.test_cases = chandef_compat_test_cases,
static struct kunit_suite chandef = {
.name = "cfg80211-chandef",
.test_cases = chandef_test_cases,
};
kunit_test_suite(chandef_compat);
kunit_test_suite(chandef);