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