File Index
Files and directories
Sign in
FILE

feature.c

File preview with syntax highlighting.

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