xref: /openbmc/linux/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c (revision 34d6f206a88c2651d216bd3487ac956a40b2ba8e)
1daeccac2SArend van Spriel // SPDX-License-Identifier: ISC
205491d2cSKalle Valo /*
305491d2cSKalle Valo  * Copyright (c) 2012 Broadcom Corporation
405491d2cSKalle Valo  */
505491d2cSKalle Valo 
605491d2cSKalle Valo /* FWIL is the Firmware Interface Layer. In this module the support functions
705491d2cSKalle Valo  * are located to set and get variables to and from the firmware.
805491d2cSKalle Valo  */
905491d2cSKalle Valo 
1005491d2cSKalle Valo #include <linux/kernel.h>
1105491d2cSKalle Valo #include <linux/netdevice.h>
1205491d2cSKalle Valo #include <brcmu_utils.h>
1305491d2cSKalle Valo #include <brcmu_wifi.h>
1405491d2cSKalle Valo #include "core.h"
1505491d2cSKalle Valo #include "bus.h"
1605491d2cSKalle Valo #include "debug.h"
1705491d2cSKalle Valo #include "tracepoint.h"
18a7dd0ac9SArend van Spriel #include "xtlv.h"
1905491d2cSKalle Valo #include "fwil.h"
2005491d2cSKalle Valo #include "proto.h"
2105491d2cSKalle Valo 
2205491d2cSKalle Valo 
2305491d2cSKalle Valo #define MAX_HEX_DUMP_LEN	64
2405491d2cSKalle Valo 
2505491d2cSKalle Valo #ifdef DEBUG
2605491d2cSKalle Valo static const char * const brcmf_fil_errstr[] = {
2705491d2cSKalle Valo 	"BCME_OK",
2805491d2cSKalle Valo 	"BCME_ERROR",
2905491d2cSKalle Valo 	"BCME_BADARG",
3005491d2cSKalle Valo 	"BCME_BADOPTION",
3105491d2cSKalle Valo 	"BCME_NOTUP",
3205491d2cSKalle Valo 	"BCME_NOTDOWN",
3305491d2cSKalle Valo 	"BCME_NOTAP",
3405491d2cSKalle Valo 	"BCME_NOTSTA",
3505491d2cSKalle Valo 	"BCME_BADKEYIDX",
3605491d2cSKalle Valo 	"BCME_RADIOOFF",
3705491d2cSKalle Valo 	"BCME_NOTBANDLOCKED",
3805491d2cSKalle Valo 	"BCME_NOCLK",
3905491d2cSKalle Valo 	"BCME_BADRATESET",
4005491d2cSKalle Valo 	"BCME_BADBAND",
4105491d2cSKalle Valo 	"BCME_BUFTOOSHORT",
4205491d2cSKalle Valo 	"BCME_BUFTOOLONG",
4305491d2cSKalle Valo 	"BCME_BUSY",
4405491d2cSKalle Valo 	"BCME_NOTASSOCIATED",
4505491d2cSKalle Valo 	"BCME_BADSSIDLEN",
4605491d2cSKalle Valo 	"BCME_OUTOFRANGECHAN",
4705491d2cSKalle Valo 	"BCME_BADCHAN",
4805491d2cSKalle Valo 	"BCME_BADADDR",
4905491d2cSKalle Valo 	"BCME_NORESOURCE",
5005491d2cSKalle Valo 	"BCME_UNSUPPORTED",
5105491d2cSKalle Valo 	"BCME_BADLEN",
5205491d2cSKalle Valo 	"BCME_NOTREADY",
5305491d2cSKalle Valo 	"BCME_EPERM",
5405491d2cSKalle Valo 	"BCME_NOMEM",
5505491d2cSKalle Valo 	"BCME_ASSOCIATED",
5605491d2cSKalle Valo 	"BCME_RANGE",
5705491d2cSKalle Valo 	"BCME_NOTFOUND",
5805491d2cSKalle Valo 	"BCME_WME_NOT_ENABLED",
5905491d2cSKalle Valo 	"BCME_TSPEC_NOTFOUND",
6005491d2cSKalle Valo 	"BCME_ACM_NOTSUPPORTED",
6105491d2cSKalle Valo 	"BCME_NOT_WME_ASSOCIATION",
6205491d2cSKalle Valo 	"BCME_SDIO_ERROR",
6305491d2cSKalle Valo 	"BCME_DONGLE_DOWN",
6405491d2cSKalle Valo 	"BCME_VERSION",
6505491d2cSKalle Valo 	"BCME_TXFAIL",
6605491d2cSKalle Valo 	"BCME_RXFAIL",
6705491d2cSKalle Valo 	"BCME_NODEVICE",
6805491d2cSKalle Valo 	"BCME_NMODE_DISABLED",
6905491d2cSKalle Valo 	"BCME_NONRESIDENT",
7005491d2cSKalle Valo 	"BCME_SCANREJECT",
7105491d2cSKalle Valo 	"BCME_USAGE_ERROR",
7205491d2cSKalle Valo 	"BCME_IOCTL_ERROR",
7305491d2cSKalle Valo 	"BCME_SERIAL_PORT_ERR",
7405491d2cSKalle Valo 	"BCME_DISABLED",
7505491d2cSKalle Valo 	"BCME_DECERR",
7605491d2cSKalle Valo 	"BCME_ENCERR",
7705491d2cSKalle Valo 	"BCME_MICERR",
7805491d2cSKalle Valo 	"BCME_REPLAY",
7905491d2cSKalle Valo 	"BCME_IE_NOTFOUND",
8005491d2cSKalle Valo };
8105491d2cSKalle Valo 
brcmf_fil_get_errstr(u32 err)8205491d2cSKalle Valo static const char *brcmf_fil_get_errstr(u32 err)
8305491d2cSKalle Valo {
8405491d2cSKalle Valo 	if (err >= ARRAY_SIZE(brcmf_fil_errstr))
8505491d2cSKalle Valo 		return "(unknown)";
8605491d2cSKalle Valo 
8705491d2cSKalle Valo 	return brcmf_fil_errstr[err];
8805491d2cSKalle Valo }
8905491d2cSKalle Valo #else
brcmf_fil_get_errstr(u32 err)9005491d2cSKalle Valo static const char *brcmf_fil_get_errstr(u32 err)
9105491d2cSKalle Valo {
9205491d2cSKalle Valo 	return "";
9305491d2cSKalle Valo }
9405491d2cSKalle Valo #endif /* DEBUG */
9505491d2cSKalle Valo 
9605491d2cSKalle Valo static s32
brcmf_fil_cmd_data(struct brcmf_if * ifp,u32 cmd,void * data,u32 len,bool set)9705491d2cSKalle Valo brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
9805491d2cSKalle Valo {
9905491d2cSKalle Valo 	struct brcmf_pub *drvr = ifp->drvr;
100b69c1df4SArend Van Spriel 	s32 err, fwerr;
10105491d2cSKalle Valo 
10205491d2cSKalle Valo 	if (drvr->bus_if->state != BRCMF_BUS_UP) {
103dcb1471bSRafał Miłecki 		bphy_err(drvr, "bus is down. we have nothing to do.\n");
10405491d2cSKalle Valo 		return -EIO;
10505491d2cSKalle Valo 	}
10605491d2cSKalle Valo 
10705491d2cSKalle Valo 	if (data != NULL)
10805491d2cSKalle Valo 		len = min_t(uint, len, BRCMF_DCMD_MAXLEN);
10905491d2cSKalle Valo 	if (set)
110b69c1df4SArend Van Spriel 		err = brcmf_proto_set_dcmd(drvr, ifp->ifidx, cmd,
111b69c1df4SArend Van Spriel 					   data, len, &fwerr);
11205491d2cSKalle Valo 	else
113b69c1df4SArend Van Spriel 		err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd,
114b69c1df4SArend Van Spriel 					     data, len, &fwerr);
11505491d2cSKalle Valo 
116b69c1df4SArend Van Spriel 	if (err) {
1171170f6d1SArend Van Spriel 		brcmf_dbg(FIL, "Failed: error=%d\n", err);
118b69c1df4SArend Van Spriel 	} else if (fwerr < 0) {
119b69c1df4SArend Van Spriel 		brcmf_dbg(FIL, "Firmware error: %s (%d)\n",
120b69c1df4SArend Van Spriel 			  brcmf_fil_get_errstr((u32)(-fwerr)), fwerr);
121b69c1df4SArend Van Spriel 		err = -EBADE;
122b69c1df4SArend Van Spriel 	}
12393389734SArend Van Spriel 	if (ifp->fwil_fwerr)
12493389734SArend Van Spriel 		return fwerr;
12593389734SArend Van Spriel 
12621000b3fSHante Meuleman 	return err;
12705491d2cSKalle Valo }
12805491d2cSKalle Valo 
12905491d2cSKalle Valo s32
brcmf_fil_cmd_data_set(struct brcmf_if * ifp,u32 cmd,void * data,u32 len)13005491d2cSKalle Valo brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
13105491d2cSKalle Valo {
13205491d2cSKalle Valo 	s32 err;
13305491d2cSKalle Valo 
13405491d2cSKalle Valo 	mutex_lock(&ifp->drvr->proto_block);
13505491d2cSKalle Valo 
13605491d2cSKalle Valo 	brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
13705491d2cSKalle Valo 	brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
13805491d2cSKalle Valo 			   min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
13905491d2cSKalle Valo 
14005491d2cSKalle Valo 	err = brcmf_fil_cmd_data(ifp, cmd, data, len, true);
14105491d2cSKalle Valo 	mutex_unlock(&ifp->drvr->proto_block);
14205491d2cSKalle Valo 
14305491d2cSKalle Valo 	return err;
14405491d2cSKalle Valo }
145*c3cfcf51SArend van Spriel BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_cmd_data_set);
14605491d2cSKalle Valo 
14705491d2cSKalle Valo s32
brcmf_fil_cmd_data_get(struct brcmf_if * ifp,u32 cmd,void * data,u32 len)14805491d2cSKalle Valo brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
14905491d2cSKalle Valo {
15005491d2cSKalle Valo 	s32 err;
15105491d2cSKalle Valo 
15205491d2cSKalle Valo 	mutex_lock(&ifp->drvr->proto_block);
15305491d2cSKalle Valo 	err = brcmf_fil_cmd_data(ifp, cmd, data, len, false);
15405491d2cSKalle Valo 
155a7dd0ac9SArend van Spriel 	brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d, err=%d\n", ifp->ifidx, cmd,
156a7dd0ac9SArend van Spriel 		  len, err);
15705491d2cSKalle Valo 	brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
15805491d2cSKalle Valo 			   min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
15905491d2cSKalle Valo 
16005491d2cSKalle Valo 	mutex_unlock(&ifp->drvr->proto_block);
16105491d2cSKalle Valo 
16205491d2cSKalle Valo 	return err;
16305491d2cSKalle Valo }
164*c3cfcf51SArend van Spriel BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_cmd_data_get);
16505491d2cSKalle Valo 
16605491d2cSKalle Valo static u32
brcmf_create_iovar(const char * name,const char * data,u32 datalen,char * buf,u32 buflen)167e7191182SHector Martin brcmf_create_iovar(const char *name, const char *data, u32 datalen,
16805491d2cSKalle Valo 		   char *buf, u32 buflen)
16905491d2cSKalle Valo {
17005491d2cSKalle Valo 	u32 len;
17105491d2cSKalle Valo 
17205491d2cSKalle Valo 	len = strlen(name) + 1;
17305491d2cSKalle Valo 
17405491d2cSKalle Valo 	if ((len + datalen) > buflen)
17505491d2cSKalle Valo 		return 0;
17605491d2cSKalle Valo 
17705491d2cSKalle Valo 	memcpy(buf, name, len);
17805491d2cSKalle Valo 
17905491d2cSKalle Valo 	/* append data onto the end of the name string */
18005491d2cSKalle Valo 	if (data && datalen)
18105491d2cSKalle Valo 		memcpy(&buf[len], data, datalen);
18205491d2cSKalle Valo 
18305491d2cSKalle Valo 	return len + datalen;
18405491d2cSKalle Valo }
18505491d2cSKalle Valo 
18605491d2cSKalle Valo 
18705491d2cSKalle Valo s32
brcmf_fil_iovar_data_set(struct brcmf_if * ifp,const char * name,const void * data,u32 len)188e7191182SHector Martin brcmf_fil_iovar_data_set(struct brcmf_if *ifp, const char *name, const void *data,
18905491d2cSKalle Valo 			 u32 len)
19005491d2cSKalle Valo {
19105491d2cSKalle Valo 	struct brcmf_pub *drvr = ifp->drvr;
19205491d2cSKalle Valo 	s32 err;
19305491d2cSKalle Valo 	u32 buflen;
19405491d2cSKalle Valo 
19505491d2cSKalle Valo 	mutex_lock(&drvr->proto_block);
19605491d2cSKalle Valo 
19705491d2cSKalle Valo 	brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
19805491d2cSKalle Valo 	brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
19905491d2cSKalle Valo 			   min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
20005491d2cSKalle Valo 
20105491d2cSKalle Valo 	buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
20205491d2cSKalle Valo 				    sizeof(drvr->proto_buf));
20305491d2cSKalle Valo 	if (buflen) {
20405491d2cSKalle Valo 		err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
20505491d2cSKalle Valo 					 buflen, true);
20605491d2cSKalle Valo 	} else {
20705491d2cSKalle Valo 		err = -EPERM;
208dcb1471bSRafał Miłecki 		bphy_err(drvr, "Creating iovar failed\n");
20905491d2cSKalle Valo 	}
21005491d2cSKalle Valo 
21105491d2cSKalle Valo 	mutex_unlock(&drvr->proto_block);
21205491d2cSKalle Valo 	return err;
21305491d2cSKalle Valo }
2148d59a64cSHector Martin BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_iovar_data_set);
21505491d2cSKalle Valo 
21605491d2cSKalle Valo s32
brcmf_fil_iovar_data_get(struct brcmf_if * ifp,const char * name,void * data,u32 len)217e7191182SHector Martin brcmf_fil_iovar_data_get(struct brcmf_if *ifp, const char *name, void *data,
21805491d2cSKalle Valo 			 u32 len)
21905491d2cSKalle Valo {
22005491d2cSKalle Valo 	struct brcmf_pub *drvr = ifp->drvr;
22105491d2cSKalle Valo 	s32 err;
22205491d2cSKalle Valo 	u32 buflen;
22305491d2cSKalle Valo 
22405491d2cSKalle Valo 	mutex_lock(&drvr->proto_block);
22505491d2cSKalle Valo 
22605491d2cSKalle Valo 	buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
22705491d2cSKalle Valo 				    sizeof(drvr->proto_buf));
22805491d2cSKalle Valo 	if (buflen) {
22905491d2cSKalle Valo 		err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
23005491d2cSKalle Valo 					 buflen, false);
23105491d2cSKalle Valo 		if (err == 0)
23205491d2cSKalle Valo 			memcpy(data, drvr->proto_buf, len);
23305491d2cSKalle Valo 	} else {
23405491d2cSKalle Valo 		err = -EPERM;
235dcb1471bSRafał Miłecki 		bphy_err(drvr, "Creating iovar failed\n");
23605491d2cSKalle Valo 	}
23705491d2cSKalle Valo 
238a7dd0ac9SArend van Spriel 	brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d, err=%d\n", ifp->ifidx, name,
239a7dd0ac9SArend van Spriel 		  len, err);
24005491d2cSKalle Valo 	brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
24105491d2cSKalle Valo 			   min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
24205491d2cSKalle Valo 
24305491d2cSKalle Valo 	mutex_unlock(&drvr->proto_block);
24405491d2cSKalle Valo 	return err;
24505491d2cSKalle Valo }
246*c3cfcf51SArend van Spriel BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_iovar_data_get);
24705491d2cSKalle Valo 
24805491d2cSKalle Valo static u32
brcmf_create_bsscfg(s32 bsscfgidx,const char * name,char * data,u32 datalen,char * buf,u32 buflen)249e7191182SHector Martin brcmf_create_bsscfg(s32 bsscfgidx, const char *name, char *data, u32 datalen,
25037a869ecSHante Meuleman 		    char *buf, u32 buflen)
25105491d2cSKalle Valo {
25205491d2cSKalle Valo 	const s8 *prefix = "bsscfg:";
25305491d2cSKalle Valo 	s8 *p;
25405491d2cSKalle Valo 	u32 prefixlen;
25505491d2cSKalle Valo 	u32 namelen;
25605491d2cSKalle Valo 	u32 iolen;
25737a869ecSHante Meuleman 	__le32 bsscfgidx_le;
25805491d2cSKalle Valo 
25937a869ecSHante Meuleman 	if (bsscfgidx == 0)
26005491d2cSKalle Valo 		return brcmf_create_iovar(name, data, datalen, buf, buflen);
26105491d2cSKalle Valo 
26205491d2cSKalle Valo 	prefixlen = strlen(prefix);
263b07e1ae2SWeitao Hou 	namelen = strlen(name) + 1; /* length of iovar  name + null */
26437a869ecSHante Meuleman 	iolen = prefixlen + namelen + sizeof(bsscfgidx_le) + datalen;
26505491d2cSKalle Valo 
26605491d2cSKalle Valo 	if (buflen < iolen) {
26705491d2cSKalle Valo 		brcmf_err("buffer is too short\n");
26805491d2cSKalle Valo 		return 0;
26905491d2cSKalle Valo 	}
27005491d2cSKalle Valo 
27105491d2cSKalle Valo 	p = buf;
27205491d2cSKalle Valo 
27305491d2cSKalle Valo 	/* copy prefix, no null */
27405491d2cSKalle Valo 	memcpy(p, prefix, prefixlen);
27505491d2cSKalle Valo 	p += prefixlen;
27605491d2cSKalle Valo 
27705491d2cSKalle Valo 	/* copy iovar name including null */
27805491d2cSKalle Valo 	memcpy(p, name, namelen);
27905491d2cSKalle Valo 	p += namelen;
28005491d2cSKalle Valo 
28105491d2cSKalle Valo 	/* bss config index as first data */
28237a869ecSHante Meuleman 	bsscfgidx_le = cpu_to_le32(bsscfgidx);
28337a869ecSHante Meuleman 	memcpy(p, &bsscfgidx_le, sizeof(bsscfgidx_le));
28437a869ecSHante Meuleman 	p += sizeof(bsscfgidx_le);
28505491d2cSKalle Valo 
28605491d2cSKalle Valo 	/* parameter buffer follows */
28705491d2cSKalle Valo 	if (datalen)
28805491d2cSKalle Valo 		memcpy(p, data, datalen);
28905491d2cSKalle Valo 
29005491d2cSKalle Valo 	return iolen;
29105491d2cSKalle Valo }
29205491d2cSKalle Valo 
29305491d2cSKalle Valo s32
brcmf_fil_bsscfg_data_set(struct brcmf_if * ifp,const char * name,void * data,u32 len)294e7191182SHector Martin brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, const char *name,
29505491d2cSKalle Valo 			  void *data, u32 len)
29605491d2cSKalle Valo {
29705491d2cSKalle Valo 	struct brcmf_pub *drvr = ifp->drvr;
29805491d2cSKalle Valo 	s32 err;
29905491d2cSKalle Valo 	u32 buflen;
30005491d2cSKalle Valo 
30105491d2cSKalle Valo 	mutex_lock(&drvr->proto_block);
30205491d2cSKalle Valo 
30337a869ecSHante Meuleman 	brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx,
30437a869ecSHante Meuleman 		  ifp->bsscfgidx, name, len);
30505491d2cSKalle Valo 	brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
30605491d2cSKalle Valo 			   min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
30705491d2cSKalle Valo 
30837a869ecSHante Meuleman 	buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,
30905491d2cSKalle Valo 				     drvr->proto_buf, sizeof(drvr->proto_buf));
31005491d2cSKalle Valo 	if (buflen) {
31105491d2cSKalle Valo 		err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
31205491d2cSKalle Valo 					 buflen, true);
31305491d2cSKalle Valo 	} else {
31405491d2cSKalle Valo 		err = -EPERM;
315dcb1471bSRafał Miłecki 		bphy_err(drvr, "Creating bsscfg failed\n");
31605491d2cSKalle Valo 	}
31705491d2cSKalle Valo 
31805491d2cSKalle Valo 	mutex_unlock(&drvr->proto_block);
31905491d2cSKalle Valo 	return err;
32005491d2cSKalle Valo }
321*c3cfcf51SArend van Spriel BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_bsscfg_data_set);
32205491d2cSKalle Valo 
32305491d2cSKalle Valo s32
brcmf_fil_bsscfg_data_get(struct brcmf_if * ifp,const char * name,void * data,u32 len)324e7191182SHector Martin brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, const char *name,
32505491d2cSKalle Valo 			  void *data, u32 len)
32605491d2cSKalle Valo {
32705491d2cSKalle Valo 	struct brcmf_pub *drvr = ifp->drvr;
32805491d2cSKalle Valo 	s32 err;
32905491d2cSKalle Valo 	u32 buflen;
33005491d2cSKalle Valo 
33105491d2cSKalle Valo 	mutex_lock(&drvr->proto_block);
33205491d2cSKalle Valo 
33337a869ecSHante Meuleman 	buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,
33405491d2cSKalle Valo 				     drvr->proto_buf, sizeof(drvr->proto_buf));
33505491d2cSKalle Valo 	if (buflen) {
33605491d2cSKalle Valo 		err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
33705491d2cSKalle Valo 					 buflen, false);
33805491d2cSKalle Valo 		if (err == 0)
33905491d2cSKalle Valo 			memcpy(data, drvr->proto_buf, len);
34005491d2cSKalle Valo 	} else {
34105491d2cSKalle Valo 		err = -EPERM;
342dcb1471bSRafał Miłecki 		bphy_err(drvr, "Creating bsscfg failed\n");
34305491d2cSKalle Valo 	}
344a7dd0ac9SArend van Spriel 	brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d, err=%d\n",
345a7dd0ac9SArend van Spriel 		  ifp->ifidx, ifp->bsscfgidx, name, len, err);
34605491d2cSKalle Valo 	brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
34705491d2cSKalle Valo 			   min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
34805491d2cSKalle Valo 
34905491d2cSKalle Valo 	mutex_unlock(&drvr->proto_block);
35005491d2cSKalle Valo 	return err;
35105491d2cSKalle Valo }
352*c3cfcf51SArend van Spriel BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_bsscfg_data_get);
353a7dd0ac9SArend van Spriel 
brcmf_create_xtlv(const char * name,u16 id,char * data,u32 len,char * buf,u32 buflen)354e7191182SHector Martin static u32 brcmf_create_xtlv(const char *name, u16 id, char *data, u32 len,
355a7dd0ac9SArend van Spriel 			     char *buf, u32 buflen)
356a7dd0ac9SArend van Spriel {
357a7dd0ac9SArend van Spriel 	u32 iolen;
358a7dd0ac9SArend van Spriel 	u32 nmlen;
359a7dd0ac9SArend van Spriel 
360a7dd0ac9SArend van Spriel 	nmlen = strlen(name) + 1;
361a7dd0ac9SArend van Spriel 	iolen = nmlen + brcmf_xtlv_data_size(len, BRCMF_XTLV_OPTION_ALIGN32);
362a7dd0ac9SArend van Spriel 
363a7dd0ac9SArend van Spriel 	if (iolen > buflen) {
364a7dd0ac9SArend van Spriel 		brcmf_err("buffer is too short\n");
365a7dd0ac9SArend van Spriel 		return 0;
366a7dd0ac9SArend van Spriel 	}
367a7dd0ac9SArend van Spriel 
368a7dd0ac9SArend van Spriel 	memcpy(buf, name, nmlen);
369a7dd0ac9SArend van Spriel 	brcmf_xtlv_pack_header((void *)(buf + nmlen), id, len, data,
370a7dd0ac9SArend van Spriel 			       BRCMF_XTLV_OPTION_ALIGN32);
371a7dd0ac9SArend van Spriel 
372a7dd0ac9SArend van Spriel 	return iolen;
373a7dd0ac9SArend van Spriel }
374a7dd0ac9SArend van Spriel 
brcmf_fil_xtlv_data_set(struct brcmf_if * ifp,const char * name,u16 id,void * data,u32 len)375e7191182SHector Martin s32 brcmf_fil_xtlv_data_set(struct brcmf_if *ifp, const char *name, u16 id,
376a7dd0ac9SArend van Spriel 			    void *data, u32 len)
377a7dd0ac9SArend van Spriel {
378a7dd0ac9SArend van Spriel 	struct brcmf_pub *drvr = ifp->drvr;
379a7dd0ac9SArend van Spriel 	s32 err;
380a7dd0ac9SArend van Spriel 	u32 buflen;
381a7dd0ac9SArend van Spriel 
382a7dd0ac9SArend van Spriel 	mutex_lock(&drvr->proto_block);
383a7dd0ac9SArend van Spriel 
384a7dd0ac9SArend van Spriel 	brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u\n", ifp->ifidx, name,
385a7dd0ac9SArend van Spriel 		  id, len);
386a7dd0ac9SArend van Spriel 	brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
387a7dd0ac9SArend van Spriel 			   min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
388a7dd0ac9SArend van Spriel 
389a7dd0ac9SArend van Spriel 	buflen = brcmf_create_xtlv(name, id, data, len,
390a7dd0ac9SArend van Spriel 				   drvr->proto_buf, sizeof(drvr->proto_buf));
391a7dd0ac9SArend van Spriel 	if (buflen) {
392a7dd0ac9SArend van Spriel 		err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
393a7dd0ac9SArend van Spriel 					 buflen, true);
394a7dd0ac9SArend van Spriel 	} else {
395a7dd0ac9SArend van Spriel 		err = -EPERM;
396a7dd0ac9SArend van Spriel 		bphy_err(drvr, "Creating xtlv failed\n");
397a7dd0ac9SArend van Spriel 	}
398a7dd0ac9SArend van Spriel 
399a7dd0ac9SArend van Spriel 	mutex_unlock(&drvr->proto_block);
400a7dd0ac9SArend van Spriel 	return err;
401a7dd0ac9SArend van Spriel }
402*c3cfcf51SArend van Spriel BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_xtlv_data_set);
403a7dd0ac9SArend van Spriel 
brcmf_fil_xtlv_data_get(struct brcmf_if * ifp,const char * name,u16 id,void * data,u32 len)404e7191182SHector Martin s32 brcmf_fil_xtlv_data_get(struct brcmf_if *ifp, const char *name, u16 id,
405a7dd0ac9SArend van Spriel 			    void *data, u32 len)
406a7dd0ac9SArend van Spriel {
407a7dd0ac9SArend van Spriel 	struct brcmf_pub *drvr = ifp->drvr;
408a7dd0ac9SArend van Spriel 	s32 err;
409a7dd0ac9SArend van Spriel 	u32 buflen;
410a7dd0ac9SArend van Spriel 
411a7dd0ac9SArend van Spriel 	mutex_lock(&drvr->proto_block);
412a7dd0ac9SArend van Spriel 
413a7dd0ac9SArend van Spriel 	buflen = brcmf_create_xtlv(name, id, data, len,
414a7dd0ac9SArend van Spriel 				   drvr->proto_buf, sizeof(drvr->proto_buf));
415a7dd0ac9SArend van Spriel 	if (buflen) {
416a7dd0ac9SArend van Spriel 		err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
417a7dd0ac9SArend van Spriel 					 buflen, false);
418a7dd0ac9SArend van Spriel 		if (err == 0)
419a7dd0ac9SArend van Spriel 			memcpy(data, drvr->proto_buf, len);
420a7dd0ac9SArend van Spriel 	} else {
421a7dd0ac9SArend van Spriel 		err = -EPERM;
422a7dd0ac9SArend van Spriel 		bphy_err(drvr, "Creating bsscfg failed\n");
423a7dd0ac9SArend van Spriel 	}
424a7dd0ac9SArend van Spriel 	brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u, err=%d\n",
425a7dd0ac9SArend van Spriel 		  ifp->ifidx, name, id, len, err);
426a7dd0ac9SArend van Spriel 	brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
427a7dd0ac9SArend van Spriel 			   min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
428a7dd0ac9SArend van Spriel 
429a7dd0ac9SArend van Spriel 	mutex_unlock(&drvr->proto_block);
430a7dd0ac9SArend van Spriel 	return err;
431a7dd0ac9SArend van Spriel }
432*c3cfcf51SArend van Spriel BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_xtlv_data_get);
433