mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-23 11:29:53 +07:00
c94b7e6cb2
The users of the fwil put an error message in the log so there is no need to do the same in the lower level functions in fwil when the firmware on the device returns an error. Some errors can be ignored for the driver to function and this will avoid driver users to point at the low-level error message as potential bug. Reviewed-by: Hante Meuleman <meuleman@broadcom.com> Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
349 lines
8.0 KiB
C
349 lines
8.0 KiB
C
/*
|
|
* Copyright (c) 2012 Broadcom Corporation
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
/* FWIL is the Firmware Interface Layer. In this module the support functions
|
|
* are located to set and get variables to and from the firmware.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/netdevice.h>
|
|
#include <brcmu_utils.h>
|
|
#include <brcmu_wifi.h>
|
|
#include "dhd.h"
|
|
#include "dhd_bus.h"
|
|
#include "dhd_dbg.h"
|
|
#include "tracepoint.h"
|
|
#include "fwil.h"
|
|
#include "proto.h"
|
|
|
|
|
|
#define MAX_HEX_DUMP_LEN 64
|
|
|
|
|
|
static s32
|
|
brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
|
|
{
|
|
struct brcmf_pub *drvr = ifp->drvr;
|
|
s32 err;
|
|
|
|
if (drvr->bus_if->state != BRCMF_BUS_DATA) {
|
|
brcmf_err("bus is down. we have nothing to do.\n");
|
|
return -EIO;
|
|
}
|
|
|
|
if (data != NULL)
|
|
len = min_t(uint, len, BRCMF_DCMD_MAXLEN);
|
|
if (set)
|
|
err = brcmf_proto_set_dcmd(drvr, ifp->ifidx, cmd, data, len);
|
|
else
|
|
err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd, data, len);
|
|
|
|
if (err >= 0)
|
|
err = 0;
|
|
else
|
|
brcmf_dbg(FIL, "Failed err=%d\n", err);
|
|
|
|
return err;
|
|
}
|
|
|
|
s32
|
|
brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
|
|
{
|
|
s32 err;
|
|
|
|
mutex_lock(&ifp->drvr->proto_block);
|
|
|
|
brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len);
|
|
brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
|
|
min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
|
|
|
|
err = brcmf_fil_cmd_data(ifp, cmd, data, len, true);
|
|
mutex_unlock(&ifp->drvr->proto_block);
|
|
|
|
return err;
|
|
}
|
|
|
|
s32
|
|
brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
|
|
{
|
|
s32 err;
|
|
|
|
mutex_lock(&ifp->drvr->proto_block);
|
|
err = brcmf_fil_cmd_data(ifp, cmd, data, len, false);
|
|
|
|
brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len);
|
|
brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
|
|
min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
|
|
|
|
mutex_unlock(&ifp->drvr->proto_block);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
s32
|
|
brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data)
|
|
{
|
|
s32 err;
|
|
__le32 data_le = cpu_to_le32(data);
|
|
|
|
mutex_lock(&ifp->drvr->proto_block);
|
|
brcmf_dbg(FIL, "cmd=%d, value=%d\n", cmd, data);
|
|
err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true);
|
|
mutex_unlock(&ifp->drvr->proto_block);
|
|
|
|
return err;
|
|
}
|
|
|
|
s32
|
|
brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data)
|
|
{
|
|
s32 err;
|
|
__le32 data_le = cpu_to_le32(*data);
|
|
|
|
mutex_lock(&ifp->drvr->proto_block);
|
|
err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false);
|
|
mutex_unlock(&ifp->drvr->proto_block);
|
|
*data = le32_to_cpu(data_le);
|
|
brcmf_dbg(FIL, "cmd=%d, value=%d\n", cmd, *data);
|
|
|
|
return err;
|
|
}
|
|
|
|
static u32
|
|
brcmf_create_iovar(char *name, const char *data, u32 datalen,
|
|
char *buf, u32 buflen)
|
|
{
|
|
u32 len;
|
|
|
|
len = strlen(name) + 1;
|
|
|
|
if ((len + datalen) > buflen)
|
|
return 0;
|
|
|
|
memcpy(buf, name, len);
|
|
|
|
/* append data onto the end of the name string */
|
|
if (data && datalen)
|
|
memcpy(&buf[len], data, datalen);
|
|
|
|
return len + datalen;
|
|
}
|
|
|
|
|
|
s32
|
|
brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data,
|
|
u32 len)
|
|
{
|
|
struct brcmf_pub *drvr = ifp->drvr;
|
|
s32 err;
|
|
u32 buflen;
|
|
|
|
mutex_lock(&drvr->proto_block);
|
|
|
|
brcmf_dbg(FIL, "name=%s, len=%d\n", name, len);
|
|
brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
|
|
min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
|
|
|
|
buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
|
|
sizeof(drvr->proto_buf));
|
|
if (buflen) {
|
|
err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
|
|
buflen, true);
|
|
} else {
|
|
err = -EPERM;
|
|
brcmf_err("Creating iovar failed\n");
|
|
}
|
|
|
|
mutex_unlock(&drvr->proto_block);
|
|
return err;
|
|
}
|
|
|
|
s32
|
|
brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data,
|
|
u32 len)
|
|
{
|
|
struct brcmf_pub *drvr = ifp->drvr;
|
|
s32 err;
|
|
u32 buflen;
|
|
|
|
mutex_lock(&drvr->proto_block);
|
|
|
|
buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
|
|
sizeof(drvr->proto_buf));
|
|
if (buflen) {
|
|
err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
|
|
buflen, false);
|
|
if (err == 0)
|
|
memcpy(data, drvr->proto_buf, len);
|
|
} else {
|
|
err = -EPERM;
|
|
brcmf_err("Creating iovar failed\n");
|
|
}
|
|
|
|
brcmf_dbg(FIL, "name=%s, len=%d\n", name, len);
|
|
brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
|
|
min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
|
|
|
|
mutex_unlock(&drvr->proto_block);
|
|
return err;
|
|
}
|
|
|
|
s32
|
|
brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data)
|
|
{
|
|
__le32 data_le = cpu_to_le32(data);
|
|
|
|
return brcmf_fil_iovar_data_set(ifp, name, &data_le, sizeof(data_le));
|
|
}
|
|
|
|
s32
|
|
brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data)
|
|
{
|
|
__le32 data_le = cpu_to_le32(*data);
|
|
s32 err;
|
|
|
|
err = brcmf_fil_iovar_data_get(ifp, name, &data_le, sizeof(data_le));
|
|
if (err == 0)
|
|
*data = le32_to_cpu(data_le);
|
|
return err;
|
|
}
|
|
|
|
static u32
|
|
brcmf_create_bsscfg(s32 bssidx, char *name, char *data, u32 datalen, char *buf,
|
|
u32 buflen)
|
|
{
|
|
const s8 *prefix = "bsscfg:";
|
|
s8 *p;
|
|
u32 prefixlen;
|
|
u32 namelen;
|
|
u32 iolen;
|
|
__le32 bssidx_le;
|
|
|
|
if (bssidx == 0)
|
|
return brcmf_create_iovar(name, data, datalen, buf, buflen);
|
|
|
|
prefixlen = strlen(prefix);
|
|
namelen = strlen(name) + 1; /* lengh of iovar name + null */
|
|
iolen = prefixlen + namelen + sizeof(bssidx_le) + datalen;
|
|
|
|
if (buflen < iolen) {
|
|
brcmf_err("buffer is too short\n");
|
|
return 0;
|
|
}
|
|
|
|
p = buf;
|
|
|
|
/* copy prefix, no null */
|
|
memcpy(p, prefix, prefixlen);
|
|
p += prefixlen;
|
|
|
|
/* copy iovar name including null */
|
|
memcpy(p, name, namelen);
|
|
p += namelen;
|
|
|
|
/* bss config index as first data */
|
|
bssidx_le = cpu_to_le32(bssidx);
|
|
memcpy(p, &bssidx_le, sizeof(bssidx_le));
|
|
p += sizeof(bssidx_le);
|
|
|
|
/* parameter buffer follows */
|
|
if (datalen)
|
|
memcpy(p, data, datalen);
|
|
|
|
return iolen;
|
|
}
|
|
|
|
s32
|
|
brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name,
|
|
void *data, u32 len)
|
|
{
|
|
struct brcmf_pub *drvr = ifp->drvr;
|
|
s32 err;
|
|
u32 buflen;
|
|
|
|
mutex_lock(&drvr->proto_block);
|
|
|
|
brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len);
|
|
brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
|
|
min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
|
|
|
|
buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len,
|
|
drvr->proto_buf, sizeof(drvr->proto_buf));
|
|
if (buflen) {
|
|
err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
|
|
buflen, true);
|
|
} else {
|
|
err = -EPERM;
|
|
brcmf_err("Creating bsscfg failed\n");
|
|
}
|
|
|
|
mutex_unlock(&drvr->proto_block);
|
|
return err;
|
|
}
|
|
|
|
s32
|
|
brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name,
|
|
void *data, u32 len)
|
|
{
|
|
struct brcmf_pub *drvr = ifp->drvr;
|
|
s32 err;
|
|
u32 buflen;
|
|
|
|
mutex_lock(&drvr->proto_block);
|
|
|
|
buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len,
|
|
drvr->proto_buf, sizeof(drvr->proto_buf));
|
|
if (buflen) {
|
|
err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
|
|
buflen, false);
|
|
if (err == 0)
|
|
memcpy(data, drvr->proto_buf, len);
|
|
} else {
|
|
err = -EPERM;
|
|
brcmf_err("Creating bsscfg failed\n");
|
|
}
|
|
brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len);
|
|
brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
|
|
min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
|
|
|
|
mutex_unlock(&drvr->proto_block);
|
|
return err;
|
|
|
|
}
|
|
|
|
s32
|
|
brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data)
|
|
{
|
|
__le32 data_le = cpu_to_le32(data);
|
|
|
|
return brcmf_fil_bsscfg_data_set(ifp, name, &data_le,
|
|
sizeof(data_le));
|
|
}
|
|
|
|
s32
|
|
brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data)
|
|
{
|
|
__le32 data_le = cpu_to_le32(*data);
|
|
s32 err;
|
|
|
|
err = brcmf_fil_bsscfg_data_get(ifp, name, &data_le,
|
|
sizeof(data_le));
|
|
if (err == 0)
|
|
*data = le32_to_cpu(data_le);
|
|
return err;
|
|
}
|