summaryrefslogtreecommitdiff
path: root/net/mac80211/tests
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/tests')
-rw-r--r--net/mac80211/tests/Makefile2
-rw-r--r--net/mac80211/tests/chan-mode.c30
-rw-r--r--net/mac80211/tests/s1g_tim.c356
3 files changed, 382 insertions, 6 deletions
diff --git a/net/mac80211/tests/Makefile b/net/mac80211/tests/Makefile
index 3b0c08356fc5..3c7f874e5c41 100644
--- a/net/mac80211/tests/Makefile
+++ b/net/mac80211/tests/Makefile
@@ -1,3 +1,3 @@
-mac80211-tests-y += module.o util.o elems.o mfp.o tpe.o chan-mode.o
+mac80211-tests-y += module.o util.o elems.o mfp.o tpe.o chan-mode.o s1g_tim.o
obj-$(CONFIG_MAC80211_KUNIT_TEST) += mac80211-tests.o
diff --git a/net/mac80211/tests/chan-mode.c b/net/mac80211/tests/chan-mode.c
index 96c7b3ab2744..adc069065e73 100644
--- a/net/mac80211/tests/chan-mode.c
+++ b/net/mac80211/tests/chan-mode.c
@@ -2,7 +2,7 @@
/*
* KUnit tests for channel mode functions
*
- * Copyright (C) 2024 Intel Corporation
+ * Copyright (C) 2024-2025 Intel Corporation
*/
#include <net/cfg80211.h>
#include <kunit/test.h>
@@ -28,6 +28,10 @@ static const struct determine_chan_mode_case {
u8 vht_basic_mcs_1_4, vht_basic_mcs_5_8;
u8 he_basic_mcs_1_4, he_basic_mcs_5_8;
u8 eht_mcs7_min_nss;
+ u16 eht_disabled_subchannels;
+ u8 eht_bw;
+ enum ieee80211_conn_bw_limit conn_bw_limit;
+ enum ieee80211_conn_bw_limit expected_bw_limit;
int error;
} determine_chan_mode_cases[] = {
{
@@ -128,6 +132,14 @@ static const struct determine_chan_mode_case {
.conn_mode = IEEE80211_CONN_MODE_EHT,
.eht_mcs7_min_nss = 0x15,
.error = EINVAL,
+ }, {
+ .desc = "80 MHz EHT is downgraded to 40 MHz HE due to puncturing",
+ .conn_mode = IEEE80211_CONN_MODE_EHT,
+ .expected_mode = IEEE80211_CONN_MODE_HE,
+ .conn_bw_limit = IEEE80211_CONN_BW_LIMIT_80,
+ .expected_bw_limit = IEEE80211_CONN_BW_LIMIT_40,
+ .eht_disabled_subchannels = 0x08,
+ .eht_bw = IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ,
}
};
KUNIT_ARRAY_PARAM_DESC(determine_chan_mode, determine_chan_mode_cases, desc)
@@ -138,7 +150,7 @@ static void test_determine_chan_mode(struct kunit *test)
struct t_sdata *t_sdata = T_SDATA(test);
struct ieee80211_conn_settings conn = {
.mode = params->conn_mode,
- .bw_limit = IEEE80211_CONN_BW_LIMIT_20,
+ .bw_limit = params->conn_bw_limit,
};
struct cfg80211_bss cbss = {
.channel = &t_sdata->band_5ghz.channels[0],
@@ -191,14 +203,21 @@ static void test_determine_chan_mode(struct kunit *test)
0x7f, 0x01, 0x00, 0x88, 0x88, 0x88, 0x00, 0x00,
0x00,
/* EHT Operation */
- WLAN_EID_EXTENSION, 0x09, WLAN_EID_EXT_EHT_OPERATION,
- 0x01, params->eht_mcs7_min_nss ? params->eht_mcs7_min_nss : 0x11,
- 0x00, 0x00, 0x00, 0x00, 0x24, 0x00,
+ WLAN_EID_EXTENSION, 0x0b, WLAN_EID_EXT_EHT_OPERATION,
+ 0x03, params->eht_mcs7_min_nss ? params->eht_mcs7_min_nss : 0x11,
+ 0x00, 0x00, 0x00, params->eht_bw,
+ params->eht_bw == IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ ? 42 : 36,
+ 0x00,
+ u16_get_bits(params->eht_disabled_subchannels, 0xff),
+ u16_get_bits(params->eht_disabled_subchannels, 0xff00),
};
struct ieee80211_chan_req chanreq = {};
struct cfg80211_chan_def ap_chandef = {};
struct ieee802_11_elems *elems;
+ /* To force EHT downgrade to HE on punctured 80 MHz downgraded to 40 MHz */
+ set_bit(IEEE80211_HW_DISALLOW_PUNCTURING, t_sdata->local.hw.flags);
+
if (params->strict)
set_bit(IEEE80211_HW_STRICT, t_sdata->local.hw.flags);
else
@@ -237,6 +256,7 @@ static void test_determine_chan_mode(struct kunit *test)
} else {
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, elems);
KUNIT_ASSERT_EQ(test, conn.mode, params->expected_mode);
+ KUNIT_ASSERT_EQ(test, conn.bw_limit, params->expected_bw_limit);
}
}
diff --git a/net/mac80211/tests/s1g_tim.c b/net/mac80211/tests/s1g_tim.c
new file mode 100644
index 000000000000..642fa4ece89f
--- /dev/null
+++ b/net/mac80211/tests/s1g_tim.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * KUnit tests for S1G TIM PVB decoding. This test suite covers
+ * IEEE80211-2024 Annex L figures 8, 9, 10, 12, 13, 14. ADE mode
+ * is not covered as it is an optional encoding format and is not
+ * currently supported by mac80211.
+ *
+ * Copyright (C) 2025 Morse Micro
+ */
+#include <linux/ieee80211.h>
+#include <kunit/test.h>
+#include <kunit/test-bug.h>
+
+#define MAX_AID 128
+
+#define BC(enc_mode, inverse, blk_off) \
+ ((((blk_off) & 0x1f) << 3) | ((inverse) ? BIT(2) : 0) | \
+ ((enc_mode) & 0x3))
+
+static void byte_to_bitstr(u8 v, char *out)
+{
+ for (int b = 7; b >= 0; b--)
+ *out++ = (v & BIT(b)) ? '1' : '0';
+ *out = '\0';
+}
+
+static void dump_tim_bits(struct kunit *test,
+ const struct ieee80211_tim_ie *tim, u8 tim_len)
+{
+ const u8 *ptr = tim->virtual_map;
+ const u8 *end = (const u8 *)tim + tim_len;
+ unsigned int oct = 1;
+ unsigned int blk = 0;
+ char bits[9];
+
+ while (ptr < end) {
+ u8 ctrl = *ptr++;
+ u8 mode = ctrl & 0x03;
+ bool inverse = ctrl & BIT(2);
+ u8 blk_off = ctrl >> 3;
+
+ kunit_info(
+ test, "Block %u (ENC=%s, blk_off=%u, inverse=%u)", blk,
+ (mode == IEEE80211_S1G_TIM_ENC_MODE_BLOCK) ? "BLOCK" :
+ (mode == IEEE80211_S1G_TIM_ENC_MODE_SINGLE) ? "SINGLE" :
+ "OLB",
+ blk_off, inverse);
+
+ byte_to_bitstr(ctrl, bits);
+ kunit_info(test, " octet %2u (ctrl) : %s (0x%02x)", oct,
+ bits, ctrl);
+ ++oct;
+
+ switch (mode) {
+ case IEEE80211_S1G_TIM_ENC_MODE_BLOCK: {
+ u8 blkmap = *ptr++;
+
+ byte_to_bitstr(blkmap, bits);
+ kunit_info(test, " octet %2u (blk-map) : %s (0x%02x)",
+ oct, bits, blkmap);
+ ++oct;
+
+ for (u8 sb = 0; sb < 8; sb++) {
+ if (!(blkmap & BIT(sb)))
+ continue;
+ u8 sub = *ptr++;
+
+ byte_to_bitstr(sub, bits);
+ kunit_info(
+ test,
+ " octet %2u (SB %2u) : %s (0x%02x)",
+ oct, sb, bits, sub);
+ ++oct;
+ }
+ break;
+ }
+ case IEEE80211_S1G_TIM_ENC_MODE_SINGLE: {
+ u8 single = *ptr++;
+
+ byte_to_bitstr(single, bits);
+ kunit_info(test, " octet %2u (single) : %s (0x%02x)",
+ oct, bits, single);
+ ++oct;
+ break;
+ }
+ case IEEE80211_S1G_TIM_ENC_MODE_OLB: {
+ u8 len = *ptr++;
+
+ byte_to_bitstr(len, bits);
+ kunit_info(test, " octet %2u (len=%2u) : %s (0x%02x)",
+ oct, len, bits, len);
+ ++oct;
+
+ for (u8 i = 0; i < len && ptr < end; i++) {
+ u8 sub = *ptr++;
+
+ byte_to_bitstr(sub, bits);
+ kunit_info(
+ test,
+ " octet %2u (SB %2u) : %s (0x%02x)",
+ oct, i, bits, sub);
+ ++oct;
+ }
+ break;
+ }
+ default:
+ kunit_info(test, " ** unknown encoding 0x%x **", mode);
+ return;
+ }
+ blk++;
+ }
+}
+
+static void tim_push(u8 **p, u8 v)
+{
+ *(*p)++ = v;
+}
+
+static void tim_begin(struct ieee80211_tim_ie *tim, u8 **p)
+{
+ tim->dtim_count = 0;
+ tim->dtim_period = 1;
+ tim->bitmap_ctrl = 0;
+ *p = tim->virtual_map;
+}
+
+static u8 tim_end(struct ieee80211_tim_ie *tim, u8 *tail)
+{
+ return tail - (u8 *)tim;
+}
+
+static void pvb_add_block_bitmap(u8 **p, u8 blk_off, bool inverse, u8 blk_bmap,
+ const u8 *subblocks)
+{
+ u8 enc = IEEE80211_S1G_TIM_ENC_MODE_BLOCK;
+ u8 n = hweight8(blk_bmap);
+
+ tim_push(p, BC(enc, inverse, blk_off));
+ tim_push(p, blk_bmap);
+
+ for (u8 i = 0; i < n; i++)
+ tim_push(p, subblocks[i]);
+}
+
+static void pvb_add_single_aid(u8 **p, u8 blk_off, bool inverse, u8 single6)
+{
+ u8 enc = IEEE80211_S1G_TIM_ENC_MODE_SINGLE;
+
+ tim_push(p, BC(enc, inverse, blk_off));
+ tim_push(p, single6 & GENMASK(5, 0));
+}
+
+static void pvb_add_olb(u8 **p, u8 blk_off, bool inverse, const u8 *subblocks,
+ u8 len)
+{
+ u8 enc = IEEE80211_S1G_TIM_ENC_MODE_OLB;
+
+ tim_push(p, BC(enc, inverse, blk_off));
+ tim_push(p, len);
+ for (u8 i = 0; i < len; i++)
+ tim_push(p, subblocks[i]);
+}
+
+static void check_all_aids(struct kunit *test,
+ const struct ieee80211_tim_ie *tim, u8 tim_len,
+ const unsigned long *expected)
+{
+ for (u16 aid = 1; aid <= MAX_AID; aid++) {
+ bool want = test_bit(aid, expected);
+ bool got = ieee80211_s1g_check_tim(tim, tim_len, aid);
+
+ KUNIT_ASSERT_EQ_MSG(test, got, want,
+ "AID %u mismatch (got=%d want=%d)", aid,
+ got, want);
+ }
+}
+
+static void fill_bitmap(unsigned long *bm, const u16 *list, size_t n)
+{
+ size_t i;
+
+ bitmap_zero(bm, MAX_AID + 1);
+ for (i = 0; i < n; i++)
+ __set_bit(list[i], bm);
+}
+
+static void fill_bitmap_inverse(unsigned long *bm, u16 max_aid,
+ const u16 *except, size_t n_except)
+{
+ bitmap_zero(bm, MAX_AID + 1);
+ for (u16 aid = 1; aid <= max_aid; aid++)
+ __set_bit(aid, bm);
+
+ for (size_t i = 0; i < n_except; i++)
+ if (except[i] <= max_aid)
+ __clear_bit(except[i], bm);
+}
+
+static void s1g_tim_block_test(struct kunit *test)
+{
+ u8 buf[256] = {};
+ struct ieee80211_tim_ie *tim = (void *)buf;
+ u8 *p, tim_len;
+ static const u8 subblocks[] = {
+ 0x42, /* SB m=0: AIDs 1,6 */
+ 0xA0, /* SB m=2: AIDs 21,23 */
+ };
+ u8 blk_bmap = 0x05; /* bits 0 and 2 set */
+ bool inverse = false;
+ static const u16 set_list[] = { 1, 6, 21, 23 };
+ DECLARE_BITMAP(exp, MAX_AID + 1);
+
+ tim_begin(tim, &p);
+ pvb_add_block_bitmap(&p, 0, inverse, blk_bmap, subblocks);
+ tim_len = tim_end(tim, p);
+
+ fill_bitmap(exp, set_list, ARRAY_SIZE(set_list));
+
+ dump_tim_bits(test, tim, tim_len);
+ check_all_aids(test, tim, tim_len, exp);
+}
+
+static void s1g_tim_single_test(struct kunit *test)
+{
+ u8 buf[256] = {};
+ struct ieee80211_tim_ie *tim = (void *)buf;
+ u8 *p, tim_len;
+ bool inverse = false;
+ u8 blk_off = 0;
+ u8 single6 = 0x1f; /* 31 */
+ static const u16 set_list[] = { 31 };
+ DECLARE_BITMAP(exp, MAX_AID + 1);
+
+ tim_begin(tim, &p);
+ pvb_add_single_aid(&p, blk_off, inverse, single6);
+ tim_len = tim_end(tim, p);
+
+ fill_bitmap(exp, set_list, ARRAY_SIZE(set_list));
+
+ dump_tim_bits(test, tim, tim_len);
+ check_all_aids(test, tim, tim_len, exp);
+}
+
+static void s1g_tim_olb_test(struct kunit *test)
+{
+ u8 buf[256] = {};
+ struct ieee80211_tim_ie *tim = (void *)buf;
+ u8 *p, tim_len;
+ bool inverse = false;
+ u8 blk_off = 0;
+ static const u16 set_list[] = { 1, 6, 13, 15, 17, 22, 29, 31, 33,
+ 38, 45, 47, 49, 54, 61, 63, 65, 70 };
+ static const u8 subblocks[] = { 0x42, 0xA0, 0x42, 0xA0, 0x42,
+ 0xA0, 0x42, 0xA0, 0x42 };
+ u8 len = ARRAY_SIZE(subblocks);
+ DECLARE_BITMAP(exp, MAX_AID + 1);
+
+ tim_begin(tim, &p);
+ pvb_add_olb(&p, blk_off, inverse, subblocks, len);
+ tim_len = tim_end(tim, p);
+
+ fill_bitmap(exp, set_list, ARRAY_SIZE(set_list));
+
+ dump_tim_bits(test, tim, tim_len);
+ check_all_aids(test, tim, tim_len, exp);
+}
+
+static void s1g_tim_inverse_block_test(struct kunit *test)
+{
+ u8 buf[256] = {};
+ struct ieee80211_tim_ie *tim = (void *)buf;
+ u8 *p, tim_len;
+ /* Same sub-block content as Figure L-8, but inverse = true */
+ static const u8 subblocks[] = {
+ 0x42, /* SB m=0: AIDs 1,6 */
+ 0xA0, /* SB m=2: AIDs 21,23 */
+ };
+ u8 blk_bmap = 0x05;
+ bool inverse = true;
+ /* All AIDs except 1,6,21,23 are set */
+ static const u16 except[] = { 1, 6, 21, 23 };
+ DECLARE_BITMAP(exp, MAX_AID + 1);
+
+ tim_begin(tim, &p);
+ pvb_add_block_bitmap(&p, 0, inverse, blk_bmap, subblocks);
+ tim_len = tim_end(tim, p);
+
+ fill_bitmap_inverse(exp, 63, except, ARRAY_SIZE(except));
+
+ dump_tim_bits(test, tim, tim_len);
+ check_all_aids(test, tim, tim_len, exp);
+}
+
+static void s1g_tim_inverse_single_test(struct kunit *test)
+{
+ u8 buf[256] = {};
+ struct ieee80211_tim_ie *tim = (void *)buf;
+ u8 *p, tim_len;
+ bool inverse = true;
+ u8 blk_off = 0;
+ u8 single6 = 0x1f; /* 31 */
+ /* All AIDs except 31 are set */
+ static const u16 except[] = { 31 };
+ DECLARE_BITMAP(exp, MAX_AID + 1);
+
+ tim_begin(tim, &p);
+ pvb_add_single_aid(&p, blk_off, inverse, single6);
+ tim_len = tim_end(tim, p);
+
+ fill_bitmap_inverse(exp, 63, except, ARRAY_SIZE(except));
+
+ dump_tim_bits(test, tim, tim_len);
+ check_all_aids(test, tim, tim_len, exp);
+}
+
+static void s1g_tim_inverse_olb_test(struct kunit *test)
+{
+ u8 buf[256] = {};
+ struct ieee80211_tim_ie *tim = (void *)buf;
+ u8 *p, tim_len;
+ bool inverse = true;
+ u8 blk_off = 0, len;
+ /* All AIDs except the list below are set */
+ static const u16 except[] = { 1, 6, 13, 15, 17, 22, 29, 31, 33,
+ 38, 45, 47, 49, 54, 61, 63, 65, 70 };
+ static const u8 subblocks[] = { 0x42, 0xA0, 0x42, 0xA0, 0x42,
+ 0xA0, 0x42, 0xA0, 0x42 };
+ len = ARRAY_SIZE(subblocks);
+ DECLARE_BITMAP(exp, MAX_AID + 1);
+
+ tim_begin(tim, &p);
+ pvb_add_olb(&p, blk_off, inverse, subblocks, len);
+ tim_len = tim_end(tim, p);
+
+ fill_bitmap_inverse(exp, 127, except, ARRAY_SIZE(except));
+
+ dump_tim_bits(test, tim, tim_len);
+ check_all_aids(test, tim, tim_len, exp);
+}
+
+static struct kunit_case s1g_tim_test_cases[] = {
+ KUNIT_CASE(s1g_tim_block_test),
+ KUNIT_CASE(s1g_tim_single_test),
+ KUNIT_CASE(s1g_tim_olb_test),
+ KUNIT_CASE(s1g_tim_inverse_block_test),
+ KUNIT_CASE(s1g_tim_inverse_single_test),
+ KUNIT_CASE(s1g_tim_inverse_olb_test),
+ {}
+};
+
+static struct kunit_suite s1g_tim = {
+ .name = "mac80211-s1g-tim",
+ .test_cases = s1g_tim_test_cases,
+};
+
+kunit_test_suite(s1g_tim);