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 BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_iovar_data_set); 243 244 s32 245 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, const char *name, void *data, 246 u32 len) 247 { 248 struct brcmf_pub *drvr = ifp->drvr; 249 s32 err; 250 u32 buflen; 251 252 mutex_lock(&drvr->proto_block); 253 254 buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, 255 sizeof(drvr->proto_buf)); 256 if (buflen) { 257 err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, 258 buflen, false); 259 if (err == 0) 260 memcpy(data, drvr->proto_buf, len); 261 } else { 262 err = -EPERM; 263 bphy_err(drvr, "Creating iovar failed\n"); 264 } 265 266 brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d, err=%d\n", ifp->ifidx, name, 267 len, err); 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 275 s32 276 brcmf_fil_iovar_int_set(struct brcmf_if *ifp, const 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 283 s32 284 brcmf_fil_iovar_int_get(struct brcmf_if *ifp, const 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 295 static u32 296 brcmf_create_bsscfg(s32 bsscfgidx, const char *name, char *data, u32 datalen, 297 char *buf, u32 buflen) 298 { 299 const s8 *prefix = "bsscfg:"; 300 s8 *p; 301 u32 prefixlen; 302 u32 namelen; 303 u32 iolen; 304 __le32 bsscfgidx_le; 305 306 if (bsscfgidx == 0) 307 return brcmf_create_iovar(name, data, datalen, buf, buflen); 308 309 prefixlen = strlen(prefix); 310 namelen = strlen(name) + 1; /* length of iovar name + null */ 311 iolen = prefixlen + namelen + sizeof(bsscfgidx_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 bsscfgidx_le = cpu_to_le32(bsscfgidx); 330 memcpy(p, &bsscfgidx_le, sizeof(bsscfgidx_le)); 331 p += sizeof(bsscfgidx_le); 332 333 /* parameter buffer follows */ 334 if (datalen) 335 memcpy(p, data, datalen); 336 337 return iolen; 338 } 339 340 s32 341 brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, const 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, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx, 351 ifp->bsscfgidx, 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->bsscfgidx, 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 bphy_err(drvr, "Creating bsscfg failed\n"); 363 } 364 365 mutex_unlock(&drvr->proto_block); 366 return err; 367 } 368 369 s32 370 brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, const 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->bsscfgidx, 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 bphy_err(drvr, "Creating bsscfg failed\n"); 389 } 390 brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d, err=%d\n", 391 ifp->ifidx, ifp->bsscfgidx, name, len, err); 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 s32 400 brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, const char *name, u32 data) 401 { 402 __le32 data_le = cpu_to_le32(data); 403 404 return brcmf_fil_bsscfg_data_set(ifp, name, &data_le, 405 sizeof(data_le)); 406 } 407 408 s32 409 brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, const char *name, u32 *data) 410 { 411 __le32 data_le = cpu_to_le32(*data); 412 s32 err; 413 414 err = brcmf_fil_bsscfg_data_get(ifp, name, &data_le, 415 sizeof(data_le)); 416 if (err == 0) 417 *data = le32_to_cpu(data_le); 418 return err; 419 } 420 421 static u32 brcmf_create_xtlv(const char *name, u16 id, char *data, u32 len, 422 char *buf, u32 buflen) 423 { 424 u32 iolen; 425 u32 nmlen; 426 427 nmlen = strlen(name) + 1; 428 iolen = nmlen + brcmf_xtlv_data_size(len, BRCMF_XTLV_OPTION_ALIGN32); 429 430 if (iolen > buflen) { 431 brcmf_err("buffer is too short\n"); 432 return 0; 433 } 434 435 memcpy(buf, name, nmlen); 436 brcmf_xtlv_pack_header((void *)(buf + nmlen), id, len, data, 437 BRCMF_XTLV_OPTION_ALIGN32); 438 439 return iolen; 440 } 441 442 s32 brcmf_fil_xtlv_data_set(struct brcmf_if *ifp, const char *name, u16 id, 443 void *data, u32 len) 444 { 445 struct brcmf_pub *drvr = ifp->drvr; 446 s32 err; 447 u32 buflen; 448 449 mutex_lock(&drvr->proto_block); 450 451 brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u\n", ifp->ifidx, name, 452 id, len); 453 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 454 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 455 456 buflen = brcmf_create_xtlv(name, id, data, len, 457 drvr->proto_buf, sizeof(drvr->proto_buf)); 458 if (buflen) { 459 err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, 460 buflen, true); 461 } else { 462 err = -EPERM; 463 bphy_err(drvr, "Creating xtlv failed\n"); 464 } 465 466 mutex_unlock(&drvr->proto_block); 467 return err; 468 } 469 470 s32 brcmf_fil_xtlv_data_get(struct brcmf_if *ifp, const char *name, u16 id, 471 void *data, u32 len) 472 { 473 struct brcmf_pub *drvr = ifp->drvr; 474 s32 err; 475 u32 buflen; 476 477 mutex_lock(&drvr->proto_block); 478 479 buflen = brcmf_create_xtlv(name, id, data, len, 480 drvr->proto_buf, sizeof(drvr->proto_buf)); 481 if (buflen) { 482 err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, 483 buflen, false); 484 if (err == 0) 485 memcpy(data, drvr->proto_buf, len); 486 } else { 487 err = -EPERM; 488 bphy_err(drvr, "Creating bsscfg failed\n"); 489 } 490 brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u, err=%d\n", 491 ifp->ifidx, name, id, len, err); 492 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 493 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 494 495 mutex_unlock(&drvr->proto_block); 496 return err; 497 } 498 499 s32 brcmf_fil_xtlv_int_set(struct brcmf_if *ifp, const char *name, u16 id, u32 data) 500 { 501 __le32 data_le = cpu_to_le32(data); 502 503 return brcmf_fil_xtlv_data_set(ifp, name, id, &data_le, 504 sizeof(data_le)); 505 } 506 507 s32 brcmf_fil_xtlv_int_get(struct brcmf_if *ifp, const char *name, u16 id, u32 *data) 508 { 509 __le32 data_le = cpu_to_le32(*data); 510 s32 err; 511 512 err = brcmf_fil_xtlv_data_get(ifp, name, id, &data_le, sizeof(data_le)); 513 if (err == 0) 514 *data = le32_to_cpu(data_le); 515 return err; 516 } 517 518 s32 brcmf_fil_xtlv_int8_get(struct brcmf_if *ifp, const char *name, u16 id, u8 *data) 519 { 520 return brcmf_fil_xtlv_data_get(ifp, name, id, data, sizeof(*data)); 521 } 522 523 s32 brcmf_fil_xtlv_int16_get(struct brcmf_if *ifp, const char *name, u16 id, u16 *data) 524 { 525 __le16 data_le = cpu_to_le16(*data); 526 s32 err; 527 528 err = brcmf_fil_xtlv_data_get(ifp, name, id, &data_le, sizeof(data_le)); 529 if (err == 0) 530 *data = le16_to_cpu(data_le); 531 return err; 532 } 533 534