@@ -23,7 +23,8 @@ brcmfmac-objs += \
feature.o \
btcoex.o \
vendor.o \
- pno.o
+ pno.o \
+ xtlv.o
brcmfmac-$(CONFIG_BRCMFMAC_PROTO_BCDC) += \
bcdc.o \
fwsignal.o
@@ -15,6 +15,7 @@
#include "bus.h"
#include "debug.h"
#include "tracepoint.h"
+#include "xtlv.h"
#include "fwil.h"
#include "proto.h"
@@ -150,7 +151,8 @@ brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
mutex_lock(&ifp->drvr->proto_block);
err = brcmf_fil_cmd_data(ifp, cmd, data, len, false);
- brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
+ brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d, err=%d\n", ifp->ifidx, cmd,
+ len, err);
brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
@@ -260,7 +262,8 @@ brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data,
bphy_err(drvr, "Creating iovar failed\n");
}
- brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
+ brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d, err=%d\n", ifp->ifidx, name,
+ len, err);
brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
@@ -383,14 +386,13 @@ brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name,
err = -EPERM;
bphy_err(drvr, "Creating bsscfg failed\n");
}
- brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx,
- ifp->bsscfgidx, name, len);
+ brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d, err=%d\n",
+ ifp->ifidx, ifp->bsscfgidx, name, len, err);
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
@@ -414,3 +416,117 @@ brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data)
*data = le32_to_cpu(data_le);
return err;
}
+
+static u32 brcmf_create_xtlv(char *name, u16 id, char *data, u32 len,
+ char *buf, u32 buflen)
+{
+ u32 iolen;
+ u32 nmlen;
+
+ nmlen = strlen(name) + 1;
+ iolen = nmlen + brcmf_xtlv_data_size(len, BRCMF_XTLV_OPTION_ALIGN32);
+
+ if (iolen > buflen) {
+ brcmf_err("buffer is too short\n");
+ return 0;
+ }
+
+ memcpy(buf, name, nmlen);
+ brcmf_xtlv_pack_header((void *)(buf + nmlen), id, len, data,
+ BRCMF_XTLV_OPTION_ALIGN32);
+
+ return iolen;
+}
+
+s32 brcmf_fil_xtlv_data_set(struct brcmf_if *ifp, char *name, u16 id,
+ void *data, u32 len)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ s32 err;
+ u32 buflen;
+
+ mutex_lock(&drvr->proto_block);
+
+ brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u\n", ifp->ifidx, name,
+ id, len);
+ brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
+ min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
+
+ buflen = brcmf_create_xtlv(name, id, 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;
+ bphy_err(drvr, "Creating xtlv failed\n");
+ }
+
+ mutex_unlock(&drvr->proto_block);
+ return err;
+}
+
+s32 brcmf_fil_xtlv_data_get(struct brcmf_if *ifp, char *name, u16 id,
+ void *data, u32 len)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ s32 err;
+ u32 buflen;
+
+ mutex_lock(&drvr->proto_block);
+
+ buflen = brcmf_create_xtlv(name, id, 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;
+ bphy_err(drvr, "Creating bsscfg failed\n");
+ }
+ brcmf_dbg(FIL, "ifidx=%d, name=%s, id=%u, len=%u, err=%d\n",
+ ifp->ifidx, name, id, len, err);
+ 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_xtlv_int_set(struct brcmf_if *ifp, char *name, u16 id, u32 data)
+{
+ __le32 data_le = cpu_to_le32(data);
+
+ return brcmf_fil_xtlv_data_set(ifp, name, id, &data_le,
+ sizeof(data_le));
+}
+
+s32 brcmf_fil_xtlv_int_get(struct brcmf_if *ifp, char *name, u16 id, u32 *data)
+{
+ __le32 data_le = cpu_to_le32(*data);
+ s32 err;
+
+ err = brcmf_fil_xtlv_data_get(ifp, name, id, &data_le, sizeof(data_le));
+ if (err == 0)
+ *data = le32_to_cpu(data_le);
+ return err;
+}
+
+s32 brcmf_fil_xtlv_int8_get(struct brcmf_if *ifp, char *name, u16 id, u8 *data)
+{
+ return brcmf_fil_xtlv_data_get(ifp, name, id, data, sizeof(*data));
+}
+
+s32 brcmf_fil_xtlv_int16_get(struct brcmf_if *ifp, char *name, u16 id, u16 *data)
+{
+ __le16 data_le = cpu_to_le16(*data);
+ s32 err;
+
+ err = brcmf_fil_xtlv_data_get(ifp, name, id, &data_le, sizeof(data_le));
+ if (err == 0)
+ *data = le16_to_cpu(data_le);
+ return err;
+}
+
@@ -97,5 +97,13 @@ s32 brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, void *data,
u32 len);
s32 brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data);
s32 brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data);
+s32 brcmf_fil_xtlv_data_set(struct brcmf_if *ifp, char *name, u16 id,
+ void *data, u32 len);
+s32 brcmf_fil_xtlv_data_get(struct brcmf_if *ifp, char *name, u16 id,
+ void *data, u32 len);
+s32 brcmf_fil_xtlv_int_set(struct brcmf_if *ifp, char *name, u16 id, u32 data);
+s32 brcmf_fil_xtlv_int_get(struct brcmf_if *ifp, char *name, u16 id, u32 *data);
+s32 brcmf_fil_xtlv_int8_get(struct brcmf_if *ifp, char *name, u16 id, u8 *data);
+s32 brcmf_fil_xtlv_int16_get(struct brcmf_if *ifp, char *name, u16 id, u16 *data);
#endif /* _fwil_h_ */
new file mode 100644
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (c) 2019 Broadcom
+ */
+
+#include <asm/unaligned.h>
+#include <linux/string.h>
+#include <linux/bug.h>
+
+#include "xtlv.h"
+
+static int brcmf_xtlv_header_size(u16 opts)
+{
+ int len = (int)offsetof(struct brcmf_xtlv, data);
+
+ if (opts & BRCMF_XTLV_OPTION_IDU8)
+ --len;
+ if (opts & BRCMF_XTLV_OPTION_LENU8)
+ --len;
+
+ return len;
+}
+
+int brcmf_xtlv_data_size(int dlen, u16 opts)
+{
+ int hsz;
+
+ hsz = brcmf_xtlv_header_size(opts);
+ if (opts & BRCMF_XTLV_OPTION_ALIGN32)
+ return roundup(dlen + hsz, 4);
+
+ return dlen + hsz;
+}
+
+void brcmf_xtlv_pack_header(struct brcmf_xtlv *xtlv, u16 id, u16 len,
+ const u8 *data, u16 opts)
+{
+ u8 *data_buf;
+ u16 mask = BRCMF_XTLV_OPTION_IDU8 | BRCMF_XTLV_OPTION_LENU8;
+
+ if (!(opts & mask)) {
+ u8 *idp = (u8 *)xtlv;
+ u8 *lenp = idp + sizeof(xtlv->id);
+
+ put_unaligned_le16(id, idp);
+ put_unaligned_le16(len, lenp);
+ data_buf = lenp + sizeof(u16);
+ } else if ((opts & mask) == mask) { /* u8 id and u8 len */
+ u8 *idp = (u8 *)xtlv;
+ u8 *lenp = idp + 1;
+
+ *idp = (u8)id;
+ *lenp = (u8)len;
+ data_buf = lenp + sizeof(u8);
+ } else if (opts & BRCMF_XTLV_OPTION_IDU8) { /* u8 id, u16 len */
+ u8 *idp = (u8 *)xtlv;
+ u8 *lenp = idp + 1;
+
+ *idp = (u8)id;
+ put_unaligned_le16(len, lenp);
+ data_buf = lenp + sizeof(u16);
+ } else if (opts & BRCMF_XTLV_OPTION_LENU8) { /* u16 id, u8 len */
+ u8 *idp = (u8 *)xtlv;
+ u8 *lenp = idp + sizeof(u16);
+
+ put_unaligned_le16(id, idp);
+ *lenp = (u8)len;
+ data_buf = lenp + sizeof(u8);
+ } else {
+ WARN(true, "Unexpected xtlv option");
+ return;
+ }
+
+ if (opts & BRCMF_XTLV_OPTION_LENU8) {
+ WARN_ON(len > 0x00ff);
+ len &= 0xff;
+ }
+
+ if (data)
+ memcpy(data_buf, data, len);
+}
+
new file mode 100644
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (c) 2019 Broadcom
+ */
+#ifndef __BRCMF_XTLV_H
+#define __BRCMF_XTLV_H
+
+#include <linux/types.h>
+#include <linux/bits.h>
+
+/* bcm type(id), length, value with w/16 bit id/len. The structure below
+ * is nominal, and is used to support variable length id and type. See
+ * xtlv options below.
+ */
+struct brcmf_xtlv {
+ u16 id;
+ u16 len;
+ u8 data[0];
+};
+
+enum brcmf_xtlv_option {
+ BRCMF_XTLV_OPTION_ALIGN32 = BIT(0),
+ BRCMF_XTLV_OPTION_IDU8 = BIT(1),
+ BRCMF_XTLV_OPTION_LENU8 = BIT(2),
+};
+
+int brcmf_xtlv_data_size(int dlen, u16 opts);
+void brcmf_xtlv_pack_header(struct brcmf_xtlv *xtlv, u16 id, u16 len,
+ const u8 *data, u16 opts);
+
+#endif /* __BRCMF_XTLV_H */