Path: blob/main/sys/contrib/dev/broadcom/brcm80211/brcmfmac/fwil.c
178665 views
// SPDX-License-Identifier: ISC1/*2* Copyright (c) 2012 Broadcom Corporation3*/45/* FWIL is the Firmware Interface Layer. In this module the support functions6* are located to set and get variables to and from the firmware.7*/89#include <linux/kernel.h>10#include <linux/netdevice.h>11#include <brcmu_utils.h>12#include <brcmu_wifi.h>13#include "core.h"14#include "bus.h"15#include "debug.h"16#include "tracepoint.h"17#include "xtlv.h"18#include "fwil.h"19#include "proto.h"202122#define MAX_HEX_DUMP_LEN 642324#ifdef DEBUG25static 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};8081static const char *brcmf_fil_get_errstr(u32 err)82{83if (err >= ARRAY_SIZE(brcmf_fil_errstr))84return "(unknown)";8586return brcmf_fil_errstr[err];87}88#else89static const char *brcmf_fil_get_errstr(u32 err)90{91return "";92}93#endif /* DEBUG */9495static s3296brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)97{98struct brcmf_pub *drvr = ifp->drvr;99s32 err, fwerr;100101if (drvr->bus_if->state != BRCMF_BUS_UP) {102bphy_err(drvr, "bus is down. we have nothing to do.\n");103return -EIO;104}105106if (data != NULL)107len = min_t(uint, len, BRCMF_DCMD_MAXLEN);108if (set)109err = brcmf_proto_set_dcmd(drvr, ifp->ifidx, cmd,110data, len, &fwerr);111else112err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd,113data, len, &fwerr);114115if (err) {116brcmf_dbg(FIL, "Failed: error=%d\n", err);117} else if (fwerr < 0) {118brcmf_dbg(FIL, "Firmware error: %s (%d)\n",119brcmf_fil_get_errstr((u32)(-fwerr)), fwerr);120err = -EBADE;121}122if (ifp->fwil_fwerr)123return fwerr;124125return err;126}127128s32129brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)130{131s32 err;132133mutex_lock(&ifp->drvr->proto_block);134135brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);136brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,137min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");138139err = brcmf_fil_cmd_data(ifp, cmd, data, len, true);140mutex_unlock(&ifp->drvr->proto_block);141142return err;143}144BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_cmd_data_set);145146s32147brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)148{149s32 err;150151mutex_lock(&ifp->drvr->proto_block);152err = brcmf_fil_cmd_data(ifp, cmd, data, len, false);153154brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d, err=%d\n", ifp->ifidx, cmd,155len, err);156brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,157min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");158159mutex_unlock(&ifp->drvr->proto_block);160161return err;162}163BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_cmd_data_get);164165static u32166brcmf_create_iovar(const char *name, const char *data, u32 datalen,167char *buf, u32 buflen)168{169u32 len;170171len = strlen(name) + 1;172173if ((len + datalen) > buflen)174return 0;175176memcpy(buf, name, len);177178/* append data onto the end of the name string */179if (data && datalen)180memcpy(&buf[len], data, datalen);181182return len + datalen;183}184185186s32187brcmf_fil_iovar_data_set(struct brcmf_if *ifp, const char *name, const void *data,188u32 len)189{190struct brcmf_pub *drvr = ifp->drvr;191s32 err;192u32 buflen;193194mutex_lock(&drvr->proto_block);195196brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);197brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,198min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");199200buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,201sizeof(drvr->proto_buf));202if (buflen) {203err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,204buflen, true);205} else {206err = -EPERM;207bphy_err(drvr, "Creating iovar failed\n");208}209210mutex_unlock(&drvr->proto_block);211return err;212}213BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_iovar_data_set);214215s32216brcmf_fil_iovar_data_get(struct brcmf_if *ifp, const char *name, void *data,217u32 len)218{219struct brcmf_pub *drvr = ifp->drvr;220s32 err;221u32 buflen;222223mutex_lock(&drvr->proto_block);224225buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,226sizeof(drvr->proto_buf));227if (buflen) {228err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,229buflen, false);230if (err == 0)231memcpy(data, drvr->proto_buf, len);232} else {233err = -EPERM;234bphy_err(drvr, "Creating iovar failed\n");235}236237brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d, err=%d\n", ifp->ifidx, name,238len, err);239brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,240min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");241242mutex_unlock(&drvr->proto_block);243return err;244}245BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_iovar_data_get);246247static u32248#if defined(__linux__)249brcmf_create_bsscfg(s32 bsscfgidx, const char *name, char *data, u32 datalen,250#elif defined(__FreeBSD__)251brcmf_create_bsscfg(s32 bsscfgidx, const char *name, const char *data, u32 datalen,252#endif253char *buf, u32 buflen)254{255const s8 *prefix = "bsscfg:";256s8 *p;257u32 prefixlen;258u32 namelen;259u32 iolen;260__le32 bsscfgidx_le;261262if (bsscfgidx == 0)263return brcmf_create_iovar(name, data, datalen, buf, buflen);264265prefixlen = strlen(prefix);266namelen = strlen(name) + 1; /* length of iovar name + null */267iolen = prefixlen + namelen + sizeof(bsscfgidx_le) + datalen;268269if (buflen < iolen) {270brcmf_err("buffer is too short\n");271return 0;272}273274p = buf;275276/* copy prefix, no null */277memcpy(p, prefix, prefixlen);278p += prefixlen;279280/* copy iovar name including null */281memcpy(p, name, namelen);282p += namelen;283284/* bss config index as first data */285bsscfgidx_le = cpu_to_le32(bsscfgidx);286memcpy(p, &bsscfgidx_le, sizeof(bsscfgidx_le));287p += sizeof(bsscfgidx_le);288289/* parameter buffer follows */290if (datalen)291memcpy(p, data, datalen);292293return iolen;294}295296s32297brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, const char *name,298#if defined(__linux__)299void *data, u32 len)300#elif defined(__FreeBSD__)301const void *data, u32 len)302#endif303{304struct brcmf_pub *drvr = ifp->drvr;305s32 err;306u32 buflen;307308mutex_lock(&drvr->proto_block);309310brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx,311ifp->bsscfgidx, name, len);312brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,313min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");314315buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,316drvr->proto_buf, sizeof(drvr->proto_buf));317if (buflen) {318err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,319buflen, true);320} else {321err = -EPERM;322bphy_err(drvr, "Creating bsscfg failed\n");323}324325mutex_unlock(&drvr->proto_block);326return err;327}328BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_bsscfg_data_set);329330s32331brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, const char *name,332void *data, u32 len)333{334struct brcmf_pub *drvr = ifp->drvr;335s32 err;336u32 buflen;337338mutex_lock(&drvr->proto_block);339340buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,341drvr->proto_buf, sizeof(drvr->proto_buf));342if (buflen) {343err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,344buflen, false);345if (err == 0)346memcpy(data, drvr->proto_buf, len);347} else {348err = -EPERM;349bphy_err(drvr, "Creating bsscfg failed\n");350}351brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d, err=%d\n",352ifp->ifidx, ifp->bsscfgidx, name, len, err);353brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,354min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");355356mutex_unlock(&drvr->proto_block);357return err;358}359BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_bsscfg_data_get);360361static u32 brcmf_create_xtlv(const char *name, u16 id, char *data, u32 len,362char *buf, u32 buflen)363{364u32 iolen;365u32 nmlen;366367nmlen = strlen(name) + 1;368iolen = nmlen + brcmf_xtlv_data_size(len, BRCMF_XTLV_OPTION_ALIGN32);369370if (iolen > buflen) {371brcmf_err("buffer is too short\n");372return 0;373}374375memcpy(buf, name, nmlen);376brcmf_xtlv_pack_header((void *)(buf + nmlen), id, len, data,377BRCMF_XTLV_OPTION_ALIGN32);378379return iolen;380}381382s32 brcmf_fil_xtlv_data_set(struct brcmf_if *ifp, const char *name, u16 id,383void *data, u32 len)384{385struct brcmf_pub *drvr = ifp->drvr;386s32 err;387u32 buflen;388389mutex_lock(&drvr->proto_block);390391brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u\n", ifp->ifidx, name,392id, len);393brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,394min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");395396buflen = brcmf_create_xtlv(name, id, data, len,397drvr->proto_buf, sizeof(drvr->proto_buf));398if (buflen) {399err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,400buflen, true);401} else {402err = -EPERM;403bphy_err(drvr, "Creating xtlv failed\n");404}405406mutex_unlock(&drvr->proto_block);407return err;408}409BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_xtlv_data_set);410411s32 brcmf_fil_xtlv_data_get(struct brcmf_if *ifp, const char *name, u16 id,412void *data, u32 len)413{414struct brcmf_pub *drvr = ifp->drvr;415s32 err;416u32 buflen;417418mutex_lock(&drvr->proto_block);419420buflen = brcmf_create_xtlv(name, id, data, len,421drvr->proto_buf, sizeof(drvr->proto_buf));422if (buflen) {423err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,424buflen, false);425if (err == 0)426memcpy(data, drvr->proto_buf, len);427} else {428err = -EPERM;429bphy_err(drvr, "Creating bsscfg failed\n");430}431brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u, err=%d\n",432ifp->ifidx, name, id, len, err);433brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,434min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");435436mutex_unlock(&drvr->proto_block);437return err;438}439BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_xtlv_data_get);440441442