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 brcmf_create_bsscfg(s32 bsscfgidx, const char *name, char *data, u32 datalen, 250 char *buf, u32 buflen) 251 { 252 const s8 *prefix = "bsscfg:"; 253 s8 *p; 254 u32 prefixlen; 255 u32 namelen; 256 u32 iolen; 257 __le32 bsscfgidx_le; 258 259 if (bsscfgidx == 0) 260 return brcmf_create_iovar(name, data, datalen, buf, buflen); 261 262 prefixlen = strlen(prefix); 263 namelen = strlen(name) + 1; /* length of iovar name + null */ 264 iolen = prefixlen + namelen + sizeof(bsscfgidx_le) + datalen; 265 266 if (buflen < iolen) { 267 brcmf_err("buffer is too short\n"); 268 return 0; 269 } 270 271 p = buf; 272 273 /* copy prefix, no null */ 274 memcpy(p, prefix, prefixlen); 275 p += prefixlen; 276 277 /* copy iovar name including null */ 278 memcpy(p, name, namelen); 279 p += namelen; 280 281 /* bss config index as first data */ 282 bsscfgidx_le = cpu_to_le32(bsscfgidx); 283 memcpy(p, &bsscfgidx_le, sizeof(bsscfgidx_le)); 284 p += sizeof(bsscfgidx_le); 285 286 /* parameter buffer follows */ 287 if (datalen) 288 memcpy(p, data, datalen); 289 290 return iolen; 291 } 292 293 s32 294 brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, const char *name, 295 void *data, u32 len) 296 { 297 struct brcmf_pub *drvr = ifp->drvr; 298 s32 err; 299 u32 buflen; 300 301 mutex_lock(&drvr->proto_block); 302 303 brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx, 304 ifp->bsscfgidx, name, len); 305 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 306 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 307 308 buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len, 309 drvr->proto_buf, sizeof(drvr->proto_buf)); 310 if (buflen) { 311 err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, 312 buflen, true); 313 } else { 314 err = -EPERM; 315 bphy_err(drvr, "Creating bsscfg failed\n"); 316 } 317 318 mutex_unlock(&drvr->proto_block); 319 return err; 320 } 321 BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_bsscfg_data_set); 322 323 s32 324 brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, const char *name, 325 void *data, u32 len) 326 { 327 struct brcmf_pub *drvr = ifp->drvr; 328 s32 err; 329 u32 buflen; 330 331 mutex_lock(&drvr->proto_block); 332 333 buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len, 334 drvr->proto_buf, sizeof(drvr->proto_buf)); 335 if (buflen) { 336 err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, 337 buflen, false); 338 if (err == 0) 339 memcpy(data, drvr->proto_buf, len); 340 } else { 341 err = -EPERM; 342 bphy_err(drvr, "Creating bsscfg failed\n"); 343 } 344 brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d, err=%d\n", 345 ifp->ifidx, ifp->bsscfgidx, name, len, err); 346 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 347 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 348 349 mutex_unlock(&drvr->proto_block); 350 return err; 351 } 352 BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_bsscfg_data_get); 353 354 static u32 brcmf_create_xtlv(const char *name, u16 id, char *data, u32 len, 355 char *buf, u32 buflen) 356 { 357 u32 iolen; 358 u32 nmlen; 359 360 nmlen = strlen(name) + 1; 361 iolen = nmlen + brcmf_xtlv_data_size(len, BRCMF_XTLV_OPTION_ALIGN32); 362 363 if (iolen > buflen) { 364 brcmf_err("buffer is too short\n"); 365 return 0; 366 } 367 368 memcpy(buf, name, nmlen); 369 brcmf_xtlv_pack_header((void *)(buf + nmlen), id, len, data, 370 BRCMF_XTLV_OPTION_ALIGN32); 371 372 return iolen; 373 } 374 375 s32 brcmf_fil_xtlv_data_set(struct brcmf_if *ifp, const char *name, u16 id, 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 brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u\n", ifp->ifidx, name, 385 id, len); 386 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 387 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 388 389 buflen = brcmf_create_xtlv(name, id, data, len, 390 drvr->proto_buf, sizeof(drvr->proto_buf)); 391 if (buflen) { 392 err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, 393 buflen, true); 394 } else { 395 err = -EPERM; 396 bphy_err(drvr, "Creating xtlv failed\n"); 397 } 398 399 mutex_unlock(&drvr->proto_block); 400 return err; 401 } 402 BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_xtlv_data_set); 403 404 s32 brcmf_fil_xtlv_data_get(struct brcmf_if *ifp, const char *name, u16 id, 405 void *data, u32 len) 406 { 407 struct brcmf_pub *drvr = ifp->drvr; 408 s32 err; 409 u32 buflen; 410 411 mutex_lock(&drvr->proto_block); 412 413 buflen = brcmf_create_xtlv(name, id, data, len, 414 drvr->proto_buf, sizeof(drvr->proto_buf)); 415 if (buflen) { 416 err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, 417 buflen, false); 418 if (err == 0) 419 memcpy(data, drvr->proto_buf, len); 420 } else { 421 err = -EPERM; 422 bphy_err(drvr, "Creating bsscfg failed\n"); 423 } 424 brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u, err=%d\n", 425 ifp->ifidx, name, id, len, err); 426 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 427 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 428 429 mutex_unlock(&drvr->proto_block); 430 return err; 431 } 432 BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_xtlv_data_get);