Linux Audio

Check our new training course

Open-source upstreaming

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