}
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,
/*
* 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,
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(¶ms->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);