xref: /linux/net/mac80211/tests/chan-mode.c (revision fcee7d82f27d6a8b1ddc5bbefda59b4e441e9bc0)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * KUnit tests for channel mode functions
4  *
5  * Copyright (C) 2024-2025 Intel Corporation
6  */
7 #include <net/cfg80211.h>
8 #include <kunit/test.h>
9 
10 #include "util.h"
11 
12 MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
13 
14 static const struct determine_chan_mode_case {
15 	const char *desc;
16 	u8 extra_supp_rate;
17 	enum ieee80211_conn_mode conn_mode;
18 	enum ieee80211_conn_mode expected_mode;
19 	bool strict;
20 	u8 userspace_selector;
21 	struct ieee80211_ht_cap ht_capa_mask;
22 	struct ieee80211_vht_cap vht_capa;
23 	struct ieee80211_vht_cap vht_capa_mask;
24 	u8 vht_basic_mcs_1_4_set:1,
25 	   vht_basic_mcs_5_8_set:1,
26 	   he_basic_mcs_1_4_set:1,
27 	   he_basic_mcs_5_8_set:1;
28 	u8 vht_basic_mcs_1_4, vht_basic_mcs_5_8;
29 	u8 he_basic_mcs_1_4, he_basic_mcs_5_8;
30 	u8 eht_mcs7_min_nss;
31 	u16 eht_disabled_subchannels;
32 	u8 eht_bw;
33 	enum ieee80211_conn_bw_limit conn_bw_limit;
34 	enum ieee80211_conn_bw_limit expected_bw_limit;
35 	int error;
36 } determine_chan_mode_cases[] = {
37 	{
38 		.desc = "Normal case, EHT is working",
39 		.conn_mode = IEEE80211_CONN_MODE_EHT,
40 		.expected_mode = IEEE80211_CONN_MODE_EHT,
41 	}, {
42 		.desc = "Requiring EHT support is fine",
43 		.conn_mode = IEEE80211_CONN_MODE_EHT,
44 		.expected_mode = IEEE80211_CONN_MODE_EHT,
45 		.extra_supp_rate = 0x80 | BSS_MEMBERSHIP_SELECTOR_EHT_PHY,
46 	}, {
47 		.desc = "Lowering the mode limits us",
48 		.conn_mode = IEEE80211_CONN_MODE_VHT,
49 		.expected_mode = IEEE80211_CONN_MODE_VHT,
50 	}, {
51 		.desc = "Requesting a basic rate/selector that we do not support",
52 		.conn_mode = IEEE80211_CONN_MODE_EHT,
53 		.extra_supp_rate = 0x80 | (BSS_MEMBERSHIP_SELECTOR_MIN - 1),
54 		.error = EINVAL,
55 	}, {
56 		.desc = "As before, but userspace says it is taking care of it",
57 		.conn_mode = IEEE80211_CONN_MODE_EHT,
58 		.userspace_selector = BSS_MEMBERSHIP_SELECTOR_MIN - 1,
59 		.extra_supp_rate = 0x80 | (BSS_MEMBERSHIP_SELECTOR_MIN - 1),
60 		.expected_mode = IEEE80211_CONN_MODE_EHT,
61 	}, {
62 		.desc = "Masking out a supported rate in HT capabilities",
63 		.conn_mode = IEEE80211_CONN_MODE_EHT,
64 		.expected_mode = IEEE80211_CONN_MODE_LEGACY,
65 		.ht_capa_mask = {
66 			.mcs.rx_mask[0] = 0xf7,
67 		},
68 		.strict = true,
69 	}, {
70 		.desc = "Masking out a RX rate in VHT capabilities",
71 		.conn_mode = IEEE80211_CONN_MODE_EHT,
72 		.expected_mode = IEEE80211_CONN_MODE_HT,
73 		/* Only one RX stream at MCS 0-7 */
74 		.vht_capa = {
75 			.supp_mcs.rx_mcs_map =
76 				cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_7),
77 		},
78 		.vht_capa_mask = {
79 			.supp_mcs.rx_mcs_map = cpu_to_le16(0xffff),
80 		},
81 		.strict = true,
82 	}, {
83 		.desc = "Masking out a TX rate in VHT capabilities",
84 		.conn_mode = IEEE80211_CONN_MODE_EHT,
85 		.expected_mode = IEEE80211_CONN_MODE_HT,
86 		/* Only one TX stream at MCS 0-7 */
87 		.vht_capa = {
88 			.supp_mcs.tx_mcs_map =
89 				cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_7),
90 		},
91 		.vht_capa_mask = {
92 			.supp_mcs.tx_mcs_map = cpu_to_le16(0xffff),
93 		},
94 		.strict = true,
95 	}, {
96 		.desc = "AP has higher VHT requirement than client",
97 		.conn_mode = IEEE80211_CONN_MODE_EHT,
98 		.expected_mode = IEEE80211_CONN_MODE_HT,
99 		.vht_basic_mcs_5_8_set = 1,
100 		.vht_basic_mcs_5_8 = 0xFE, /* require 5th stream */
101 		.strict = true,
102 	}, {
103 		.desc = "all zero VHT basic rates are ignored (many APs broken)",
104 		.conn_mode = IEEE80211_CONN_MODE_VHT,
105 		.expected_mode = IEEE80211_CONN_MODE_VHT,
106 		.vht_basic_mcs_1_4_set = 1,
107 		.vht_basic_mcs_5_8_set = 1,
108 	}, {
109 		.desc = "AP requires 3 HE streams but client only has two",
110 		.conn_mode = IEEE80211_CONN_MODE_EHT,
111 		.expected_mode = IEEE80211_CONN_MODE_VHT,
112 		.he_basic_mcs_1_4 = 0b11001010,
113 		.he_basic_mcs_1_4_set = 1,
114 	}, {
115 		.desc = "all zero HE basic rates are ignored (iPhone workaround)",
116 		.conn_mode = IEEE80211_CONN_MODE_HE,
117 		.expected_mode = IEEE80211_CONN_MODE_HE,
118 		.he_basic_mcs_1_4_set = 1,
119 		.he_basic_mcs_5_8_set = 1,
120 	}, {
121 		.desc = "AP requires too many RX streams with EHT MCS 7",
122 		.conn_mode = IEEE80211_CONN_MODE_EHT,
123 		.expected_mode = IEEE80211_CONN_MODE_HE,
124 		.eht_mcs7_min_nss = 0x15,
125 	}, {
126 		.desc = "AP requires too many TX streams with EHT MCS 7",
127 		.conn_mode = IEEE80211_CONN_MODE_EHT,
128 		.expected_mode = IEEE80211_CONN_MODE_HE,
129 		.eht_mcs7_min_nss = 0x51,
130 	}, {
131 		.desc = "AP requires too many RX streams with EHT MCS 7 and EHT is required",
132 		.extra_supp_rate = 0x80 | BSS_MEMBERSHIP_SELECTOR_EHT_PHY,
133 		.conn_mode = IEEE80211_CONN_MODE_EHT,
134 		.eht_mcs7_min_nss = 0x15,
135 		.error = EINVAL,
136 	}, {
137 		.desc = "80 MHz EHT is downgraded to 40 MHz HE due to puncturing",
138 		.conn_mode = IEEE80211_CONN_MODE_EHT,
139 		.expected_mode = IEEE80211_CONN_MODE_HE,
140 		.conn_bw_limit = IEEE80211_CONN_BW_LIMIT_80,
141 		.expected_bw_limit = IEEE80211_CONN_BW_LIMIT_40,
142 		.eht_disabled_subchannels = 0x08,
143 		.eht_bw = IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ,
144 	}
145 };
KUNIT_ARRAY_PARAM_DESC(determine_chan_mode,determine_chan_mode_cases,desc)146 KUNIT_ARRAY_PARAM_DESC(determine_chan_mode, determine_chan_mode_cases, desc)
147 
148 static void test_determine_chan_mode(struct kunit *test)
149 {
150 	const struct determine_chan_mode_case *params = test->param_value;
151 	struct t_sdata *t_sdata = T_SDATA(test);
152 	struct ieee80211_conn_settings conn = {
153 		.mode = params->conn_mode,
154 		.bw_limit = params->conn_bw_limit,
155 	};
156 	struct cfg80211_bss cbss = {
157 		.channel = &t_sdata->band_5ghz.channels[0],
158 	};
159 	unsigned long userspace_selectors[BITS_TO_LONGS(128)] = {};
160 	u8 bss_ies[] = {
161 		/* Supported Rates */
162 		WLAN_EID_SUPP_RATES, 0x08,
163 		0x82, 0x84, 0x8b, 0x96, 0xc, 0x12, 0x18, 0x24,
164 		/* Extended Supported Rates */
165 		WLAN_EID_EXT_SUPP_RATES, 0x05,
166 		0x30, 0x48, 0x60, 0x6c, params->extra_supp_rate,
167 		/* HT Capabilities */
168 		WLAN_EID_HT_CAPABILITY, 0x1a,
169 		0x0c, 0x00, 0x1b, 0xff, 0xff, 0x00, 0x00, 0x00,
170 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
171 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
172 		0x00, 0x00,
173 		/* HT Information (0xff for 1 stream) */
174 		WLAN_EID_HT_OPERATION, 0x16,
175 		0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
176 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
177 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
178 		/* VHT Capabilities */
179 		WLAN_EID_VHT_CAPABILITY, 0xc,
180 		0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
181 		0xff, 0xff, 0x00, 0x00,
182 		/* VHT Operation */
183 		WLAN_EID_VHT_OPERATION, 0x05,
184 		0x00, 0x00, 0x00,
185 		params->vht_basic_mcs_1_4_set ?
186 			params->vht_basic_mcs_1_4 :
187 			le16_get_bits(t_sdata->band_5ghz.vht_cap.vht_mcs.rx_mcs_map, 0xff),
188 		params->vht_basic_mcs_5_8_set ?
189 			params->vht_basic_mcs_5_8 :
190 			le16_get_bits(t_sdata->band_5ghz.vht_cap.vht_mcs.rx_mcs_map, 0xff00),
191 		/* HE Capabilities */
192 		WLAN_EID_EXTENSION, 0x16, WLAN_EID_EXT_HE_CAPABILITY,
193 		0x01, 0x78, 0xc8, 0x1a, 0x40, 0x00, 0x00, 0xbf,
194 		0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
195 		0x00, 0xfa, 0xff, 0xfa, 0xff,
196 		/* HE Operation (permit overriding values) */
197 		WLAN_EID_EXTENSION, 0x07, WLAN_EID_EXT_HE_OPERATION,
198 		0xf0, 0x3f, 0x00, 0xb0,
199 		params->he_basic_mcs_1_4_set ? params->he_basic_mcs_1_4 : 0xfc,
200 		params->he_basic_mcs_5_8_set ? params->he_basic_mcs_5_8 : 0xff,
201 		/* EHT Capabilities */
202 		WLAN_EID_EXTENSION, 0x12, WLAN_EID_EXT_EHT_CAPABILITY,
203 		0x07, 0x00, 0x1c, 0x00, 0x00, 0xfe, 0xff, 0xff,
204 		0x7f, 0x01, 0x00, 0x88, 0x88, 0x88, 0x00, 0x00,
205 		0x00,
206 		/* EHT Operation */
207 		WLAN_EID_EXTENSION, 0x0b, WLAN_EID_EXT_EHT_OPERATION,
208 		0x03, params->eht_mcs7_min_nss ? params->eht_mcs7_min_nss : 0x11,
209 		0x00, 0x00, 0x00, params->eht_bw,
210 		params->eht_bw == IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ ? 42 : 36,
211 		0x00,
212 		u16_get_bits(params->eht_disabled_subchannels, 0xff),
213 		u16_get_bits(params->eht_disabled_subchannels, 0xff00),
214 	};
215 	struct ieee80211_chan_req chanreq = {};
216 	struct cfg80211_chan_def ap_chandef = {};
217 	struct ieee802_11_elems *elems;
218 
219 	/* To force EHT downgrade to HE on punctured 80 MHz downgraded to 40 MHz */
220 	set_bit(IEEE80211_HW_DISALLOW_PUNCTURING, t_sdata->local.hw.flags);
221 
222 	if (params->strict)
223 		set_bit(IEEE80211_HW_STRICT, t_sdata->local.hw.flags);
224 	else
225 		clear_bit(IEEE80211_HW_STRICT, t_sdata->local.hw.flags);
226 
227 	t_sdata->sdata->u.mgd.ht_capa_mask = params->ht_capa_mask;
228 	t_sdata->sdata->u.mgd.vht_capa = params->vht_capa;
229 	t_sdata->sdata->u.mgd.vht_capa_mask = params->vht_capa_mask;
230 
231 	if (params->userspace_selector)
232 		set_bit(params->userspace_selector, userspace_selectors);
233 
234 	rcu_assign_pointer(cbss.ies,
235 			   kunit_kzalloc(test,
236 					 sizeof(cbss) + sizeof(bss_ies),
237 					 GFP_KERNEL));
238 	KUNIT_ASSERT_NOT_NULL(test, rcu_access_pointer(cbss.ies));
239 	((struct cfg80211_bss_ies *)rcu_access_pointer(cbss.ies))->len = sizeof(bss_ies);
240 
241 	memcpy((void *)rcu_access_pointer(cbss.ies)->data, bss_ies,
242 	       sizeof(bss_ies));
243 
244 	rcu_read_lock();
245 	elems = ieee80211_determine_chan_mode(t_sdata->sdata, &conn, &cbss,
246 					      0, &chanreq, &ap_chandef,
247 					      userspace_selectors);
248 	rcu_read_unlock();
249 
250 	/* We do not need elems, free them if they are valid. */
251 	if (!IS_ERR_OR_NULL(elems))
252 		kfree(elems);
253 
254 	if (params->error) {
255 		KUNIT_ASSERT_TRUE(test, IS_ERR(elems));
256 		KUNIT_ASSERT_EQ(test, PTR_ERR(elems), -params->error);
257 	} else {
258 		KUNIT_ASSERT_NOT_ERR_OR_NULL(test, elems);
259 		KUNIT_ASSERT_EQ(test, conn.mode, params->expected_mode);
260 		KUNIT_ASSERT_EQ(test, conn.bw_limit, params->expected_bw_limit);
261 	}
262 }
263 
264 static struct kunit_case chan_mode_cases[] = {
265 	KUNIT_CASE_PARAM(test_determine_chan_mode,
266 			 determine_chan_mode_gen_params),
267 	{}
268 };
269 
270 static struct kunit_suite chan_mode = {
271 	.name = "mac80211-mlme-chan-mode",
272 	.test_cases = chan_mode_cases,
273 };
274 
275 kunit_test_suite(chan_mode);
276