]> www.infradead.org Git - users/hch/misc.git/commitdiff
wifi: mac80211: kunit: add kunit tests for S1G PVB decoding
authorLachlan Hodges <lachlan.hodges@morsemicro.com>
Fri, 25 Jul 2025 13:22:21 +0000 (23:22 +1000)
committerJohannes Berg <johannes.berg@intel.com>
Thu, 4 Sep 2025 09:19:01 +0000 (11:19 +0200)
Add support for testing the 6 examples mentioned in IEEE80211-2024
Annex L. These tests cover the 3 mandatory decoding modes being
block bitmap, single AID and OLB alongside their equivalent
inverses.

Test output:

1..6
 s1g_tim_block_test: Block 0 (ENC=BLOCK, blk_off=0, inverse=0)
 s1g_tim_block_test:   octet  1 (ctrl)    : 00000000 (0x00)
 s1g_tim_block_test:   octet  2 (blk-map) : 00000101 (0x05)
 s1g_tim_block_test:   octet  3 (SB  0)   : 01000010 (0x42)
 s1g_tim_block_test:   octet  4 (SB  2)   : 10100000 (0xa0)
ok 1 s1g_tim_block_test
 s1g_tim_single_test: Block 0 (ENC=SINGLE, blk_off=0, inverse=0)
 s1g_tim_single_test:   octet  1 (ctrl)    : 00000001 (0x01)
 s1g_tim_single_test:   octet  2 (single)  : 00011111 (0x1f)
ok 2 s1g_tim_single_test
 s1g_tim_olb_test: Block 0 (ENC=OLB, blk_off=0, inverse=0)
 s1g_tim_olb_test:   octet  1 (ctrl)    : 00000010 (0x02)
 s1g_tim_olb_test:   octet  2 (len= 9)  : 00001001 (0x09)
 s1g_tim_olb_test:   octet  3 (SB  0)   : 01000010 (0x42)
 s1g_tim_olb_test:   octet  4 (SB  1)   : 10100000 (0xa0)
 s1g_tim_olb_test:   octet  5 (SB  2)   : 01000010 (0x42)
 s1g_tim_olb_test:   octet  6 (SB  3)   : 10100000 (0xa0)
 s1g_tim_olb_test:   octet  7 (SB  4)   : 01000010 (0x42)
 s1g_tim_olb_test:   octet  8 (SB  5)   : 10100000 (0xa0)
 s1g_tim_olb_test:   octet  9 (SB  6)   : 01000010 (0x42)
 s1g_tim_olb_test:   octet 10 (SB  7)   : 10100000 (0xa0)
 s1g_tim_olb_test:   octet 11 (SB  8)   : 01000010 (0x42)
ok 3 s1g_tim_olb_test
 s1g_tim_inverse_block_test: Block 0 (ENC=BLOCK, blk_off=0, inverse=1)
 s1g_tim_inverse_block_test:   octet  1 (ctrl)    : 00000100 (0x04)
 s1g_tim_inverse_block_test:   octet  2 (blk-map) : 00000101 (0x05)
 s1g_tim_inverse_block_test:   octet  3 (SB  0)   : 01000010 (0x42)
 s1g_tim_inverse_block_test:   octet  4 (SB  2)   : 10100000 (0xa0)
ok 4 s1g_tim_inverse_block_test
 s1g_tim_inverse_single_test: Block 0 (ENC=SINGLE, blk_off=0, inverse=1)
 s1g_tim_inverse_single_test:   octet  1 (ctrl)    : 00000101 (0x05)
 s1g_tim_inverse_single_test:   octet  2 (single)  : 00011111 (0x1f)
ok 5 s1g_tim_inverse_single_test
 s1g_tim_inverse_olb_test: Block 0 (ENC=OLB, blk_off=0, inverse=1)
 s1g_tim_inverse_olb_test:   octet  1 (ctrl)    : 00000110 (0x06)
 s1g_tim_inverse_olb_test:   octet  2 (len= 9)  : 00001001 (0x09)
 s1g_tim_inverse_olb_test:   octet  3 (SB  0)   : 01000010 (0x42)
 s1g_tim_inverse_olb_test:   octet  4 (SB  1)   : 10100000 (0xa0)
 s1g_tim_inverse_olb_test:   octet  5 (SB  2)   : 01000010 (0x42)
 s1g_tim_inverse_olb_test:   octet  6 (SB  3)   : 10100000 (0xa0)
 s1g_tim_inverse_olb_test:   octet  7 (SB  4)   : 01000010 (0x42)
 s1g_tim_inverse_olb_test:   octet  8 (SB  5)   : 10100000 (0xa0)
 s1g_tim_inverse_olb_test:   octet  9 (SB  6)   : 01000010 (0x42)
 s1g_tim_inverse_olb_test:   octet 10 (SB  7)   : 10100000 (0xa0)
 s1g_tim_inverse_olb_test:   octet 11 (SB  8)   : 01000010 (0x42)
ok 6 s1g_tim_inverse_olb_test
 mac80211-s1g-tim: pass:6 fail:0 skip:0 total:6
 Totals: pass:6 fail:0 skip:0 total:6

Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
Link: https://patch.msgid.link/20250725132221.258217-4-lachlan.hodges@morsemicro.com
[make tim_push() void, non-inline]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/tests/Makefile
net/mac80211/tests/s1g_tim.c [new file with mode: 0644]

index 3b0c08356fc5502084a0148239b597725f4f27c6..3c7f874e5c41262b3d1eaf0b0d2f7b6f5858bf48 100644 (file)
@@ -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/s1g_tim.c b/net/mac80211/tests/s1g_tim.c
new file mode 100644 (file)
index 0000000..642fa4e
--- /dev/null
@@ -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);