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