xref: /linux/net/mac80211/tests/s1g_tim.c (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * KUnit tests for S1G TIM PVB decoding. This test suite covers
4  * IEEE80211-2024 Annex L figures 8, 9, 10, 12, 13, 14. ADE mode
5  * is not covered as it is an optional encoding format and is not
6  * currently supported by mac80211.
7  *
8  * Copyright (C) 2025 Morse Micro
9  */
10 #include <linux/ieee80211.h>
11 #include <kunit/test.h>
12 #include <kunit/test-bug.h>
13 
14 #define MAX_AID 128
15 
16 #define BC(enc_mode, inverse, blk_off)                          \
17 	((((blk_off) & 0x1f) << 3) | ((inverse) ? BIT(2) : 0) | \
18 	 ((enc_mode) & 0x3))
19 
20 static void byte_to_bitstr(u8 v, char *out)
21 {
22 	for (int b = 7; b >= 0; b--)
23 		*out++ = (v & BIT(b)) ? '1' : '0';
24 	*out = '\0';
25 }
26 
27 static void dump_tim_bits(struct kunit *test,
28 			  const struct ieee80211_tim_ie *tim, u8 tim_len)
29 {
30 	const u8 *ptr = tim->virtual_map;
31 	const u8 *end = (const u8 *)tim + tim_len;
32 	unsigned int oct = 1;
33 	unsigned int blk = 0;
34 	char bits[9];
35 
36 	while (ptr < end) {
37 		u8 ctrl = *ptr++;
38 		u8 mode = ctrl & 0x03;
39 		bool inverse = ctrl & BIT(2);
40 		u8 blk_off = ctrl >> 3;
41 
42 		kunit_info(
43 			test, "Block %u (ENC=%s, blk_off=%u, inverse=%u)", blk,
44 			(mode == IEEE80211_S1G_TIM_ENC_MODE_BLOCK)  ? "BLOCK" :
45 			(mode == IEEE80211_S1G_TIM_ENC_MODE_SINGLE) ? "SINGLE" :
46 								      "OLB",
47 			blk_off, inverse);
48 
49 		byte_to_bitstr(ctrl, bits);
50 		kunit_info(test, "  octet %2u (ctrl)    : %s (0x%02x)", oct,
51 			   bits, ctrl);
52 		++oct;
53 
54 		switch (mode) {
55 		case IEEE80211_S1G_TIM_ENC_MODE_BLOCK: {
56 			u8 blkmap = *ptr++;
57 
58 			byte_to_bitstr(blkmap, bits);
59 			kunit_info(test, "  octet %2u (blk-map) : %s (0x%02x)",
60 				   oct, bits, blkmap);
61 			++oct;
62 
63 			for (u8 sb = 0; sb < 8; sb++) {
64 				if (!(blkmap & BIT(sb)))
65 					continue;
66 				u8 sub = *ptr++;
67 
68 				byte_to_bitstr(sub, bits);
69 				kunit_info(
70 					test,
71 					"  octet %2u (SB %2u)   : %s (0x%02x)",
72 					oct, sb, bits, sub);
73 				++oct;
74 			}
75 			break;
76 		}
77 		case IEEE80211_S1G_TIM_ENC_MODE_SINGLE: {
78 			u8 single = *ptr++;
79 
80 			byte_to_bitstr(single, bits);
81 			kunit_info(test, "  octet %2u (single)  : %s (0x%02x)",
82 				   oct, bits, single);
83 			++oct;
84 			break;
85 		}
86 		case IEEE80211_S1G_TIM_ENC_MODE_OLB: {
87 			u8 len = *ptr++;
88 
89 			byte_to_bitstr(len, bits);
90 			kunit_info(test, "  octet %2u (len=%2u)  : %s (0x%02x)",
91 				   oct, len, bits, len);
92 			++oct;
93 
94 			for (u8 i = 0; i < len && ptr < end; i++) {
95 				u8 sub = *ptr++;
96 
97 				byte_to_bitstr(sub, bits);
98 				kunit_info(
99 					test,
100 					"  octet %2u (SB %2u)   : %s (0x%02x)",
101 					oct, i, bits, sub);
102 				++oct;
103 			}
104 			break;
105 		}
106 		default:
107 			kunit_info(test, "  ** unknown encoding 0x%x **", mode);
108 			return;
109 		}
110 		blk++;
111 	}
112 }
113 
114 static void tim_push(u8 **p, u8 v)
115 {
116 	*(*p)++ = v;
117 }
118 
119 static void tim_begin(struct ieee80211_tim_ie *tim, u8 **p)
120 {
121 	tim->dtim_count = 0;
122 	tim->dtim_period = 1;
123 	tim->bitmap_ctrl = 0;
124 	*p = tim->virtual_map;
125 }
126 
127 static u8 tim_end(struct ieee80211_tim_ie *tim, u8 *tail)
128 {
129 	return tail - (u8 *)tim;
130 }
131 
132 static void pvb_add_block_bitmap(u8 **p, u8 blk_off, bool inverse, u8 blk_bmap,
133 				 const u8 *subblocks)
134 {
135 	u8 enc = IEEE80211_S1G_TIM_ENC_MODE_BLOCK;
136 	u8 n = hweight8(blk_bmap);
137 
138 	tim_push(p, BC(enc, inverse, blk_off));
139 	tim_push(p, blk_bmap);
140 
141 	for (u8 i = 0; i < n; i++)
142 		tim_push(p, subblocks[i]);
143 }
144 
145 static void pvb_add_single_aid(u8 **p, u8 blk_off, bool inverse, u8 single6)
146 {
147 	u8 enc = IEEE80211_S1G_TIM_ENC_MODE_SINGLE;
148 
149 	tim_push(p, BC(enc, inverse, blk_off));
150 	tim_push(p, single6 & GENMASK(5, 0));
151 }
152 
153 static void pvb_add_olb(u8 **p, u8 blk_off, bool inverse, const u8 *subblocks,
154 			u8 len)
155 {
156 	u8 enc = IEEE80211_S1G_TIM_ENC_MODE_OLB;
157 
158 	tim_push(p, BC(enc, inverse, blk_off));
159 	tim_push(p, len);
160 	for (u8 i = 0; i < len; i++)
161 		tim_push(p, subblocks[i]);
162 }
163 
164 static void check_all_aids(struct kunit *test,
165 			   const struct ieee80211_tim_ie *tim, u8 tim_len,
166 			   const unsigned long *expected)
167 {
168 	for (u16 aid = 1; aid <= MAX_AID; aid++) {
169 		bool want = test_bit(aid, expected);
170 		bool got = ieee80211_s1g_check_tim(tim, tim_len, aid);
171 
172 		KUNIT_ASSERT_EQ_MSG(test, got, want,
173 				    "AID %u mismatch (got=%d want=%d)", aid,
174 				    got, want);
175 	}
176 }
177 
178 static void fill_bitmap(unsigned long *bm, const u16 *list, size_t n)
179 {
180 	size_t i;
181 
182 	bitmap_zero(bm, MAX_AID + 1);
183 	for (i = 0; i < n; i++)
184 		__set_bit(list[i], bm);
185 }
186 
187 static void fill_bitmap_inverse(unsigned long *bm, u16 max_aid,
188 				const u16 *except, size_t n_except)
189 {
190 	bitmap_zero(bm, MAX_AID + 1);
191 	for (u16 aid = 1; aid <= max_aid; aid++)
192 		__set_bit(aid, bm);
193 
194 	for (size_t i = 0; i < n_except; i++)
195 		if (except[i] <= max_aid)
196 			__clear_bit(except[i], bm);
197 }
198 
199 static void s1g_tim_block_test(struct kunit *test)
200 {
201 	u8 buf[256] = {};
202 	struct ieee80211_tim_ie *tim = (void *)buf;
203 	u8 *p, tim_len;
204 	static const u8 subblocks[] = {
205 		0x42, /* SB m=0: AIDs 1,6 */
206 		0xA0, /* SB m=2: AIDs 21,23 */
207 	};
208 	u8 blk_bmap = 0x05; /* bits 0 and 2 set */
209 	bool inverse = false;
210 	static const u16 set_list[] = { 1, 6, 21, 23 };
211 	DECLARE_BITMAP(exp, MAX_AID + 1);
212 
213 	tim_begin(tim, &p);
214 	pvb_add_block_bitmap(&p, 0, inverse, blk_bmap, subblocks);
215 	tim_len = tim_end(tim, p);
216 
217 	fill_bitmap(exp, set_list, ARRAY_SIZE(set_list));
218 
219 	dump_tim_bits(test, tim, tim_len);
220 	check_all_aids(test, tim, tim_len, exp);
221 }
222 
223 static void s1g_tim_single_test(struct kunit *test)
224 {
225 	u8 buf[256] = {};
226 	struct ieee80211_tim_ie *tim = (void *)buf;
227 	u8 *p, tim_len;
228 	bool inverse = false;
229 	u8 blk_off = 0;
230 	u8 single6 = 0x1f; /* 31 */
231 	static const u16 set_list[] = { 31 };
232 	DECLARE_BITMAP(exp, MAX_AID + 1);
233 
234 	tim_begin(tim, &p);
235 	pvb_add_single_aid(&p, blk_off, inverse, single6);
236 	tim_len = tim_end(tim, p);
237 
238 	fill_bitmap(exp, set_list, ARRAY_SIZE(set_list));
239 
240 	dump_tim_bits(test, tim, tim_len);
241 	check_all_aids(test, tim, tim_len, exp);
242 }
243 
244 static void s1g_tim_olb_test(struct kunit *test)
245 {
246 	u8 buf[256] = {};
247 	struct ieee80211_tim_ie *tim = (void *)buf;
248 	u8 *p, tim_len;
249 	bool inverse = false;
250 	u8 blk_off = 0;
251 	static const u16 set_list[] = { 1,  6,	13, 15, 17, 22, 29, 31, 33,
252 					38, 45, 47, 49, 54, 61, 63, 65, 70 };
253 	static const u8 subblocks[] = { 0x42, 0xA0, 0x42, 0xA0, 0x42,
254 					0xA0, 0x42, 0xA0, 0x42 };
255 	u8 len = ARRAY_SIZE(subblocks);
256 	DECLARE_BITMAP(exp, MAX_AID + 1);
257 
258 	tim_begin(tim, &p);
259 	pvb_add_olb(&p, blk_off, inverse, subblocks, len);
260 	tim_len = tim_end(tim, p);
261 
262 	fill_bitmap(exp, set_list, ARRAY_SIZE(set_list));
263 
264 	dump_tim_bits(test, tim, tim_len);
265 	check_all_aids(test, tim, tim_len, exp);
266 }
267 
268 static void s1g_tim_inverse_block_test(struct kunit *test)
269 {
270 	u8 buf[256] = {};
271 	struct ieee80211_tim_ie *tim = (void *)buf;
272 	u8 *p, tim_len;
273 	/* Same sub-block content as Figure L-8, but inverse = true */
274 	static const u8 subblocks[] = {
275 		0x42, /* SB m=0: AIDs 1,6 */
276 		0xA0, /* SB m=2: AIDs 21,23 */
277 	};
278 	u8 blk_bmap = 0x05;
279 	bool inverse = true;
280 	/*  All AIDs except 1,6,21,23 are set */
281 	static const u16 except[] = { 1, 6, 21, 23 };
282 	DECLARE_BITMAP(exp, MAX_AID + 1);
283 
284 	tim_begin(tim, &p);
285 	pvb_add_block_bitmap(&p, 0, inverse, blk_bmap, subblocks);
286 	tim_len = tim_end(tim, p);
287 
288 	fill_bitmap_inverse(exp, 63, except, ARRAY_SIZE(except));
289 
290 	dump_tim_bits(test, tim, tim_len);
291 	check_all_aids(test, tim, tim_len, exp);
292 }
293 
294 static void s1g_tim_inverse_single_test(struct kunit *test)
295 {
296 	u8 buf[256] = {};
297 	struct ieee80211_tim_ie *tim = (void *)buf;
298 	u8 *p, tim_len;
299 	bool inverse = true;
300 	u8 blk_off = 0;
301 	u8 single6 = 0x1f; /* 31 */
302 	/*  All AIDs except 31 are set */
303 	static const u16 except[] = { 31 };
304 	DECLARE_BITMAP(exp, MAX_AID + 1);
305 
306 	tim_begin(tim, &p);
307 	pvb_add_single_aid(&p, blk_off, inverse, single6);
308 	tim_len = tim_end(tim, p);
309 
310 	fill_bitmap_inverse(exp, 63, except, ARRAY_SIZE(except));
311 
312 	dump_tim_bits(test, tim, tim_len);
313 	check_all_aids(test, tim, tim_len, exp);
314 }
315 
316 static void s1g_tim_inverse_olb_test(struct kunit *test)
317 {
318 	u8 buf[256] = {};
319 	struct ieee80211_tim_ie *tim = (void *)buf;
320 	u8 *p, tim_len;
321 	bool inverse = true;
322 	u8 blk_off = 0, len;
323 	/*  All AIDs except the list below are set */
324 	static const u16 except[] = { 1,  6,  13, 15, 17, 22, 29, 31, 33,
325 				      38, 45, 47, 49, 54, 61, 63, 65, 70 };
326 	static const u8 subblocks[] = { 0x42, 0xA0, 0x42, 0xA0, 0x42,
327 					0xA0, 0x42, 0xA0, 0x42 };
328 	len = ARRAY_SIZE(subblocks);
329 	DECLARE_BITMAP(exp, MAX_AID + 1);
330 
331 	tim_begin(tim, &p);
332 	pvb_add_olb(&p, blk_off, inverse, subblocks, len);
333 	tim_len = tim_end(tim, p);
334 
335 	fill_bitmap_inverse(exp, 127, except, ARRAY_SIZE(except));
336 
337 	dump_tim_bits(test, tim, tim_len);
338 	check_all_aids(test, tim, tim_len, exp);
339 }
340 
341 static struct kunit_case s1g_tim_test_cases[] = {
342 	KUNIT_CASE(s1g_tim_block_test),
343 	KUNIT_CASE(s1g_tim_single_test),
344 	KUNIT_CASE(s1g_tim_olb_test),
345 	KUNIT_CASE(s1g_tim_inverse_block_test),
346 	KUNIT_CASE(s1g_tim_inverse_single_test),
347 	KUNIT_CASE(s1g_tim_inverse_olb_test),
348 	{}
349 };
350 
351 static struct kunit_suite s1g_tim = {
352 	.name = "mac80211-s1g-tim",
353 	.test_cases = s1g_tim_test_cases,
354 };
355 
356 kunit_test_suite(s1g_tim);
357