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