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