File Index
Files and directories
Sign in
FILE

s1g_tim.c

File preview with syntax highlighting.

linux-6.19.14/net/mac80211/tests/s1g_tim.c C
  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
 20static 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
 27static 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
114static void tim_push(u8 **p, u8 v)
115{
116	*(*p)++ = v;
117}
118
119static 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
127static u8 tim_end(struct ieee80211_tim_ie *tim, u8 *tail)
128{
129	return tail - (u8 *)tim;
130}
131
132static 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
145static 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
153static 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
164static 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
178static 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
187static 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
199static 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
223static 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
244static 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
268static 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
294static 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
316static 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
341static 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
351static struct kunit_suite s1g_tim = {
352	.name = "mac80211-s1g-tim",
353	.test_cases = s1g_tim_test_cases,
354};
355
356kunit_test_suite(s1g_tim);
~/ Read-only file preview.