433 lines · c
1// SPDX-License-Identifier: ISC2/*3 * Copyright (c) 2012 Broadcom Corporation4 */5 6/* FWIL is the Firmware Interface Layer. In this module the support functions7 * 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 6424 25#ifdef DEBUG26static 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 82static 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#else90static const char *brcmf_fil_get_errstr(u32 err)91{92 return "";93}94#endif /* DEBUG */95 96static s3297brcmf_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 else113 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 129s32130brcmf_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}145BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_cmd_data_set);146 147s32148brcmf_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}164BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_cmd_data_get);165 166static u32167brcmf_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 187s32188brcmf_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}214BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_iovar_data_set);215 216s32217brcmf_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}246BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_iovar_data_get);247 248static u32249brcmf_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 293s32294brcmf_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}321BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_bsscfg_data_set);322 323s32324brcmf_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}352BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_bsscfg_data_get);353 354static 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 375s32 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}402BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_xtlv_data_set);403 404s32 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}432BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_xtlv_data_get);433