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 36static 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 92static 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 100static const char *brcmf_fil_get_errstr(u32 err) 101{ 102 return ""; 103} 104#endif /* DEBUG */ 105 106static s32 107brcmf_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; 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, data, len); 121 else 122 err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd, data, len); 123 124 if (err >= 0) 125 return 0; 126 127 brcmf_dbg(FIL, "Failed: %s (%d)\n", 128 brcmf_fil_get_errstr((u32)(-err)), err); 129 return -EBADE; 130} 131 132s32 133brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) 134{ 135 s32 err; 136 137 mutex_lock(&ifp->drvr->proto_block); 138 139 brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len); 140 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 141 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 142 143 err = brcmf_fil_cmd_data(ifp, cmd, data, len, true); 144 mutex_unlock(&ifp->drvr->proto_block); 145 146 return err; 147} 148 149s32 150brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) 151{ 152 s32 err; 153 154 mutex_lock(&ifp->drvr->proto_block); 155 err = brcmf_fil_cmd_data(ifp, cmd, data, len, false); 156 157 brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len); 158 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 159 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 160 161 mutex_unlock(&ifp->drvr->proto_block); 162 163 return err; 164} 165 166 167s32 168brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data) 169{ 170 s32 err; 171 __le32 data_le = cpu_to_le32(data); 172 173 mutex_lock(&ifp->drvr->proto_block); 174 brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, data); 175 err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true); 176 mutex_unlock(&ifp->drvr->proto_block); 177 178 return err; 179} 180 181s32 182brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data) 183{ 184 s32 err; 185 __le32 data_le = cpu_to_le32(*data); 186 187 mutex_lock(&ifp->drvr->proto_block); 188 err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false); 189 mutex_unlock(&ifp->drvr->proto_block); 190 *data = le32_to_cpu(data_le); 191 brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, *data); 192 193 return err; 194} 195 196static u32 197brcmf_create_iovar(char *name, const char *data, u32 datalen, 198 char *buf, u32 buflen) 199{ 200 u32 len; 201 202 len = strlen(name) + 1; 203 204 if ((len + datalen) > buflen) 205 return 0; 206 207 memcpy(buf, name, len); 208 209 /* append data onto the end of the name string */ 210 if (data && datalen) 211 memcpy(&buf[len], data, datalen); 212 213 return len + datalen; 214} 215 216 217s32 218brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, 219 u32 len) 220{ 221 struct brcmf_pub *drvr = ifp->drvr; 222 s32 err; 223 u32 buflen; 224 225 mutex_lock(&drvr->proto_block); 226 227 brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len); 228 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 229 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 230 231 buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, 232 sizeof(drvr->proto_buf)); 233 if (buflen) { 234 err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, 235 buflen, true); 236 } else { 237 err = -EPERM; 238 brcmf_err("Creating iovar failed\n"); 239 } 240 241 mutex_unlock(&drvr->proto_block); 242 return err; 243} 244 245s32 246brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, 247 u32 len) 248{ 249 struct brcmf_pub *drvr = ifp->drvr; 250 s32 err; 251 u32 buflen; 252 253 mutex_lock(&drvr->proto_block); 254 255 buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, 256 sizeof(drvr->proto_buf)); 257 if (buflen) { 258 err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, 259 buflen, false); 260 if (err == 0) 261 memcpy(data, drvr->proto_buf, len); 262 } else { 263 err = -EPERM; 264 brcmf_err("Creating iovar failed\n"); 265 } 266 267 brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len); 268 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 269 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 270 271 mutex_unlock(&drvr->proto_block); 272 return err; 273} 274 275s32 276brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data) 277{ 278 __le32 data_le = cpu_to_le32(data); 279 280 return brcmf_fil_iovar_data_set(ifp, name, &data_le, sizeof(data_le)); 281} 282 283s32 284brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data) 285{ 286 __le32 data_le = cpu_to_le32(*data); 287 s32 err; 288 289 err = brcmf_fil_iovar_data_get(ifp, name, &data_le, sizeof(data_le)); 290 if (err == 0) 291 *data = le32_to_cpu(data_le); 292 return err; 293} 294 295static u32 296brcmf_create_bsscfg(s32 bssidx, char *name, char *data, u32 datalen, char *buf, 297 u32 buflen) 298{ 299 const s8 *prefix = "bsscfg:"; 300 s8 *p; 301 u32 prefixlen; 302 u32 namelen; 303 u32 iolen; 304 __le32 bssidx_le; 305 306 if (bssidx == 0) 307 return brcmf_create_iovar(name, data, datalen, buf, buflen); 308 309 prefixlen = strlen(prefix); 310 namelen = strlen(name) + 1; /* lengh of iovar name + null */ 311 iolen = prefixlen + namelen + sizeof(bssidx_le) + datalen; 312 313 if (buflen < iolen) { 314 brcmf_err("buffer is too short\n"); 315 return 0; 316 } 317 318 p = buf; 319 320 /* copy prefix, no null */ 321 memcpy(p, prefix, prefixlen); 322 p += prefixlen; 323 324 /* copy iovar name including null */ 325 memcpy(p, name, namelen); 326 p += namelen; 327 328 /* bss config index as first data */ 329 bssidx_le = cpu_to_le32(bssidx); 330 memcpy(p, &bssidx_le, sizeof(bssidx_le)); 331 p += sizeof(bssidx_le); 332 333 /* parameter buffer follows */ 334 if (datalen) 335 memcpy(p, data, datalen); 336 337 return iolen; 338} 339 340s32 341brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, 342 void *data, u32 len) 343{ 344 struct brcmf_pub *drvr = ifp->drvr; 345 s32 err; 346 u32 buflen; 347 348 mutex_lock(&drvr->proto_block); 349 350 brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx, 351 ifp->bssidx, name, len); 352 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 353 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 354 355 buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len, 356 drvr->proto_buf, sizeof(drvr->proto_buf)); 357 if (buflen) { 358 err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, 359 buflen, true); 360 } else { 361 err = -EPERM; 362 brcmf_err("Creating bsscfg failed\n"); 363 } 364 365 mutex_unlock(&drvr->proto_block); 366 return err; 367} 368 369s32 370brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, 371 void *data, u32 len) 372{ 373 struct brcmf_pub *drvr = ifp->drvr; 374 s32 err; 375 u32 buflen; 376 377 mutex_lock(&drvr->proto_block); 378 379 buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len, 380 drvr->proto_buf, sizeof(drvr->proto_buf)); 381 if (buflen) { 382 err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, 383 buflen, false); 384 if (err == 0) 385 memcpy(data, drvr->proto_buf, len); 386 } else { 387 err = -EPERM; 388 brcmf_err("Creating bsscfg failed\n"); 389 } 390 brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx, 391 ifp->bssidx, name, len); 392 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 393 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 394 395 mutex_unlock(&drvr->proto_block); 396 return err; 397 398} 399 400s32 401brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data) 402{ 403 __le32 data_le = cpu_to_le32(data); 404 405 return brcmf_fil_bsscfg_data_set(ifp, name, &data_le, 406 sizeof(data_le)); 407} 408 409s32 410brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data) 411{ 412 __le32 data_le = cpu_to_le32(*data); 413 s32 err; 414 415 err = brcmf_fil_bsscfg_data_get(ifp, name, &data_le, 416 sizeof(data_le)); 417 if (err == 0) 418 *data = le32_to_cpu(data_le); 419 return err; 420} 421