xref: /freebsd/sys/contrib/dev/broadcom/brcm80211/brcmfmac/fwil.c (revision 902136e0fe112383ec64d2ef43a446063b5e6417)
1b4c3e9b5SBjoern A. Zeeb // SPDX-License-Identifier: ISC
2b4c3e9b5SBjoern A. Zeeb /*
3b4c3e9b5SBjoern A. Zeeb  * Copyright (c) 2012 Broadcom Corporation
4b4c3e9b5SBjoern A. Zeeb  */
5b4c3e9b5SBjoern A. Zeeb 
6b4c3e9b5SBjoern A. Zeeb /* FWIL is the Firmware Interface Layer. In this module the support functions
7b4c3e9b5SBjoern A. Zeeb  * are located to set and get variables to and from the firmware.
8b4c3e9b5SBjoern A. Zeeb  */
9b4c3e9b5SBjoern A. Zeeb 
10b4c3e9b5SBjoern A. Zeeb #include <linux/kernel.h>
11b4c3e9b5SBjoern A. Zeeb #include <linux/netdevice.h>
12b4c3e9b5SBjoern A. Zeeb #include <brcmu_utils.h>
13b4c3e9b5SBjoern A. Zeeb #include <brcmu_wifi.h>
14b4c3e9b5SBjoern A. Zeeb #include "core.h"
15b4c3e9b5SBjoern A. Zeeb #include "bus.h"
16b4c3e9b5SBjoern A. Zeeb #include "debug.h"
17b4c3e9b5SBjoern A. Zeeb #include "tracepoint.h"
18b4c3e9b5SBjoern A. Zeeb #include "xtlv.h"
19b4c3e9b5SBjoern A. Zeeb #include "fwil.h"
20b4c3e9b5SBjoern A. Zeeb #include "proto.h"
21b4c3e9b5SBjoern A. Zeeb 
22b4c3e9b5SBjoern A. Zeeb 
23b4c3e9b5SBjoern A. Zeeb #define MAX_HEX_DUMP_LEN	64
24b4c3e9b5SBjoern A. Zeeb 
25b4c3e9b5SBjoern A. Zeeb #ifdef DEBUG
26b4c3e9b5SBjoern A. Zeeb static const char * const brcmf_fil_errstr[] = {
27b4c3e9b5SBjoern A. Zeeb 	"BCME_OK",
28b4c3e9b5SBjoern A. Zeeb 	"BCME_ERROR",
29b4c3e9b5SBjoern A. Zeeb 	"BCME_BADARG",
30b4c3e9b5SBjoern A. Zeeb 	"BCME_BADOPTION",
31b4c3e9b5SBjoern A. Zeeb 	"BCME_NOTUP",
32b4c3e9b5SBjoern A. Zeeb 	"BCME_NOTDOWN",
33b4c3e9b5SBjoern A. Zeeb 	"BCME_NOTAP",
34b4c3e9b5SBjoern A. Zeeb 	"BCME_NOTSTA",
35b4c3e9b5SBjoern A. Zeeb 	"BCME_BADKEYIDX",
36b4c3e9b5SBjoern A. Zeeb 	"BCME_RADIOOFF",
37b4c3e9b5SBjoern A. Zeeb 	"BCME_NOTBANDLOCKED",
38b4c3e9b5SBjoern A. Zeeb 	"BCME_NOCLK",
39b4c3e9b5SBjoern A. Zeeb 	"BCME_BADRATESET",
40b4c3e9b5SBjoern A. Zeeb 	"BCME_BADBAND",
41b4c3e9b5SBjoern A. Zeeb 	"BCME_BUFTOOSHORT",
42b4c3e9b5SBjoern A. Zeeb 	"BCME_BUFTOOLONG",
43b4c3e9b5SBjoern A. Zeeb 	"BCME_BUSY",
44b4c3e9b5SBjoern A. Zeeb 	"BCME_NOTASSOCIATED",
45b4c3e9b5SBjoern A. Zeeb 	"BCME_BADSSIDLEN",
46b4c3e9b5SBjoern A. Zeeb 	"BCME_OUTOFRANGECHAN",
47b4c3e9b5SBjoern A. Zeeb 	"BCME_BADCHAN",
48b4c3e9b5SBjoern A. Zeeb 	"BCME_BADADDR",
49b4c3e9b5SBjoern A. Zeeb 	"BCME_NORESOURCE",
50b4c3e9b5SBjoern A. Zeeb 	"BCME_UNSUPPORTED",
51b4c3e9b5SBjoern A. Zeeb 	"BCME_BADLEN",
52b4c3e9b5SBjoern A. Zeeb 	"BCME_NOTREADY",
53b4c3e9b5SBjoern A. Zeeb 	"BCME_EPERM",
54b4c3e9b5SBjoern A. Zeeb 	"BCME_NOMEM",
55b4c3e9b5SBjoern A. Zeeb 	"BCME_ASSOCIATED",
56b4c3e9b5SBjoern A. Zeeb 	"BCME_RANGE",
57b4c3e9b5SBjoern A. Zeeb 	"BCME_NOTFOUND",
58b4c3e9b5SBjoern A. Zeeb 	"BCME_WME_NOT_ENABLED",
59b4c3e9b5SBjoern A. Zeeb 	"BCME_TSPEC_NOTFOUND",
60b4c3e9b5SBjoern A. Zeeb 	"BCME_ACM_NOTSUPPORTED",
61b4c3e9b5SBjoern A. Zeeb 	"BCME_NOT_WME_ASSOCIATION",
62b4c3e9b5SBjoern A. Zeeb 	"BCME_SDIO_ERROR",
63b4c3e9b5SBjoern A. Zeeb 	"BCME_DONGLE_DOWN",
64b4c3e9b5SBjoern A. Zeeb 	"BCME_VERSION",
65b4c3e9b5SBjoern A. Zeeb 	"BCME_TXFAIL",
66b4c3e9b5SBjoern A. Zeeb 	"BCME_RXFAIL",
67b4c3e9b5SBjoern A. Zeeb 	"BCME_NODEVICE",
68b4c3e9b5SBjoern A. Zeeb 	"BCME_NMODE_DISABLED",
69b4c3e9b5SBjoern A. Zeeb 	"BCME_NONRESIDENT",
70b4c3e9b5SBjoern A. Zeeb 	"BCME_SCANREJECT",
71b4c3e9b5SBjoern A. Zeeb 	"BCME_USAGE_ERROR",
72b4c3e9b5SBjoern A. Zeeb 	"BCME_IOCTL_ERROR",
73b4c3e9b5SBjoern A. Zeeb 	"BCME_SERIAL_PORT_ERR",
74b4c3e9b5SBjoern A. Zeeb 	"BCME_DISABLED",
75b4c3e9b5SBjoern A. Zeeb 	"BCME_DECERR",
76b4c3e9b5SBjoern A. Zeeb 	"BCME_ENCERR",
77b4c3e9b5SBjoern A. Zeeb 	"BCME_MICERR",
78b4c3e9b5SBjoern A. Zeeb 	"BCME_REPLAY",
79b4c3e9b5SBjoern A. Zeeb 	"BCME_IE_NOTFOUND",
80b4c3e9b5SBjoern A. Zeeb };
81b4c3e9b5SBjoern A. Zeeb 
brcmf_fil_get_errstr(u32 err)82b4c3e9b5SBjoern A. Zeeb static const char *brcmf_fil_get_errstr(u32 err)
83b4c3e9b5SBjoern A. Zeeb {
84b4c3e9b5SBjoern A. Zeeb 	if (err >= ARRAY_SIZE(brcmf_fil_errstr))
85b4c3e9b5SBjoern A. Zeeb 		return "(unknown)";
86b4c3e9b5SBjoern A. Zeeb 
87b4c3e9b5SBjoern A. Zeeb 	return brcmf_fil_errstr[err];
88b4c3e9b5SBjoern A. Zeeb }
89b4c3e9b5SBjoern A. Zeeb #else
brcmf_fil_get_errstr(u32 err)90b4c3e9b5SBjoern A. Zeeb static const char *brcmf_fil_get_errstr(u32 err)
91b4c3e9b5SBjoern A. Zeeb {
92b4c3e9b5SBjoern A. Zeeb 	return "";
93b4c3e9b5SBjoern A. Zeeb }
94b4c3e9b5SBjoern A. Zeeb #endif /* DEBUG */
95b4c3e9b5SBjoern A. Zeeb 
96b4c3e9b5SBjoern A. Zeeb static s32
brcmf_fil_cmd_data(struct brcmf_if * ifp,u32 cmd,void * data,u32 len,bool set)97b4c3e9b5SBjoern A. Zeeb brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
98b4c3e9b5SBjoern A. Zeeb {
99b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
100b4c3e9b5SBjoern A. Zeeb 	s32 err, fwerr;
101b4c3e9b5SBjoern A. Zeeb 
102b4c3e9b5SBjoern A. Zeeb 	if (drvr->bus_if->state != BRCMF_BUS_UP) {
103b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "bus is down. we have nothing to do.\n");
104b4c3e9b5SBjoern A. Zeeb 		return -EIO;
105b4c3e9b5SBjoern A. Zeeb 	}
106b4c3e9b5SBjoern A. Zeeb 
107b4c3e9b5SBjoern A. Zeeb 	if (data != NULL)
108b4c3e9b5SBjoern A. Zeeb 		len = min_t(uint, len, BRCMF_DCMD_MAXLEN);
109b4c3e9b5SBjoern A. Zeeb 	if (set)
110b4c3e9b5SBjoern A. Zeeb 		err = brcmf_proto_set_dcmd(drvr, ifp->ifidx, cmd,
111b4c3e9b5SBjoern A. Zeeb 					   data, len, &fwerr);
112b4c3e9b5SBjoern A. Zeeb 	else
113b4c3e9b5SBjoern A. Zeeb 		err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd,
114b4c3e9b5SBjoern A. Zeeb 					     data, len, &fwerr);
115b4c3e9b5SBjoern A. Zeeb 
116b4c3e9b5SBjoern A. Zeeb 	if (err) {
117b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(FIL, "Failed: error=%d\n", err);
118b4c3e9b5SBjoern A. Zeeb 	} else if (fwerr < 0) {
119b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(FIL, "Firmware error: %s (%d)\n",
120b4c3e9b5SBjoern A. Zeeb 			  brcmf_fil_get_errstr((u32)(-fwerr)), fwerr);
121b4c3e9b5SBjoern A. Zeeb 		err = -EBADE;
122b4c3e9b5SBjoern A. Zeeb 	}
123b4c3e9b5SBjoern A. Zeeb 	if (ifp->fwil_fwerr)
124b4c3e9b5SBjoern A. Zeeb 		return fwerr;
125b4c3e9b5SBjoern A. Zeeb 
126b4c3e9b5SBjoern A. Zeeb 	return err;
127b4c3e9b5SBjoern A. Zeeb }
128b4c3e9b5SBjoern A. Zeeb 
129b4c3e9b5SBjoern A. Zeeb s32
brcmf_fil_cmd_data_set(struct brcmf_if * ifp,u32 cmd,void * data,u32 len)130b4c3e9b5SBjoern A. Zeeb brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
131b4c3e9b5SBjoern A. Zeeb {
132b4c3e9b5SBjoern A. Zeeb 	s32 err;
133b4c3e9b5SBjoern A. Zeeb 
134b4c3e9b5SBjoern A. Zeeb 	mutex_lock(&ifp->drvr->proto_block);
135b4c3e9b5SBjoern A. Zeeb 
136b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
137b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
138b4c3e9b5SBjoern A. Zeeb 			   min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
139b4c3e9b5SBjoern A. Zeeb 
140b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_data(ifp, cmd, data, len, true);
141b4c3e9b5SBjoern A. Zeeb 	mutex_unlock(&ifp->drvr->proto_block);
142b4c3e9b5SBjoern A. Zeeb 
143b4c3e9b5SBjoern A. Zeeb 	return err;
144b4c3e9b5SBjoern A. Zeeb }
145b4c3e9b5SBjoern A. Zeeb BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_cmd_data_set);
146b4c3e9b5SBjoern A. Zeeb 
147b4c3e9b5SBjoern A. Zeeb s32
brcmf_fil_cmd_data_get(struct brcmf_if * ifp,u32 cmd,void * data,u32 len)148b4c3e9b5SBjoern A. Zeeb brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
149b4c3e9b5SBjoern A. Zeeb {
150b4c3e9b5SBjoern A. Zeeb 	s32 err;
151b4c3e9b5SBjoern A. Zeeb 
152b4c3e9b5SBjoern A. Zeeb 	mutex_lock(&ifp->drvr->proto_block);
153b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_data(ifp, cmd, data, len, false);
154b4c3e9b5SBjoern A. Zeeb 
155b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d, err=%d\n", ifp->ifidx, cmd,
156b4c3e9b5SBjoern A. Zeeb 		  len, err);
157b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
158b4c3e9b5SBjoern A. Zeeb 			   min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
159b4c3e9b5SBjoern A. Zeeb 
160b4c3e9b5SBjoern A. Zeeb 	mutex_unlock(&ifp->drvr->proto_block);
161b4c3e9b5SBjoern A. Zeeb 
162b4c3e9b5SBjoern A. Zeeb 	return err;
163b4c3e9b5SBjoern A. Zeeb }
164b4c3e9b5SBjoern A. Zeeb BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_cmd_data_get);
165b4c3e9b5SBjoern A. Zeeb 
166b4c3e9b5SBjoern A. Zeeb static u32
brcmf_create_iovar(const char * name,const char * data,u32 datalen,char * buf,u32 buflen)167b4c3e9b5SBjoern A. Zeeb brcmf_create_iovar(const char *name, const char *data, u32 datalen,
168b4c3e9b5SBjoern A. Zeeb 		   char *buf, u32 buflen)
169b4c3e9b5SBjoern A. Zeeb {
170b4c3e9b5SBjoern A. Zeeb 	u32 len;
171b4c3e9b5SBjoern A. Zeeb 
172b4c3e9b5SBjoern A. Zeeb 	len = strlen(name) + 1;
173b4c3e9b5SBjoern A. Zeeb 
174b4c3e9b5SBjoern A. Zeeb 	if ((len + datalen) > buflen)
175b4c3e9b5SBjoern A. Zeeb 		return 0;
176b4c3e9b5SBjoern A. Zeeb 
177b4c3e9b5SBjoern A. Zeeb 	memcpy(buf, name, len);
178b4c3e9b5SBjoern A. Zeeb 
179b4c3e9b5SBjoern A. Zeeb 	/* append data onto the end of the name string */
180b4c3e9b5SBjoern A. Zeeb 	if (data && datalen)
181b4c3e9b5SBjoern A. Zeeb 		memcpy(&buf[len], data, datalen);
182b4c3e9b5SBjoern A. Zeeb 
183b4c3e9b5SBjoern A. Zeeb 	return len + datalen;
184b4c3e9b5SBjoern A. Zeeb }
185b4c3e9b5SBjoern A. Zeeb 
186b4c3e9b5SBjoern A. Zeeb 
187b4c3e9b5SBjoern A. Zeeb s32
brcmf_fil_iovar_data_set(struct brcmf_if * ifp,const char * name,const void * data,u32 len)188b4c3e9b5SBjoern A. Zeeb brcmf_fil_iovar_data_set(struct brcmf_if *ifp, const char *name, const void *data,
189b4c3e9b5SBjoern A. Zeeb 			 u32 len)
190b4c3e9b5SBjoern A. Zeeb {
191b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
192b4c3e9b5SBjoern A. Zeeb 	s32 err;
193b4c3e9b5SBjoern A. Zeeb 	u32 buflen;
194b4c3e9b5SBjoern A. Zeeb 
195b4c3e9b5SBjoern A. Zeeb 	mutex_lock(&drvr->proto_block);
196b4c3e9b5SBjoern A. Zeeb 
197b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
198b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
199b4c3e9b5SBjoern A. Zeeb 			   min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
200b4c3e9b5SBjoern A. Zeeb 
201b4c3e9b5SBjoern A. Zeeb 	buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
202b4c3e9b5SBjoern A. Zeeb 				    sizeof(drvr->proto_buf));
203b4c3e9b5SBjoern A. Zeeb 	if (buflen) {
204b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
205b4c3e9b5SBjoern A. Zeeb 					 buflen, true);
206b4c3e9b5SBjoern A. Zeeb 	} else {
207b4c3e9b5SBjoern A. Zeeb 		err = -EPERM;
208b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Creating iovar failed\n");
209b4c3e9b5SBjoern A. Zeeb 	}
210b4c3e9b5SBjoern A. Zeeb 
211b4c3e9b5SBjoern A. Zeeb 	mutex_unlock(&drvr->proto_block);
212b4c3e9b5SBjoern A. Zeeb 	return err;
213b4c3e9b5SBjoern A. Zeeb }
214b4c3e9b5SBjoern A. Zeeb BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_iovar_data_set);
215b4c3e9b5SBjoern A. Zeeb 
216b4c3e9b5SBjoern A. Zeeb s32
brcmf_fil_iovar_data_get(struct brcmf_if * ifp,const char * name,void * data,u32 len)217b4c3e9b5SBjoern A. Zeeb brcmf_fil_iovar_data_get(struct brcmf_if *ifp, const char *name, void *data,
218b4c3e9b5SBjoern A. Zeeb 			 u32 len)
219b4c3e9b5SBjoern A. Zeeb {
220b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
221b4c3e9b5SBjoern A. Zeeb 	s32 err;
222b4c3e9b5SBjoern A. Zeeb 	u32 buflen;
223b4c3e9b5SBjoern A. Zeeb 
224b4c3e9b5SBjoern A. Zeeb 	mutex_lock(&drvr->proto_block);
225b4c3e9b5SBjoern A. Zeeb 
226b4c3e9b5SBjoern A. Zeeb 	buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
227b4c3e9b5SBjoern A. Zeeb 				    sizeof(drvr->proto_buf));
228b4c3e9b5SBjoern A. Zeeb 	if (buflen) {
229b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
230b4c3e9b5SBjoern A. Zeeb 					 buflen, false);
231b4c3e9b5SBjoern A. Zeeb 		if (err == 0)
232b4c3e9b5SBjoern A. Zeeb 			memcpy(data, drvr->proto_buf, len);
233b4c3e9b5SBjoern A. Zeeb 	} else {
234b4c3e9b5SBjoern A. Zeeb 		err = -EPERM;
235b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Creating iovar failed\n");
236b4c3e9b5SBjoern A. Zeeb 	}
237b4c3e9b5SBjoern A. Zeeb 
238b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d, err=%d\n", ifp->ifidx, name,
239b4c3e9b5SBjoern A. Zeeb 		  len, err);
240b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
241b4c3e9b5SBjoern A. Zeeb 			   min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
242b4c3e9b5SBjoern A. Zeeb 
243b4c3e9b5SBjoern A. Zeeb 	mutex_unlock(&drvr->proto_block);
244b4c3e9b5SBjoern A. Zeeb 	return err;
245b4c3e9b5SBjoern A. Zeeb }
246b4c3e9b5SBjoern A. Zeeb BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_iovar_data_get);
247b4c3e9b5SBjoern A. Zeeb 
248b4c3e9b5SBjoern A. Zeeb static u32
249*902136e0SBjoern A. Zeeb #if defined(__linux__)
brcmf_create_bsscfg(s32 bsscfgidx,const char * name,char * data,u32 datalen,char * buf,u32 buflen)250b4c3e9b5SBjoern A. Zeeb brcmf_create_bsscfg(s32 bsscfgidx, const char *name, char *data, u32 datalen,
251*902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
252*902136e0SBjoern A. Zeeb brcmf_create_bsscfg(s32 bsscfgidx, const char *name, const char *data, u32 datalen,
253*902136e0SBjoern A. Zeeb #endif
254b4c3e9b5SBjoern A. Zeeb 		    char *buf, u32 buflen)
255b4c3e9b5SBjoern A. Zeeb {
256b4c3e9b5SBjoern A. Zeeb 	const s8 *prefix = "bsscfg:";
257b4c3e9b5SBjoern A. Zeeb 	s8 *p;
258b4c3e9b5SBjoern A. Zeeb 	u32 prefixlen;
259b4c3e9b5SBjoern A. Zeeb 	u32 namelen;
260b4c3e9b5SBjoern A. Zeeb 	u32 iolen;
261b4c3e9b5SBjoern A. Zeeb 	__le32 bsscfgidx_le;
262b4c3e9b5SBjoern A. Zeeb 
263b4c3e9b5SBjoern A. Zeeb 	if (bsscfgidx == 0)
264b4c3e9b5SBjoern A. Zeeb 		return brcmf_create_iovar(name, data, datalen, buf, buflen);
265b4c3e9b5SBjoern A. Zeeb 
266b4c3e9b5SBjoern A. Zeeb 	prefixlen = strlen(prefix);
267b4c3e9b5SBjoern A. Zeeb 	namelen = strlen(name) + 1; /* length of iovar  name + null */
268b4c3e9b5SBjoern A. Zeeb 	iolen = prefixlen + namelen + sizeof(bsscfgidx_le) + datalen;
269b4c3e9b5SBjoern A. Zeeb 
270b4c3e9b5SBjoern A. Zeeb 	if (buflen < iolen) {
271b4c3e9b5SBjoern A. Zeeb 		brcmf_err("buffer is too short\n");
272b4c3e9b5SBjoern A. Zeeb 		return 0;
273b4c3e9b5SBjoern A. Zeeb 	}
274b4c3e9b5SBjoern A. Zeeb 
275b4c3e9b5SBjoern A. Zeeb 	p = buf;
276b4c3e9b5SBjoern A. Zeeb 
277b4c3e9b5SBjoern A. Zeeb 	/* copy prefix, no null */
278b4c3e9b5SBjoern A. Zeeb 	memcpy(p, prefix, prefixlen);
279b4c3e9b5SBjoern A. Zeeb 	p += prefixlen;
280b4c3e9b5SBjoern A. Zeeb 
281b4c3e9b5SBjoern A. Zeeb 	/* copy iovar name including null */
282b4c3e9b5SBjoern A. Zeeb 	memcpy(p, name, namelen);
283b4c3e9b5SBjoern A. Zeeb 	p += namelen;
284b4c3e9b5SBjoern A. Zeeb 
285b4c3e9b5SBjoern A. Zeeb 	/* bss config index as first data */
286b4c3e9b5SBjoern A. Zeeb 	bsscfgidx_le = cpu_to_le32(bsscfgidx);
287b4c3e9b5SBjoern A. Zeeb 	memcpy(p, &bsscfgidx_le, sizeof(bsscfgidx_le));
288b4c3e9b5SBjoern A. Zeeb 	p += sizeof(bsscfgidx_le);
289b4c3e9b5SBjoern A. Zeeb 
290b4c3e9b5SBjoern A. Zeeb 	/* parameter buffer follows */
291b4c3e9b5SBjoern A. Zeeb 	if (datalen)
292b4c3e9b5SBjoern A. Zeeb 		memcpy(p, data, datalen);
293b4c3e9b5SBjoern A. Zeeb 
294b4c3e9b5SBjoern A. Zeeb 	return iolen;
295b4c3e9b5SBjoern A. Zeeb }
296b4c3e9b5SBjoern A. Zeeb 
297b4c3e9b5SBjoern A. Zeeb s32
brcmf_fil_bsscfg_data_set(struct brcmf_if * ifp,const char * name,void * data,u32 len)298b4c3e9b5SBjoern A. Zeeb brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, const char *name,
299*902136e0SBjoern A. Zeeb #if defined(__linux__)
300b4c3e9b5SBjoern A. Zeeb 			  void *data, u32 len)
301*902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
302*902136e0SBjoern A. Zeeb 			  const void *data, u32 len)
303*902136e0SBjoern A. Zeeb #endif
304b4c3e9b5SBjoern A. Zeeb {
305b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
306b4c3e9b5SBjoern A. Zeeb 	s32 err;
307b4c3e9b5SBjoern A. Zeeb 	u32 buflen;
308b4c3e9b5SBjoern A. Zeeb 
309b4c3e9b5SBjoern A. Zeeb 	mutex_lock(&drvr->proto_block);
310b4c3e9b5SBjoern A. Zeeb 
311b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx,
312b4c3e9b5SBjoern A. Zeeb 		  ifp->bsscfgidx, name, len);
313b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
314b4c3e9b5SBjoern A. Zeeb 			   min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
315b4c3e9b5SBjoern A. Zeeb 
316b4c3e9b5SBjoern A. Zeeb 	buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,
317b4c3e9b5SBjoern A. Zeeb 				     drvr->proto_buf, sizeof(drvr->proto_buf));
318b4c3e9b5SBjoern A. Zeeb 	if (buflen) {
319b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
320b4c3e9b5SBjoern A. Zeeb 					 buflen, true);
321b4c3e9b5SBjoern A. Zeeb 	} else {
322b4c3e9b5SBjoern A. Zeeb 		err = -EPERM;
323b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Creating bsscfg failed\n");
324b4c3e9b5SBjoern A. Zeeb 	}
325b4c3e9b5SBjoern A. Zeeb 
326b4c3e9b5SBjoern A. Zeeb 	mutex_unlock(&drvr->proto_block);
327b4c3e9b5SBjoern A. Zeeb 	return err;
328b4c3e9b5SBjoern A. Zeeb }
329b4c3e9b5SBjoern A. Zeeb BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_bsscfg_data_set);
330b4c3e9b5SBjoern A. Zeeb 
331b4c3e9b5SBjoern A. Zeeb s32
brcmf_fil_bsscfg_data_get(struct brcmf_if * ifp,const char * name,void * data,u32 len)332b4c3e9b5SBjoern A. Zeeb brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, const char *name,
333b4c3e9b5SBjoern A. Zeeb 			  void *data, u32 len)
334b4c3e9b5SBjoern A. Zeeb {
335b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
336b4c3e9b5SBjoern A. Zeeb 	s32 err;
337b4c3e9b5SBjoern A. Zeeb 	u32 buflen;
338b4c3e9b5SBjoern A. Zeeb 
339b4c3e9b5SBjoern A. Zeeb 	mutex_lock(&drvr->proto_block);
340b4c3e9b5SBjoern A. Zeeb 
341b4c3e9b5SBjoern A. Zeeb 	buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,
342b4c3e9b5SBjoern A. Zeeb 				     drvr->proto_buf, sizeof(drvr->proto_buf));
343b4c3e9b5SBjoern A. Zeeb 	if (buflen) {
344b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
345b4c3e9b5SBjoern A. Zeeb 					 buflen, false);
346b4c3e9b5SBjoern A. Zeeb 		if (err == 0)
347b4c3e9b5SBjoern A. Zeeb 			memcpy(data, drvr->proto_buf, len);
348b4c3e9b5SBjoern A. Zeeb 	} else {
349b4c3e9b5SBjoern A. Zeeb 		err = -EPERM;
350b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Creating bsscfg failed\n");
351b4c3e9b5SBjoern A. Zeeb 	}
352b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d, err=%d\n",
353b4c3e9b5SBjoern A. Zeeb 		  ifp->ifidx, ifp->bsscfgidx, name, len, err);
354b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
355b4c3e9b5SBjoern A. Zeeb 			   min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
356b4c3e9b5SBjoern A. Zeeb 
357b4c3e9b5SBjoern A. Zeeb 	mutex_unlock(&drvr->proto_block);
358b4c3e9b5SBjoern A. Zeeb 	return err;
359b4c3e9b5SBjoern A. Zeeb }
360b4c3e9b5SBjoern A. Zeeb BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_bsscfg_data_get);
361b4c3e9b5SBjoern A. Zeeb 
brcmf_create_xtlv(const char * name,u16 id,char * data,u32 len,char * buf,u32 buflen)362b4c3e9b5SBjoern A. Zeeb static u32 brcmf_create_xtlv(const char *name, u16 id, char *data, u32 len,
363b4c3e9b5SBjoern A. Zeeb 			     char *buf, u32 buflen)
364b4c3e9b5SBjoern A. Zeeb {
365b4c3e9b5SBjoern A. Zeeb 	u32 iolen;
366b4c3e9b5SBjoern A. Zeeb 	u32 nmlen;
367b4c3e9b5SBjoern A. Zeeb 
368b4c3e9b5SBjoern A. Zeeb 	nmlen = strlen(name) + 1;
369b4c3e9b5SBjoern A. Zeeb 	iolen = nmlen + brcmf_xtlv_data_size(len, BRCMF_XTLV_OPTION_ALIGN32);
370b4c3e9b5SBjoern A. Zeeb 
371b4c3e9b5SBjoern A. Zeeb 	if (iolen > buflen) {
372b4c3e9b5SBjoern A. Zeeb 		brcmf_err("buffer is too short\n");
373b4c3e9b5SBjoern A. Zeeb 		return 0;
374b4c3e9b5SBjoern A. Zeeb 	}
375b4c3e9b5SBjoern A. Zeeb 
376b4c3e9b5SBjoern A. Zeeb 	memcpy(buf, name, nmlen);
377b4c3e9b5SBjoern A. Zeeb 	brcmf_xtlv_pack_header((void *)(buf + nmlen), id, len, data,
378b4c3e9b5SBjoern A. Zeeb 			       BRCMF_XTLV_OPTION_ALIGN32);
379b4c3e9b5SBjoern A. Zeeb 
380b4c3e9b5SBjoern A. Zeeb 	return iolen;
381b4c3e9b5SBjoern A. Zeeb }
382b4c3e9b5SBjoern A. Zeeb 
brcmf_fil_xtlv_data_set(struct brcmf_if * ifp,const char * name,u16 id,void * data,u32 len)383b4c3e9b5SBjoern A. Zeeb s32 brcmf_fil_xtlv_data_set(struct brcmf_if *ifp, const char *name, u16 id,
384b4c3e9b5SBjoern A. Zeeb 			    void *data, u32 len)
385b4c3e9b5SBjoern A. Zeeb {
386b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
387b4c3e9b5SBjoern A. Zeeb 	s32 err;
388b4c3e9b5SBjoern A. Zeeb 	u32 buflen;
389b4c3e9b5SBjoern A. Zeeb 
390b4c3e9b5SBjoern A. Zeeb 	mutex_lock(&drvr->proto_block);
391b4c3e9b5SBjoern A. Zeeb 
392b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u\n", ifp->ifidx, name,
393b4c3e9b5SBjoern A. Zeeb 		  id, len);
394b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
395b4c3e9b5SBjoern A. Zeeb 			   min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
396b4c3e9b5SBjoern A. Zeeb 
397b4c3e9b5SBjoern A. Zeeb 	buflen = brcmf_create_xtlv(name, id, data, len,
398b4c3e9b5SBjoern A. Zeeb 				   drvr->proto_buf, sizeof(drvr->proto_buf));
399b4c3e9b5SBjoern A. Zeeb 	if (buflen) {
400b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
401b4c3e9b5SBjoern A. Zeeb 					 buflen, true);
402b4c3e9b5SBjoern A. Zeeb 	} else {
403b4c3e9b5SBjoern A. Zeeb 		err = -EPERM;
404b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Creating xtlv failed\n");
405b4c3e9b5SBjoern A. Zeeb 	}
406b4c3e9b5SBjoern A. Zeeb 
407b4c3e9b5SBjoern A. Zeeb 	mutex_unlock(&drvr->proto_block);
408b4c3e9b5SBjoern A. Zeeb 	return err;
409b4c3e9b5SBjoern A. Zeeb }
410b4c3e9b5SBjoern A. Zeeb BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_xtlv_data_set);
411b4c3e9b5SBjoern A. Zeeb 
brcmf_fil_xtlv_data_get(struct brcmf_if * ifp,const char * name,u16 id,void * data,u32 len)412b4c3e9b5SBjoern A. Zeeb s32 brcmf_fil_xtlv_data_get(struct brcmf_if *ifp, const char *name, u16 id,
413b4c3e9b5SBjoern A. Zeeb 			    void *data, u32 len)
414b4c3e9b5SBjoern A. Zeeb {
415b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
416b4c3e9b5SBjoern A. Zeeb 	s32 err;
417b4c3e9b5SBjoern A. Zeeb 	u32 buflen;
418b4c3e9b5SBjoern A. Zeeb 
419b4c3e9b5SBjoern A. Zeeb 	mutex_lock(&drvr->proto_block);
420b4c3e9b5SBjoern A. Zeeb 
421b4c3e9b5SBjoern A. Zeeb 	buflen = brcmf_create_xtlv(name, id, data, len,
422b4c3e9b5SBjoern A. Zeeb 				   drvr->proto_buf, sizeof(drvr->proto_buf));
423b4c3e9b5SBjoern A. Zeeb 	if (buflen) {
424b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
425b4c3e9b5SBjoern A. Zeeb 					 buflen, false);
426b4c3e9b5SBjoern A. Zeeb 		if (err == 0)
427b4c3e9b5SBjoern A. Zeeb 			memcpy(data, drvr->proto_buf, len);
428b4c3e9b5SBjoern A. Zeeb 	} else {
429b4c3e9b5SBjoern A. Zeeb 		err = -EPERM;
430b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Creating bsscfg failed\n");
431b4c3e9b5SBjoern A. Zeeb 	}
432b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u, err=%d\n",
433b4c3e9b5SBjoern A. Zeeb 		  ifp->ifidx, name, id, len, err);
434b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
435b4c3e9b5SBjoern A. Zeeb 			   min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
436b4c3e9b5SBjoern A. Zeeb 
437b4c3e9b5SBjoern A. Zeeb 	mutex_unlock(&drvr->proto_block);
438b4c3e9b5SBjoern A. Zeeb 	return err;
439b4c3e9b5SBjoern A. Zeeb }
440b4c3e9b5SBjoern A. Zeeb BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_xtlv_data_get);
441