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