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 146 s32 147 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) 148 { 149 s32 err; 150 151 mutex_lock(&ifp->drvr->proto_block); 152 err = brcmf_fil_cmd_data(ifp, cmd, data, len, false); 153 154 brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d, err=%d\n", ifp->ifidx, cmd, 155 len, err); 156 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 157 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 158 159 mutex_unlock(&ifp->drvr->proto_block); 160 161 return err; 162 } 163 164 165 s32 166 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data) 167 { 168 s32 err; 169 __le32 data_le = cpu_to_le32(data); 170 171 mutex_lock(&ifp->drvr->proto_block); 172 brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, data); 173 err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true); 174 mutex_unlock(&ifp->drvr->proto_block); 175 176 return err; 177 } 178 179 s32 180 brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data) 181 { 182 s32 err; 183 __le32 data_le = cpu_to_le32(*data); 184 185 mutex_lock(&ifp->drvr->proto_block); 186 err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false); 187 mutex_unlock(&ifp->drvr->proto_block); 188 *data = le32_to_cpu(data_le); 189 brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, *data); 190 191 return err; 192 } 193 194 static u32 195 brcmf_create_iovar(const char *name, const char *data, u32 datalen, 196 char *buf, u32 buflen) 197 { 198 u32 len; 199 200 len = strlen(name) + 1; 201 202 if ((len + datalen) > buflen) 203 return 0; 204 205 memcpy(buf, name, len); 206 207 /* append data onto the end of the name string */ 208 if (data && datalen) 209 memcpy(&buf[len], data, datalen); 210 211 return len + datalen; 212 } 213 214 215 s32 216 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, const char *name, const void *data, 217 u32 len) 218 { 219 struct brcmf_pub *drvr = ifp->drvr; 220 s32 err; 221 u32 buflen; 222 223 mutex_lock(&drvr->proto_block); 224 225 brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len); 226 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 227 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 228 229 buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, 230 sizeof(drvr->proto_buf)); 231 if (buflen) { 232 err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, 233 buflen, true); 234 } else { 235 err = -EPERM; 236 bphy_err(drvr, "Creating iovar failed\n"); 237 } 238 239 mutex_unlock(&drvr->proto_block); 240 return err; 241 } 242 243 s32 244 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, const char *name, void *data, 245 u32 len) 246 { 247 struct brcmf_pub *drvr = ifp->drvr; 248 s32 err; 249 u32 buflen; 250 251 mutex_lock(&drvr->proto_block); 252 253 buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, 254 sizeof(drvr->proto_buf)); 255 if (buflen) { 256 err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, 257 buflen, false); 258 if (err == 0) 259 memcpy(data, drvr->proto_buf, len); 260 } else { 261 err = -EPERM; 262 bphy_err(drvr, "Creating iovar failed\n"); 263 } 264 265 brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d, err=%d\n", ifp->ifidx, name, 266 len, err); 267 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 268 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 269 270 mutex_unlock(&drvr->proto_block); 271 return err; 272 } 273 274 s32 275 brcmf_fil_iovar_int_set(struct brcmf_if *ifp, const char *name, u32 data) 276 { 277 __le32 data_le = cpu_to_le32(data); 278 279 return brcmf_fil_iovar_data_set(ifp, name, &data_le, sizeof(data_le)); 280 } 281 282 s32 283 brcmf_fil_iovar_int_get(struct brcmf_if *ifp, const char *name, u32 *data) 284 { 285 __le32 data_le = cpu_to_le32(*data); 286 s32 err; 287 288 err = brcmf_fil_iovar_data_get(ifp, name, &data_le, sizeof(data_le)); 289 if (err == 0) 290 *data = le32_to_cpu(data_le); 291 return err; 292 } 293 294 static u32 295 brcmf_create_bsscfg(s32 bsscfgidx, const char *name, char *data, u32 datalen, 296 char *buf, u32 buflen) 297 { 298 const s8 *prefix = "bsscfg:"; 299 s8 *p; 300 u32 prefixlen; 301 u32 namelen; 302 u32 iolen; 303 __le32 bsscfgidx_le; 304 305 if (bsscfgidx == 0) 306 return brcmf_create_iovar(name, data, datalen, buf, buflen); 307 308 prefixlen = strlen(prefix); 309 namelen = strlen(name) + 1; /* length of iovar name + null */ 310 iolen = prefixlen + namelen + sizeof(bsscfgidx_le) + datalen; 311 312 if (buflen < iolen) { 313 brcmf_err("buffer is too short\n"); 314 return 0; 315 } 316 317 p = buf; 318 319 /* copy prefix, no null */ 320 memcpy(p, prefix, prefixlen); 321 p += prefixlen; 322 323 /* copy iovar name including null */ 324 memcpy(p, name, namelen); 325 p += namelen; 326 327 /* bss config index as first data */ 328 bsscfgidx_le = cpu_to_le32(bsscfgidx); 329 memcpy(p, &bsscfgidx_le, sizeof(bsscfgidx_le)); 330 p += sizeof(bsscfgidx_le); 331 332 /* parameter buffer follows */ 333 if (datalen) 334 memcpy(p, data, datalen); 335 336 return iolen; 337 } 338 339 s32 340 brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, const char *name, 341 void *data, u32 len) 342 { 343 struct brcmf_pub *drvr = ifp->drvr; 344 s32 err; 345 u32 buflen; 346 347 mutex_lock(&drvr->proto_block); 348 349 brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx, 350 ifp->bsscfgidx, name, len); 351 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 352 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 353 354 buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len, 355 drvr->proto_buf, sizeof(drvr->proto_buf)); 356 if (buflen) { 357 err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, 358 buflen, true); 359 } else { 360 err = -EPERM; 361 bphy_err(drvr, "Creating bsscfg failed\n"); 362 } 363 364 mutex_unlock(&drvr->proto_block); 365 return err; 366 } 367 368 s32 369 brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, const char *name, 370 void *data, u32 len) 371 { 372 struct brcmf_pub *drvr = ifp->drvr; 373 s32 err; 374 u32 buflen; 375 376 mutex_lock(&drvr->proto_block); 377 378 buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len, 379 drvr->proto_buf, sizeof(drvr->proto_buf)); 380 if (buflen) { 381 err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, 382 buflen, false); 383 if (err == 0) 384 memcpy(data, drvr->proto_buf, len); 385 } else { 386 err = -EPERM; 387 bphy_err(drvr, "Creating bsscfg failed\n"); 388 } 389 brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d, err=%d\n", 390 ifp->ifidx, ifp->bsscfgidx, name, len, err); 391 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 392 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 393 394 mutex_unlock(&drvr->proto_block); 395 return err; 396 } 397 398 s32 399 brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, const char *name, u32 data) 400 { 401 __le32 data_le = cpu_to_le32(data); 402 403 return brcmf_fil_bsscfg_data_set(ifp, name, &data_le, 404 sizeof(data_le)); 405 } 406 407 s32 408 brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, const char *name, u32 *data) 409 { 410 __le32 data_le = cpu_to_le32(*data); 411 s32 err; 412 413 err = brcmf_fil_bsscfg_data_get(ifp, name, &data_le, 414 sizeof(data_le)); 415 if (err == 0) 416 *data = le32_to_cpu(data_le); 417 return err; 418 } 419 420 static u32 brcmf_create_xtlv(const char *name, u16 id, char *data, u32 len, 421 char *buf, u32 buflen) 422 { 423 u32 iolen; 424 u32 nmlen; 425 426 nmlen = strlen(name) + 1; 427 iolen = nmlen + brcmf_xtlv_data_size(len, BRCMF_XTLV_OPTION_ALIGN32); 428 429 if (iolen > buflen) { 430 brcmf_err("buffer is too short\n"); 431 return 0; 432 } 433 434 memcpy(buf, name, nmlen); 435 brcmf_xtlv_pack_header((void *)(buf + nmlen), id, len, data, 436 BRCMF_XTLV_OPTION_ALIGN32); 437 438 return iolen; 439 } 440 441 s32 brcmf_fil_xtlv_data_set(struct brcmf_if *ifp, const char *name, u16 id, 442 void *data, u32 len) 443 { 444 struct brcmf_pub *drvr = ifp->drvr; 445 s32 err; 446 u32 buflen; 447 448 mutex_lock(&drvr->proto_block); 449 450 brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u\n", ifp->ifidx, name, 451 id, len); 452 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 453 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 454 455 buflen = brcmf_create_xtlv(name, id, data, len, 456 drvr->proto_buf, sizeof(drvr->proto_buf)); 457 if (buflen) { 458 err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, 459 buflen, true); 460 } else { 461 err = -EPERM; 462 bphy_err(drvr, "Creating xtlv failed\n"); 463 } 464 465 mutex_unlock(&drvr->proto_block); 466 return err; 467 } 468 469 s32 brcmf_fil_xtlv_data_get(struct brcmf_if *ifp, const char *name, u16 id, 470 void *data, u32 len) 471 { 472 struct brcmf_pub *drvr = ifp->drvr; 473 s32 err; 474 u32 buflen; 475 476 mutex_lock(&drvr->proto_block); 477 478 buflen = brcmf_create_xtlv(name, id, data, len, 479 drvr->proto_buf, sizeof(drvr->proto_buf)); 480 if (buflen) { 481 err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, 482 buflen, false); 483 if (err == 0) 484 memcpy(data, drvr->proto_buf, len); 485 } else { 486 err = -EPERM; 487 bphy_err(drvr, "Creating bsscfg failed\n"); 488 } 489 brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u, err=%d\n", 490 ifp->ifidx, name, id, len, err); 491 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 492 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 493 494 mutex_unlock(&drvr->proto_block); 495 return err; 496 } 497 498 s32 brcmf_fil_xtlv_int_set(struct brcmf_if *ifp, const char *name, u16 id, u32 data) 499 { 500 __le32 data_le = cpu_to_le32(data); 501 502 return brcmf_fil_xtlv_data_set(ifp, name, id, &data_le, 503 sizeof(data_le)); 504 } 505 506 s32 brcmf_fil_xtlv_int_get(struct brcmf_if *ifp, const char *name, u16 id, u32 *data) 507 { 508 __le32 data_le = cpu_to_le32(*data); 509 s32 err; 510 511 err = brcmf_fil_xtlv_data_get(ifp, name, id, &data_le, sizeof(data_le)); 512 if (err == 0) 513 *data = le32_to_cpu(data_le); 514 return err; 515 } 516 517 s32 brcmf_fil_xtlv_int8_get(struct brcmf_if *ifp, const char *name, u16 id, u8 *data) 518 { 519 return brcmf_fil_xtlv_data_get(ifp, name, id, data, sizeof(*data)); 520 } 521 522 s32 brcmf_fil_xtlv_int16_get(struct brcmf_if *ifp, const char *name, u16 id, u16 *data) 523 { 524 __le16 data_le = cpu_to_le16(*data); 525 s32 err; 526 527 err = brcmf_fil_xtlv_data_get(ifp, name, id, &data_le, sizeof(data_le)); 528 if (err == 0) 529 *data = le16_to_cpu(data_le); 530 return err; 531 } 532 533