xref: /linux/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c (revision 2d7eaeb05ae265bd64270376e6a2c0d80244771f)
1 // SPDX-License-Identifier: ISC
2 /*
3  * Copyright (c) 2014 Broadcom Corporation
4  */
5 
6 #include <linux/netdevice.h>
7 #include <linux/module.h>
8 
9 #include <brcm_hw_ids.h>
10 #include <brcmu_wifi.h>
11 #include "core.h"
12 #include "bus.h"
13 #include "debug.h"
14 #include "fwil.h"
15 #include "fwil_types.h"
16 #include "fwvid.h"
17 #include "feature.h"
18 #include "common.h"
19 
20 #define BRCMF_FW_UNSUPPORTED	23
21 
22 /*
23  * expand feature list to array of feature strings.
24  */
25 #define BRCMF_FEAT_DEF(_f) \
26 	#_f,
27 static const char *brcmf_feat_names[] = {
28 	BRCMF_FEAT_LIST
29 };
30 #undef BRCMF_FEAT_DEF
31 
32 struct brcmf_feat_fwcap {
33 	enum brcmf_feat_id feature;
34 	const char * const fwcap_id;
35 };
36 
37 static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = {
38 	{ BRCMF_FEAT_MBSS, "mbss" },
39 	{ BRCMF_FEAT_MCHAN, "mchan" },
40 	{ BRCMF_FEAT_P2P, "p2p" },
41 	{ BRCMF_FEAT_MONITOR, "monitor" },
42 	{ BRCMF_FEAT_MONITOR_FLAG, "rtap" },
43 	{ BRCMF_FEAT_MONITOR_FMT_RADIOTAP, "rtap" },
44 	{ BRCMF_FEAT_DOT11H, "802.11h" },
45 	{ BRCMF_FEAT_SAE, "sae " },
46 	{ BRCMF_FEAT_FWAUTH, "idauth" },
47 	{ BRCMF_FEAT_SAE_EXT, "sae_ext" },
48 	{ BRCMF_FEAT_FBT, "fbt " },
49 	{ BRCMF_FEAT_OKC, "okc" },
50 };
51 
52 #ifdef DEBUG
53 /*
54  * expand quirk list to array of quirk strings.
55  */
56 #define BRCMF_QUIRK_DEF(_q) \
57 	#_q,
58 static const char * const brcmf_quirk_names[] = {
59 	BRCMF_QUIRK_LIST
60 };
61 #undef BRCMF_QUIRK_DEF
62 
63 /**
64  * brcmf_feat_debugfs_read() - expose feature info to debugfs.
65  *
66  * @seq: sequence for debugfs entry.
67  * @data: raw data pointer.
68  */
69 static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
70 {
71 	struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
72 	u32 feats = bus_if->drvr->feat_flags;
73 	u32 quirks = bus_if->drvr->chip_quirks;
74 	int id;
75 
76 	seq_printf(seq, "Features: %08x\n", feats);
77 	for (id = 0; id < BRCMF_FEAT_LAST; id++)
78 		if (feats & BIT(id))
79 			seq_printf(seq, "\t%s\n", brcmf_feat_names[id]);
80 	seq_printf(seq, "\nQuirks:   %08x\n", quirks);
81 	for (id = 0; id < BRCMF_FEAT_QUIRK_LAST; id++)
82 		if (quirks & BIT(id))
83 			seq_printf(seq, "\t%s\n", brcmf_quirk_names[id]);
84 	return 0;
85 }
86 #else
87 static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
88 {
89 	return 0;
90 }
91 #endif /* DEBUG */
92 
93 struct brcmf_feat_fwfeat {
94 	const char * const fwid;
95 	u32 feat_flags;
96 };
97 
98 static const struct brcmf_feat_fwfeat brcmf_feat_fwfeat_map[] = {
99 	/* brcmfmac43602-pcie.ap.bin from linux-firmware.git commit ea1178515b88 */
100 	{ "01-6cb8e269", BIT(BRCMF_FEAT_MONITOR) },
101 	/* brcmfmac4366b-pcie.bin from linux-firmware.git commit 52442afee990 */
102 	{ "01-c47a91a4", BIT(BRCMF_FEAT_MONITOR) },
103 	/* brcmfmac4366b-pcie.bin from linux-firmware.git commit 211de1679a68 */
104 	{ "01-801fb449", BIT(BRCMF_FEAT_MONITOR_FMT_HW_RX_HDR) },
105 	/* brcmfmac4366c-pcie.bin from linux-firmware.git commit 211de1679a68 */
106 	{ "01-d2cbb8fd", BIT(BRCMF_FEAT_MONITOR_FMT_HW_RX_HDR) },
107 };
108 
109 static void brcmf_feat_firmware_overrides(struct brcmf_pub *drv)
110 {
111 	const struct brcmf_feat_fwfeat *e;
112 	u32 feat_flags = 0;
113 	int i;
114 
115 	for (i = 0; i < ARRAY_SIZE(brcmf_feat_fwfeat_map); i++) {
116 		e = &brcmf_feat_fwfeat_map[i];
117 		if (!strcmp(e->fwid, drv->fwver)) {
118 			feat_flags = e->feat_flags;
119 			break;
120 		}
121 	}
122 
123 	if (!feat_flags)
124 		return;
125 
126 	for (i = 0; i < BRCMF_FEAT_LAST; i++)
127 		if (feat_flags & BIT(i))
128 			brcmf_dbg(INFO, "enabling firmware feature: %s\n",
129 				  brcmf_feat_names[i]);
130 	drv->feat_flags |= feat_flags;
131 }
132 
133 struct brcmf_feat_wlcfeat {
134 	u16 min_ver_major;
135 	u16 min_ver_minor;
136 	u32 feat_flags;
137 };
138 
139 static const struct brcmf_feat_wlcfeat brcmf_feat_wlcfeat_map[] = {
140 	{ 12, 0, BIT(BRCMF_FEAT_PMKID_V2) },
141 	{ 13, 0, BIT(BRCMF_FEAT_PMKID_V3) },
142 };
143 
144 static void brcmf_feat_wlc_version_overrides(struct brcmf_pub *drv)
145 {
146 	struct brcmf_if *ifp = brcmf_get_ifp(drv, 0);
147 	const struct brcmf_feat_wlcfeat *e;
148 	struct brcmf_wlc_version_le ver;
149 	u32 feat_flags = 0;
150 	int i, err, major, minor;
151 
152 	err = brcmf_fil_iovar_data_get(ifp, "wlc_ver", &ver, sizeof(ver));
153 	if (err)
154 		return;
155 
156 	major = le16_to_cpu(ver.wlc_ver_major);
157 	minor = le16_to_cpu(ver.wlc_ver_minor);
158 
159 	brcmf_dbg(INFO, "WLC version: %d.%d\n", major, minor);
160 
161 	for (i = 0; i < ARRAY_SIZE(brcmf_feat_wlcfeat_map); i++) {
162 		e = &brcmf_feat_wlcfeat_map[i];
163 		if (major > e->min_ver_major ||
164 		    (major == e->min_ver_major &&
165 		     minor >= e->min_ver_minor)) {
166 			feat_flags |= e->feat_flags;
167 		}
168 	}
169 
170 	if (!feat_flags)
171 		return;
172 
173 	for (i = 0; i < BRCMF_FEAT_LAST; i++)
174 		if (feat_flags & BIT(i))
175 			brcmf_dbg(INFO, "enabling firmware feature: %s\n",
176 				  brcmf_feat_names[i]);
177 	drv->feat_flags |= feat_flags;
178 }
179 
180 /**
181  * brcmf_feat_iovar_int_get() - determine feature through iovar query.
182  *
183  * @ifp: interface to query.
184  * @id: feature id.
185  * @name: iovar name.
186  */
187 static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp,
188 				     enum brcmf_feat_id id, char *name)
189 {
190 	u32 data;
191 	int err;
192 
193 	/* we need to know firmware error */
194 	ifp->fwil_fwerr = true;
195 
196 	err = brcmf_fil_iovar_int_get(ifp, name, &data);
197 	if (err != -BRCMF_FW_UNSUPPORTED) {
198 		brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
199 		ifp->drvr->feat_flags |= BIT(id);
200 	} else {
201 		brcmf_dbg(TRACE, "%s feature check failed: %d\n",
202 			  brcmf_feat_names[id], err);
203 	}
204 
205 	ifp->fwil_fwerr = false;
206 }
207 
208 static void brcmf_feat_iovar_data_set(struct brcmf_if *ifp,
209 				      enum brcmf_feat_id id, char *name,
210 				      const void *data, size_t len)
211 {
212 	int err;
213 
214 	/* we need to know firmware error */
215 	ifp->fwil_fwerr = true;
216 
217 	err = brcmf_fil_iovar_data_set(ifp, name, data, len);
218 	if (err != -BRCMF_FW_UNSUPPORTED) {
219 		brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
220 		ifp->drvr->feat_flags |= BIT(id);
221 	} else {
222 		brcmf_dbg(TRACE, "%s feature check failed: %d\n",
223 			  brcmf_feat_names[id], err);
224 	}
225 
226 	ifp->fwil_fwerr = false;
227 }
228 
229 #define MAX_CAPS_BUFFER_SIZE	768
230 static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp)
231 {
232 	struct brcmf_pub *drvr = ifp->drvr;
233 	char caps[MAX_CAPS_BUFFER_SIZE];
234 	enum brcmf_feat_id id;
235 	int i, err;
236 
237 	err = brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps));
238 	if (err) {
239 		bphy_err(drvr, "could not get firmware cap (%d)\n", err);
240 		return;
241 	}
242 
243 	brcmf_dbg(INFO, "[ %s]\n", caps);
244 
245 	for (i = 0; i < ARRAY_SIZE(brcmf_fwcap_map); i++) {
246 		if (strnstr(caps, brcmf_fwcap_map[i].fwcap_id, sizeof(caps))) {
247 			id = brcmf_fwcap_map[i].feature;
248 			brcmf_dbg(INFO, "enabling feature: %s\n",
249 				  brcmf_feat_names[id]);
250 			ifp->drvr->feat_flags |= BIT(id);
251 		}
252 	}
253 }
254 
255 /**
256  * brcmf_feat_fwcap_debugfs_read() - expose firmware capabilities to debugfs.
257  *
258  * @seq: sequence for debugfs entry.
259  * @data: raw data pointer.
260  */
261 static int brcmf_feat_fwcap_debugfs_read(struct seq_file *seq, void *data)
262 {
263 	struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
264 	struct brcmf_pub *drvr = bus_if->drvr;
265 	struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
266 	char caps[MAX_CAPS_BUFFER_SIZE + 1] = { };
267 	char *tmp;
268 	int err;
269 
270 	err = brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps));
271 	if (err) {
272 		bphy_err(drvr, "could not get firmware cap (%d)\n", err);
273 		return err;
274 	}
275 
276 	/* Put every capability in a new line */
277 	for (tmp = caps; *tmp; tmp++) {
278 		if (*tmp == ' ')
279 			*tmp = '\n';
280 	}
281 
282 	/* Usually there is a space at the end of capabilities string */
283 	seq_printf(seq, "%s", caps);
284 	/* So make sure we don't print two line breaks */
285 	if (tmp > caps && *(tmp - 1) != '\n')
286 		seq_printf(seq, "\n");
287 
288 	return 0;
289 }
290 
291 void brcmf_feat_attach(struct brcmf_pub *drvr)
292 {
293 	struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
294 	struct brcmf_pno_macaddr_le pfn_mac;
295 	struct brcmf_gscan_config gscan_cfg;
296 	u32 wowl_cap;
297 	s32 err;
298 
299 	brcmf_feat_firmware_capabilities(ifp);
300 	memset(&gscan_cfg, 0, sizeof(gscan_cfg));
301 	if (drvr->bus_if->chip != BRCM_CC_43430_CHIP_ID &&
302 	    drvr->bus_if->chip != BRCM_CC_4345_CHIP_ID &&
303 	    drvr->bus_if->chip != BRCM_CC_43454_CHIP_ID &&
304 	    drvr->bus_if->chip != CY_CC_43439_CHIP_ID)
305 		brcmf_feat_iovar_data_set(ifp, BRCMF_FEAT_GSCAN,
306 					  "pfn_gscan_cfg",
307 					  &gscan_cfg, sizeof(gscan_cfg));
308 	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn");
309 	if (drvr->bus_if->wowl_supported)
310 		brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
311 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) {
312 		err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap);
313 		if (!err) {
314 			ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_WOWL_ARP_ND);
315 			if (wowl_cap & BRCMF_WOWL_PFN_FOUND)
316 				ifp->drvr->feat_flags |=
317 					BIT(BRCMF_FEAT_WOWL_ND);
318 			if (wowl_cap & BRCMF_WOWL_GTK_FAILURE)
319 				ifp->drvr->feat_flags |=
320 					BIT(BRCMF_FEAT_WOWL_GTK);
321 		}
322 	}
323 	/* MBSS does not work for all chips */
324 	switch (drvr->bus_if->chip) {
325 	case BRCM_CC_4330_CHIP_ID:
326 	case BRCM_CC_43362_CHIP_ID:
327 		ifp->drvr->feat_flags &= ~BIT(BRCMF_FEAT_MBSS);
328 		break;
329 	default:
330 		break;
331 	}
332 	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode");
333 	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable");
334 	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MFP, "mfp");
335 	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_DUMP_OBSS, "dump_obss");
336 
337 	pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
338 	err = brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", &pfn_mac,
339 				       sizeof(pfn_mac));
340 	if (!err)
341 		ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_SCAN_RANDOM_MAC);
342 
343 	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_FWSUP, "sup_wpa");
344 	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_SCAN_V2, "scan_ver");
345 
346 	brcmf_feat_wlc_version_overrides(drvr);
347 	brcmf_feat_firmware_overrides(drvr);
348 
349 	brcmf_fwvid_feat_attach(ifp);
350 
351 	if (drvr->settings->feature_disable) {
352 		brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n",
353 			  ifp->drvr->feat_flags,
354 			  drvr->settings->feature_disable);
355 		ifp->drvr->feat_flags &= ~drvr->settings->feature_disable;
356 	}
357 
358 	/* set chip related quirks */
359 	switch (drvr->bus_if->chip) {
360 	case BRCM_CC_43236_CHIP_ID:
361 		drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_AUTO_AUTH);
362 		break;
363 	case BRCM_CC_4329_CHIP_ID:
364 		drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_NEED_MPC);
365 		break;
366 	default:
367 		/* no quirks */
368 		break;
369 	}
370 }
371 
372 void brcmf_feat_debugfs_create(struct brcmf_pub *drvr)
373 {
374 	brcmf_debugfs_add_entry(drvr, "features", brcmf_feat_debugfs_read);
375 	brcmf_debugfs_add_entry(drvr, "fwcap", brcmf_feat_fwcap_debugfs_read);
376 }
377 
378 bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id)
379 {
380 	return (ifp->drvr->feat_flags & BIT(id));
381 }
382 
383 bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp,
384 				 enum brcmf_feat_quirk quirk)
385 {
386 	return (ifp->drvr->chip_quirks & BIT(quirk));
387 }
388