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
brcmf_create_bsscfg(s32 bsscfgidx,const char * name,char * data,u32 datalen,char * buf,u32 buflen)249 brcmf_create_bsscfg(s32 bsscfgidx, const char *name, char *data, u32 datalen,
250 char *buf, u32 buflen)
251 {
252 const s8 *prefix = "bsscfg:";
253 s8 *p;
254 u32 prefixlen;
255 u32 namelen;
256 u32 iolen;
257 __le32 bsscfgidx_le;
258
259 if (bsscfgidx == 0)
260 return brcmf_create_iovar(name, data, datalen, buf, buflen);
261
262 prefixlen = strlen(prefix);
263 namelen = strlen(name) + 1; /* length of iovar name + null */
264 iolen = prefixlen + namelen + sizeof(bsscfgidx_le) + datalen;
265
266 if (buflen < iolen) {
267 brcmf_err("buffer is too short\n");
268 return 0;
269 }
270
271 p = buf;
272
273 /* copy prefix, no null */
274 memcpy(p, prefix, prefixlen);
275 p += prefixlen;
276
277 /* copy iovar name including null */
278 memcpy(p, name, namelen);
279 p += namelen;
280
281 /* bss config index as first data */
282 bsscfgidx_le = cpu_to_le32(bsscfgidx);
283 memcpy(p, &bsscfgidx_le, sizeof(bsscfgidx_le));
284 p += sizeof(bsscfgidx_le);
285
286 /* parameter buffer follows */
287 if (datalen)
288 memcpy(p, data, datalen);
289
290 return iolen;
291 }
292
293 s32
brcmf_fil_bsscfg_data_set(struct brcmf_if * ifp,const char * name,void * data,u32 len)294 brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, const char *name,
295 void *data, u32 len)
296 {
297 struct brcmf_pub *drvr = ifp->drvr;
298 s32 err;
299 u32 buflen;
300
301 mutex_lock(&drvr->proto_block);
302
303 brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx,
304 ifp->bsscfgidx, name, len);
305 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
306 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
307
308 buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,
309 drvr->proto_buf, sizeof(drvr->proto_buf));
310 if (buflen) {
311 err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
312 buflen, true);
313 } else {
314 err = -EPERM;
315 bphy_err(drvr, "Creating bsscfg failed\n");
316 }
317
318 mutex_unlock(&drvr->proto_block);
319 return err;
320 }
321 BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_bsscfg_data_set);
322
323 s32
brcmf_fil_bsscfg_data_get(struct brcmf_if * ifp,const char * name,void * data,u32 len)324 brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, const char *name,
325 void *data, u32 len)
326 {
327 struct brcmf_pub *drvr = ifp->drvr;
328 s32 err;
329 u32 buflen;
330
331 mutex_lock(&drvr->proto_block);
332
333 buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,
334 drvr->proto_buf, sizeof(drvr->proto_buf));
335 if (buflen) {
336 err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
337 buflen, false);
338 if (err == 0)
339 memcpy(data, drvr->proto_buf, len);
340 } else {
341 err = -EPERM;
342 bphy_err(drvr, "Creating bsscfg failed\n");
343 }
344 brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d, err=%d\n",
345 ifp->ifidx, ifp->bsscfgidx, name, len, err);
346 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
347 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
348
349 mutex_unlock(&drvr->proto_block);
350 return err;
351 }
352 BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_bsscfg_data_get);
353
brcmf_create_xtlv(const char * name,u16 id,char * data,u32 len,char * buf,u32 buflen)354 static u32 brcmf_create_xtlv(const char *name, u16 id, char *data, u32 len,
355 char *buf, u32 buflen)
356 {
357 u32 iolen;
358 u32 nmlen;
359
360 nmlen = strlen(name) + 1;
361 iolen = nmlen + brcmf_xtlv_data_size(len, BRCMF_XTLV_OPTION_ALIGN32);
362
363 if (iolen > buflen) {
364 brcmf_err("buffer is too short\n");
365 return 0;
366 }
367
368 memcpy(buf, name, nmlen);
369 brcmf_xtlv_pack_header((void *)(buf + nmlen), id, len, data,
370 BRCMF_XTLV_OPTION_ALIGN32);
371
372 return iolen;
373 }
374
brcmf_fil_xtlv_data_set(struct brcmf_if * ifp,const char * name,u16 id,void * data,u32 len)375 s32 brcmf_fil_xtlv_data_set(struct brcmf_if *ifp, const char *name, u16 id,
376 void *data, u32 len)
377 {
378 struct brcmf_pub *drvr = ifp->drvr;
379 s32 err;
380 u32 buflen;
381
382 mutex_lock(&drvr->proto_block);
383
384 brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u\n", ifp->ifidx, name,
385 id, len);
386 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
387 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
388
389 buflen = brcmf_create_xtlv(name, id, data, len,
390 drvr->proto_buf, sizeof(drvr->proto_buf));
391 if (buflen) {
392 err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
393 buflen, true);
394 } else {
395 err = -EPERM;
396 bphy_err(drvr, "Creating xtlv failed\n");
397 }
398
399 mutex_unlock(&drvr->proto_block);
400 return err;
401 }
402 BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_xtlv_data_set);
403
brcmf_fil_xtlv_data_get(struct brcmf_if * ifp,const char * name,u16 id,void * data,u32 len)404 s32 brcmf_fil_xtlv_data_get(struct brcmf_if *ifp, const char *name, u16 id,
405 void *data, u32 len)
406 {
407 struct brcmf_pub *drvr = ifp->drvr;
408 s32 err;
409 u32 buflen;
410
411 mutex_lock(&drvr->proto_block);
412
413 buflen = brcmf_create_xtlv(name, id, data, len,
414 drvr->proto_buf, sizeof(drvr->proto_buf));
415 if (buflen) {
416 err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
417 buflen, false);
418 if (err == 0)
419 memcpy(data, drvr->proto_buf, len);
420 } else {
421 err = -EPERM;
422 bphy_err(drvr, "Creating bsscfg failed\n");
423 }
424 brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u, err=%d\n",
425 ifp->ifidx, name, id, len, err);
426 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
427 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
428
429 mutex_unlock(&drvr->proto_block);
430 return err;
431 }
432 BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_xtlv_data_get);
433