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