1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3 * KUnit tests for the iwlwifi device info table
4 *
5 * Copyright (C) 2023-2025 Intel Corporation
6 */
7 #include <kunit/test.h>
8 #include <linux/pci.h>
9 #include "iwl-drv.h"
10 #include "iwl-config.h"
11
12 MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
13
iwl_pci_print_dev_info(const char * pfx,const struct iwl_dev_info * di)14 static void iwl_pci_print_dev_info(const char *pfx, const struct iwl_dev_info *di)
15 {
16 u16 subdevice_mask = GENMASK(di->subdevice_m_h, di->subdevice_m_l);
17 char buf[100] = {};
18 int pos = 0;
19
20 if (di->match_rf_type)
21 pos += scnprintf(buf + pos, sizeof(buf) - pos,
22 " rf_type=%03x", di->rf_type);
23 else
24 pos += scnprintf(buf + pos, sizeof(buf) - pos,
25 " rf_type=*");
26
27 if (di->match_bw_limit)
28 pos += scnprintf(buf + pos, sizeof(buf) - pos,
29 " bw_limit=%d", di->bw_limit);
30 else
31 pos += scnprintf(buf + pos, sizeof(buf) - pos,
32 " bw_limit=*");
33
34 if (di->match_rf_id)
35 pos += scnprintf(buf + pos, sizeof(buf) - pos,
36 " rf_id=0x%x", di->rf_id);
37 else
38 pos += scnprintf(buf + pos, sizeof(buf) - pos,
39 " rf_id=*");
40
41 if (di->match_cdb)
42 pos += scnprintf(buf + pos, sizeof(buf) - pos,
43 " cdb=%d", di->cdb);
44 else
45 pos += scnprintf(buf + pos, sizeof(buf) - pos,
46 " cdb=*");
47
48 if (di->match_discrete)
49 pos += scnprintf(buf + pos, sizeof(buf) - pos,
50 " discrete=%d",
51 di->discrete);
52 else
53 pos += scnprintf(buf + pos, sizeof(buf) - pos,
54 " discrete=*");
55
56 printk(KERN_DEBUG "%sdev=%04x subdev=%04x/%04x%s\n",
57 pfx, di->device, di->subdevice, subdevice_mask, buf);
58 }
59
devinfo_table_order(struct kunit * test)60 static void devinfo_table_order(struct kunit *test)
61 {
62 int idx;
63
64 for (idx = 0; idx < iwl_dev_info_table_size; idx++) {
65 const struct iwl_dev_info *di = &iwl_dev_info_table[idx];
66 const struct iwl_dev_info *ret;
67
68 ret = iwl_pci_find_dev_info(di->device, di->subdevice,
69 di->rf_type, di->cdb,
70 di->rf_id, di->bw_limit,
71 di->discrete);
72 if (!ret) {
73 iwl_pci_print_dev_info("No entry found for: ", di);
74 KUNIT_FAIL(test,
75 "No entry found for entry at index %d\n", idx);
76 } else if (ret != di) {
77 iwl_pci_print_dev_info("searched: ", di);
78 iwl_pci_print_dev_info("found: ", ret);
79 KUNIT_FAIL(test,
80 "unusable entry at index %d (found index %d instead)\n",
81 idx, (int)(ret - iwl_dev_info_table));
82 }
83 }
84 }
85
devinfo_discrete_match(struct kunit * test)86 static void devinfo_discrete_match(struct kunit *test)
87 {
88 /*
89 * Validate that any entries with discrete/integrated match have
90 * the same config with the value inverted (if they match at all.)
91 */
92
93 for (int idx = 0; idx < iwl_dev_info_table_size; idx++) {
94 const struct iwl_dev_info *di = &iwl_dev_info_table[idx];
95 const struct iwl_dev_info *ret;
96
97 if (!di->match_discrete)
98 continue;
99
100 ret = iwl_pci_find_dev_info(di->device, di->subdevice,
101 di->rf_type, di->cdb,
102 di->rf_id, di->bw_limit,
103 !di->discrete);
104 if (!ret)
105 continue;
106 KUNIT_EXPECT_PTR_EQ(test, di->cfg, ret->cfg);
107 /* and check the name is different, that'd be the point of it */
108 KUNIT_EXPECT_NE(test, strcmp(di->name, ret->name), 0);
109 }
110 }
111
devinfo_names(struct kunit * test)112 static void devinfo_names(struct kunit *test)
113 {
114 int idx;
115
116 for (idx = 0; idx < iwl_dev_info_table_size; idx++) {
117 const struct iwl_dev_info *di = &iwl_dev_info_table[idx];
118
119 KUNIT_ASSERT_TRUE(test, di->name);
120 }
121 }
122
devinfo_no_cfg_dups(struct kunit * test)123 static void devinfo_no_cfg_dups(struct kunit *test)
124 {
125 for (int i = 0; i < iwl_dev_info_table_size; i++) {
126 const struct iwl_rf_cfg *cfg_i = iwl_dev_info_table[i].cfg;
127
128 for (int j = 0; j < i; j++) {
129 const struct iwl_rf_cfg *cfg_j = iwl_dev_info_table[j].cfg;
130
131 if (cfg_i == cfg_j)
132 continue;
133
134 KUNIT_EXPECT_NE_MSG(test, memcmp(cfg_i, cfg_j,
135 sizeof(*cfg_i)), 0,
136 "identical configs: %ps and %ps\n",
137 cfg_i, cfg_j);
138 }
139 }
140 }
141
devinfo_no_name_dups(struct kunit * test)142 static void devinfo_no_name_dups(struct kunit *test)
143 {
144 for (int i = 0; i < iwl_dev_info_table_size; i++) {
145 for (int j = 0; j < i; j++) {
146 if (iwl_dev_info_table[i].name == iwl_dev_info_table[j].name)
147 continue;
148
149 KUNIT_EXPECT_NE_MSG(test,
150 strcmp(iwl_dev_info_table[i].name,
151 iwl_dev_info_table[j].name),
152 0,
153 "name dup: %ps/%ps",
154 iwl_dev_info_table[i].name,
155 iwl_dev_info_table[j].name);
156 }
157 }
158 }
159
devinfo_check_subdev_match(struct kunit * test)160 static void devinfo_check_subdev_match(struct kunit *test)
161 {
162 for (int i = 0; i < iwl_dev_info_table_size; i++) {
163 const struct iwl_dev_info *di = &iwl_dev_info_table[i];
164 u16 subdevice_mask = GENMASK(di->subdevice_m_h,
165 di->subdevice_m_l);
166
167 /* if BW limit bit is matched then must have a limit */
168 if (di->match_bw_limit == 1 && di->bw_limit == 1)
169 KUNIT_EXPECT_NE(test, di->cfg->bw_limit, 0);
170
171 /* if subdevice is ANY we can have RF ID/BW limit */
172 if (di->subdevice == (u16)IWL_CFG_ANY)
173 continue;
174
175 /* same if the subdevice mask doesn't overlap them */
176 if (IWL_SUBDEVICE_RF_ID(subdevice_mask) == 0 &&
177 IWL_SUBDEVICE_BW_LIM(subdevice_mask) == 0)
178 continue;
179
180 /* but otherwise they shouldn't be used */
181 KUNIT_EXPECT_EQ(test, (int)di->match_rf_id, 0);
182 KUNIT_EXPECT_EQ(test, (int)di->match_bw_limit, 0);
183 }
184 }
185
devinfo_check_killer_subdev(struct kunit * test)186 static void devinfo_check_killer_subdev(struct kunit *test)
187 {
188 for (int i = 0; i < iwl_dev_info_table_size; i++) {
189 const struct iwl_dev_info *di = &iwl_dev_info_table[i];
190
191 if (!strstr(di->name, "Killer"))
192 continue;
193
194 KUNIT_EXPECT_NE(test, di->subdevice, (u16)IWL_CFG_ANY);
195 }
196 }
197
devinfo_pci_ids(struct kunit * test)198 static void devinfo_pci_ids(struct kunit *test)
199 {
200 struct pci_dev *dev;
201
202 dev = kunit_kmalloc(test, sizeof(*dev), GFP_KERNEL);
203 KUNIT_ASSERT_NOT_NULL(test, dev);
204
205 for (int i = 0; iwl_hw_card_ids[i].vendor; i++) {
206 const struct pci_device_id *s, *t;
207
208 s = &iwl_hw_card_ids[i];
209 dev->vendor = s->vendor;
210 dev->device = s->device;
211 dev->subsystem_vendor = s->subvendor;
212 dev->subsystem_device = s->subdevice;
213 dev->class = s->class;
214
215 t = pci_match_id(iwl_hw_card_ids, dev);
216 KUNIT_EXPECT_PTR_EQ(test, t, s);
217 }
218 }
219
devinfo_no_mac_cfg_dups(struct kunit * test)220 static void devinfo_no_mac_cfg_dups(struct kunit *test)
221 {
222 for (int i = 0; iwl_hw_card_ids[i].vendor; i++) {
223 const struct iwl_mac_cfg *cfg_i =
224 (void *)iwl_hw_card_ids[i].driver_data;
225
226 for (int j = 0; j < i; j++) {
227 const struct iwl_mac_cfg *cfg_j =
228 (void *)iwl_hw_card_ids[j].driver_data;
229
230 if (cfg_i == cfg_j)
231 continue;
232
233 KUNIT_EXPECT_NE_MSG(test, memcmp(cfg_j, cfg_i,
234 sizeof(*cfg_i)), 0,
235 "identical configs: %ps and %ps\n",
236 cfg_i, cfg_j);
237 }
238 }
239 }
240
devinfo_api_range(struct kunit * test)241 static void devinfo_api_range(struct kunit *test)
242 {
243 /* Check that all iwl_mac_cfg's have either both min and max set, or neither */
244 for (int i = 0; iwl_hw_card_ids[i].vendor; i++) {
245 const struct iwl_mac_cfg *mac_cfg =
246 (void *)iwl_hw_card_ids[i].driver_data;
247 const struct iwl_family_base_params *base = mac_cfg->base;
248
249 KUNIT_EXPECT_EQ_MSG(test, !!base->ucode_api_min,
250 !!base->ucode_api_max,
251 "%ps: ucode_api_min (%u) and ucode_api_min (%u) should be both set or neither.\n",
252 base, base->ucode_api_min,
253 base->ucode_api_max);
254 }
255
256 /* Check the same for the iwl_rf_cfg's */
257 for (int i = 0; i < iwl_dev_info_table_size; i++) {
258 const struct iwl_rf_cfg *rf_cfg = iwl_dev_info_table[i].cfg;
259
260 KUNIT_EXPECT_EQ_MSG(test, !!rf_cfg->ucode_api_min,
261 !!rf_cfg->ucode_api_max,
262 "%ps: ucode_api_min (%u) and ucode_api_min (%u) should be both set or neither.\n",
263 rf_cfg, rf_cfg->ucode_api_min,
264 rf_cfg->ucode_api_max);
265 }
266 }
267
268 static struct kunit_case devinfo_test_cases[] = {
269 KUNIT_CASE(devinfo_table_order),
270 KUNIT_CASE(devinfo_discrete_match),
271 KUNIT_CASE(devinfo_names),
272 KUNIT_CASE(devinfo_no_cfg_dups),
273 KUNIT_CASE(devinfo_no_name_dups),
274 KUNIT_CASE(devinfo_check_subdev_match),
275 KUNIT_CASE(devinfo_check_killer_subdev),
276 KUNIT_CASE(devinfo_pci_ids),
277 KUNIT_CASE(devinfo_no_mac_cfg_dups),
278 KUNIT_CASE(devinfo_api_range),
279 {}
280 };
281
282 static struct kunit_suite iwlwifi_devinfo = {
283 .name = "iwlwifi-devinfo",
284 .test_cases = devinfo_test_cases,
285 };
286
287 kunit_test_suite(iwlwifi_devinfo);
288