Commit a7dd0ac9 authored by Arend van Spriel's avatar Arend van Spriel Committed by Kalle Valo
Browse files

brcmfmac: add xtlv support to firmware interface layer



Newer firmware API require commands to use xtlv format. Add support
for that in the firmware interface layer.

Reviewed-by: default avatarHante Meuleman <hante.meuleman@broadcom.com>
Reviewed-by: default avatarPieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
Reviewed-by: default avatarFranky Lin <franky.lin@broadcom.com>
Signed-off-by: default avatarArend van Spriel <arend.vanspriel@broadcom.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/1627505434-9544-4-git-send-email-arend.vanspriel@broadcom.com
parent 8e73facb
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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
+121 −5
Original line number Diff line number Diff line
@@ -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;
}
+8 −0
Original line number Diff line number Diff line
@@ -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_ */
+82 −0
Original line number Diff line number Diff line
// 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);
}
+31 −0
Original line number Diff line number Diff line
// 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 */