@@ -20,6 +20,7 @@ brcmfmac-objs += \
common.o \
core.o \
firmware.o \
+ fwvid.o \
feature.o \
btcoex.o \
vendor.o \
@@ -47,3 +48,9 @@ brcmfmac-$(CONFIG_OF) += \
of.o
brcmfmac-$(CONFIG_DMI) += \
dmi.o
+
+ifeq ($(CONFIG_BRCMFMAC),m)
+obj-m += wcc/
+else
+brcmfmac-$(CONFIG_BRCMFMAC) += wcc/core.o
+endif
@@ -155,7 +155,9 @@ struct brcmf_bus_stats {
* @fwvid: firmware vendor-support identifier of the device.
* @always_use_fws_queue: bus wants use queue also when fwsignal is inactive.
* @wowl_supported: is wowl supported by bus driver.
+ * @ops: callbacks for this bus instance.
* @msgbuf: msgbuf protocol parameters provided by bus layer.
+ * @list: member used to add this bus instance to linked list.
*/
struct brcmf_bus {
union {
@@ -177,6 +179,8 @@ struct brcmf_bus {
const struct brcmf_bus_ops *ops;
struct brcmf_bus_msgbuf *msgbuf;
+
+ struct list_head list;
};
/*
@@ -18,6 +18,7 @@
#include "core.h"
#include "bus.h"
+#include "fwvid.h"
#include "debug.h"
#include "fwil_types.h"
#include "p2p.h"
@@ -1332,6 +1333,12 @@ int brcmf_attach(struct device *dev)
/* Link to bus module */
drvr->hdrlen = 0;
+ ret = brcmf_fwvid_attach(drvr);
+ if (ret != 0) {
+ bphy_err(drvr, "brcmf_fwvid_attach failed\n");
+ goto fail;
+ }
+
/* Attach and link in the protocol */
ret = brcmf_proto_attach(drvr);
if (ret != 0) {
@@ -1443,6 +1450,8 @@ void brcmf_detach(struct device *dev)
brcmf_cfg80211_detach(drvr->config);
drvr->config = NULL;
}
+
+ brcmf_fwvid_detach(drvr);
}
void brcmf_free(struct device *dev)
@@ -137,6 +137,8 @@ struct brcmf_pub {
u8 clmver[BRCMF_DCMD_SMLEN];
u8 sta_mac_idx;
+ const struct brcmf_fwvid_ops *vops;
+ void *vdata;
};
/* forward declarations */
new file mode 100644
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (c) 2022 Broadcom Corporation
+ */
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/list.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/printk.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+
+#include "core.h"
+#include "bus.h"
+#include "debug.h"
+#include "fwvid.h"
+
+#include "wcc/vops.h"
+
+struct brcmf_fwvid_entry {
+ const char *name;
+ const struct brcmf_fwvid_ops *vops;
+ struct list_head drvr_list;
+#if IS_MODULE(CONFIG_BRCMFMAC)
+ struct module *vmod;
+ struct completion reg_done;
+#endif
+};
+
+static DEFINE_MUTEX(fwvid_list_lock);
+
+#if IS_MODULE(CONFIG_BRCMFMAC)
+#define FWVID_ENTRY_INIT(_vid, _name) \
+ [BRCMF_FWVENDOR_ ## _vid] = { \
+ .name = #_name, \
+ .reg_done = COMPLETION_INITIALIZER(fwvid_list[BRCMF_FWVENDOR_ ## _vid].reg_done), \
+ .drvr_list = LIST_HEAD_INIT(fwvid_list[BRCMF_FWVENDOR_ ## _vid].drvr_list), \
+ }
+#else
+#define FWVID_ENTRY_INIT(_vid, _name) \
+ [BRCMF_FWVENDOR_ ## _vid] = { \
+ .name = #_name, \
+ .drvr_list = LIST_HEAD_INIT(fwvid_list[BRCMF_FWVENDOR_ ## _vid].drvr_list), \
+ .vops = _vid ## _VOPS \
+ }
+#endif /* IS_MODULE(CONFIG_BRCMFMAC) */
+
+static struct brcmf_fwvid_entry fwvid_list[BRCMF_FWVENDOR_NUM] = {
+ FWVID_ENTRY_INIT(WCC, wcc),
+};
+
+#if IS_MODULE(CONFIG_BRCMFMAC)
+static int brcmf_fwvid_request_module(enum brcmf_fwvendor fwvid)
+{
+ int ret;
+
+ if (!fwvid_list[fwvid].vmod) {
+ struct completion *reg_done = &fwvid_list[fwvid].reg_done;
+
+ mutex_unlock(&fwvid_list_lock);
+
+ ret = request_module("brcmfmac-%s", fwvid_list[fwvid].name);
+ if (ret)
+ goto fail;
+
+ ret = wait_for_completion_interruptible(reg_done);
+ if (ret)
+ goto fail;
+
+ mutex_lock(&fwvid_list_lock);
+ }
+ return 0;
+
+fail:
+ brcmf_err("mod=%s: failed %d\n", fwvid_list[fwvid].name, ret);
+ return ret;
+}
+
+int brcmf_fwvid_register_vendor(enum brcmf_fwvendor fwvid, struct module *vmod,
+ const struct brcmf_fwvid_ops *vops)
+{
+ if (fwvid >= BRCMF_FWVENDOR_NUM)
+ return -ERANGE;
+
+ if (WARN_ON(!vmod) || WARN_ON(!vops) ||
+ WARN_ON(!vops->attach) || WARN_ON(!vops->detach))
+ return -EINVAL;
+
+ if (WARN_ON(fwvid_list[fwvid].vmod))
+ return -EEXIST;
+
+ brcmf_dbg(TRACE, "mod=%s: enter\n", fwvid_list[fwvid].name);
+
+ mutex_lock(&fwvid_list_lock);
+
+ fwvid_list[fwvid].vmod = vmod;
+ fwvid_list[fwvid].vops = vops;
+
+ mutex_unlock(&fwvid_list_lock);
+
+ complete_all(&fwvid_list[fwvid].reg_done);
+
+ return 0;
+}
+EXPORT_SYMBOL(brcmf_fwvid_register_vendor);
+
+int brcmf_fwvid_unregister_vendor(enum brcmf_fwvendor fwvid, struct module *mod)
+{
+ struct brcmf_bus *bus, *tmp;
+
+ if (fwvid >= BRCMF_FWVENDOR_NUM)
+ return -ERANGE;
+
+ if (WARN_ON(fwvid_list[fwvid].vmod != mod))
+ return -ENOENT;
+
+ mutex_lock(&fwvid_list_lock);
+
+ list_for_each_entry_safe(bus, tmp, &fwvid_list[fwvid].drvr_list, list) {
+ mutex_unlock(&fwvid_list_lock);
+
+ brcmf_dbg(INFO, "mod=%s: removing %s\n", fwvid_list[fwvid].name,
+ dev_name(bus->dev));
+ brcmf_bus_remove(bus);
+
+ mutex_lock(&fwvid_list_lock);
+ }
+
+ fwvid_list[fwvid].vmod = NULL;
+ fwvid_list[fwvid].vops = NULL;
+ reinit_completion(&fwvid_list[fwvid].reg_done);
+
+ brcmf_dbg(TRACE, "mod=%s: exit\n", fwvid_list[fwvid].name);
+ mutex_unlock(&fwvid_list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(brcmf_fwvid_unregister_vendor);
+#else
+static inline int brcmf_fwvid_request_module(enum brcmf_fwvendor fwvid)
+{
+ return 0;
+}
+#endif
+
+int brcmf_fwvid_attach_ops(struct brcmf_pub *drvr)
+{
+ enum brcmf_fwvendor fwvid = drvr->bus_if->fwvid;
+ int ret;
+
+ if (fwvid >= ARRAY_SIZE(fwvid_list))
+ return -ERANGE;
+
+ brcmf_dbg(TRACE, "mod=%s: enter: dev %s\n", fwvid_list[fwvid].name,
+ dev_name(drvr->bus_if->dev));
+
+ mutex_lock(&fwvid_list_lock);
+
+ ret = brcmf_fwvid_request_module(fwvid);
+ if (ret)
+ return ret;
+
+ drvr->vops = fwvid_list[fwvid].vops;
+ list_add(&drvr->bus_if->list, &fwvid_list[fwvid].drvr_list);
+
+ mutex_unlock(&fwvid_list_lock);
+
+ return ret;
+}
+
+void brcmf_fwvid_detach_ops(struct brcmf_pub *drvr)
+{
+ enum brcmf_fwvendor fwvid = drvr->bus_if->fwvid;
+
+ if (fwvid >= ARRAY_SIZE(fwvid_list))
+ return;
+
+ brcmf_dbg(TRACE, "mod=%s: enter: dev %s\n", fwvid_list[fwvid].name,
+ dev_name(drvr->bus_if->dev));
+
+ mutex_lock(&fwvid_list_lock);
+
+ drvr->vops = NULL;
+ list_del(&drvr->bus_if->list);
+
+ mutex_unlock(&fwvid_list_lock);
+}
new file mode 100644
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: ISC */
+/*
+ * Copyright (c) 2022 Broadcom Corporation
+ */
+#ifndef FWVID_H_
+#define FWVID_H_
+
+#include "firmware.h"
+
+struct brcmf_pub;
+
+struct brcmf_fwvid_ops {
+ int (*attach)(struct brcmf_pub *drvr);
+ void (*detach)(struct brcmf_pub *drvr);
+};
+
+/* exported functions */
+int brcmf_fwvid_register_vendor(enum brcmf_fwvendor fwvid, struct module *mod,
+ const struct brcmf_fwvid_ops *ops);
+int brcmf_fwvid_unregister_vendor(enum brcmf_fwvendor fwvid, struct module *mod);
+
+/* core driver functions */
+int brcmf_fwvid_attach_ops(struct brcmf_pub *drvr);
+void brcmf_fwvid_detach_ops(struct brcmf_pub *drvr);
+
+static inline int brcmf_fwvid_attach(struct brcmf_pub *drvr)
+{
+ int ret;
+
+ ret = brcmf_fwvid_attach_ops(drvr);
+ if (ret)
+ return ret;
+
+ return drvr->vops->attach(drvr);
+}
+
+static inline void brcmf_fwvid_detach(struct brcmf_pub *drvr)
+{
+ if (!drvr->vops)
+ return;
+
+ drvr->vops->detach(drvr);
+ brcmf_fwvid_detach_ops(drvr);
+}
+
+#endif /* FWVID_H_ */
new file mode 100644
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2022 Broadcom Corporation
+
+ccflags-y += \
+ -I $(srctree)/$(src) \
+ -I $(srctree)/$(src)/.. \
+ -I $(srctree)/$(src)/../../include
+
+obj-m += brcmfmac-wcc.o
+brcmfmac-wcc-objs += \
+ core.o module.o
new file mode 100644
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (c) 2022 Broadcom Corporation
+ */
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <core.h>
+#include <bus.h>
+#include <fwvid.h>
+
+#include "vops.h"
+
+static int brcmf_wcc_attach(struct brcmf_pub *drvr)
+{
+ pr_err("%s: executing\n", __func__);
+ return 0;
+}
+
+static void brcmf_wcc_detach(struct brcmf_pub *drvr)
+{
+ pr_err("%s: executing\n", __func__);
+}
+
+const struct brcmf_fwvid_ops brcmf_wcc_ops = {
+ .attach = brcmf_wcc_attach,
+ .detach = brcmf_wcc_detach,
+};
new file mode 100644
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (c) 2022 Broadcom Corporation
+ */
+#include <linux/module.h>
+#include <bus.h>
+#include <core.h>
+#include <fwvid.h>
+
+#include "vops.h"
+
+static int __init brcmf_wcc_init(void)
+{
+ return brcmf_fwvid_register_vendor(BRCMF_FWVENDOR_WCC, THIS_MODULE,
+ &brcmf_wcc_ops);
+}
+
+static void __exit brcmf_wcc_exit(void)
+{
+ brcmf_fwvid_unregister_vendor(BRCMF_FWVENDOR_WCC, THIS_MODULE);
+}
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+module_init(brcmf_wcc_init);
+module_exit(brcmf_wcc_exit);
new file mode 100644
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: ISC */
+/*
+ * Copyright (c) 2022 Broadcom Corporation
+ */
+#ifndef _BRCMFMAC_WCC_VOPS_H
+#define _BRCMFMAC_WCC_VOPS_H
+
+extern const struct brcmf_fwvid_ops brcmf_wcc_ops;
+#define WCC_VOPS (&brcmf_wcc_ops)
+
+#endif /* _BRCMFMAC_WCC_VOPS_H */