Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/broadcom/brcm80211/brcmfmac/fwil.c
178665 views
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
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
90
static const char *brcmf_fil_get_errstr(u32 err)
91
{
92
return "";
93
}
94
#endif /* DEBUG */
95
96
static s32
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
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
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
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
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
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__)
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
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
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
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
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
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
442