Path: blob/main/sys/contrib/dev/broadcom/brcm80211/brcmfmac/fwvid.c
178665 views
// SPDX-License-Identifier: ISC1/*2* Copyright (c) 2022 Broadcom Corporation3*/4#include <linux/errno.h>5#include <linux/export.h>6#include <linux/module.h>7#include <linux/kmod.h>8#include <linux/list.h>9#include <linux/completion.h>10#include <linux/mutex.h>11#include <linux/printk.h>12#include <linux/jiffies.h>13#include <linux/workqueue.h>1415#include "core.h"16#include "bus.h"17#include "debug.h"18#include "fwvid.h"1920#include "wcc/vops.h"21#include "cyw/vops.h"22#include "bca/vops.h"2324struct brcmf_fwvid_entry {25const char *name;26const struct brcmf_fwvid_ops *vops;27struct list_head drvr_list;28#if IS_MODULE(CONFIG_BRCMFMAC)29struct module *vmod;30struct completion reg_done;31#endif32};3334static DEFINE_MUTEX(fwvid_list_lock);3536#if IS_MODULE(CONFIG_BRCMFMAC)37#if defined(__linux__)38#define FWVID_ENTRY_INIT(_vid, _name) \39[BRCMF_FWVENDOR_ ## _vid] = { \40.name = #_name, \41.reg_done = COMPLETION_INITIALIZER(fwvid_list[BRCMF_FWVENDOR_ ## _vid].reg_done), \42.drvr_list = LIST_HEAD_INIT(fwvid_list[BRCMF_FWVENDOR_ ## _vid].drvr_list), \43}44#elif defined(__FreeBSD__)45#define FWVID_ENTRY_INIT(_vid, _name) \46[BRCMF_FWVENDOR_ ## _vid] = { \47.name = #_name, \48.reg_done = COMPLETION_INITIALIZER(fwvid_list[BRCMF_FWVENDOR_ ## _vid].reg_done), \49.drvr_list = LINUX_LIST_HEAD_INIT(fwvid_list[BRCMF_FWVENDOR_ ## _vid].drvr_list), \50}51#endif52#else53#if defined(__linux__)54#define FWVID_ENTRY_INIT(_vid, _name) \55[BRCMF_FWVENDOR_ ## _vid] = { \56.name = #_name, \57.drvr_list = LIST_HEAD_INIT(fwvid_list[BRCMF_FWVENDOR_ ## _vid].drvr_list), \58.vops = _vid ## _VOPS \59}60#elif defined(__FreeBSD__)61#define FWVID_ENTRY_INIT(_vid, _name) \62[BRCMF_FWVENDOR_ ## _vid] = { \63.name = #_name, \64.drvr_list = LINUX_LIST_HEAD_INIT(fwvid_list[BRCMF_FWVENDOR_ ## _vid].drvr_list), \65.vops = _vid ## _VOPS \66}67#endif68#endif /* IS_MODULE(CONFIG_BRCMFMAC) */6970static struct brcmf_fwvid_entry fwvid_list[BRCMF_FWVENDOR_NUM] = {71FWVID_ENTRY_INIT(WCC, wcc),72FWVID_ENTRY_INIT(CYW, cyw),73FWVID_ENTRY_INIT(BCA, bca),74};7576#if IS_MODULE(CONFIG_BRCMFMAC)77static int brcmf_fwvid_request_module(enum brcmf_fwvendor fwvid)78{79int ret;8081if (!fwvid_list[fwvid].vmod) {82struct completion *reg_done = &fwvid_list[fwvid].reg_done;8384mutex_unlock(&fwvid_list_lock);8586ret = request_module("brcmfmac-%s", fwvid_list[fwvid].name);87if (ret)88goto fail;8990ret = wait_for_completion_interruptible(reg_done);91if (ret)92goto fail;9394mutex_lock(&fwvid_list_lock);95}96return 0;9798fail:99brcmf_err("mod=%s: failed %d\n", fwvid_list[fwvid].name, ret);100return ret;101}102103int brcmf_fwvid_register_vendor(enum brcmf_fwvendor fwvid, struct module *vmod,104const struct brcmf_fwvid_ops *vops)105{106if (fwvid >= BRCMF_FWVENDOR_NUM)107return -ERANGE;108109if (WARN_ON(!vmod) || WARN_ON(!vops) ||110WARN_ON(!vops->alloc_fweh_info))111return -EINVAL;112113if (WARN_ON(fwvid_list[fwvid].vmod))114return -EEXIST;115116brcmf_dbg(TRACE, "mod=%s: enter\n", fwvid_list[fwvid].name);117118mutex_lock(&fwvid_list_lock);119120fwvid_list[fwvid].vmod = vmod;121fwvid_list[fwvid].vops = vops;122123mutex_unlock(&fwvid_list_lock);124125complete_all(&fwvid_list[fwvid].reg_done);126127return 0;128}129BRCMF_EXPORT_SYMBOL_GPL(brcmf_fwvid_register_vendor);130131int brcmf_fwvid_unregister_vendor(enum brcmf_fwvendor fwvid, struct module *mod)132{133struct brcmf_bus *bus, *tmp;134135if (fwvid >= BRCMF_FWVENDOR_NUM)136return -ERANGE;137138if (WARN_ON(fwvid_list[fwvid].vmod != mod))139return -ENOENT;140141mutex_lock(&fwvid_list_lock);142143list_for_each_entry_safe(bus, tmp, &fwvid_list[fwvid].drvr_list, list) {144mutex_unlock(&fwvid_list_lock);145146brcmf_dbg(INFO, "mod=%s: removing %s\n", fwvid_list[fwvid].name,147dev_name(bus->dev));148brcmf_bus_remove(bus);149150mutex_lock(&fwvid_list_lock);151}152153fwvid_list[fwvid].vmod = NULL;154fwvid_list[fwvid].vops = NULL;155reinit_completion(&fwvid_list[fwvid].reg_done);156157brcmf_dbg(TRACE, "mod=%s: exit\n", fwvid_list[fwvid].name);158mutex_unlock(&fwvid_list_lock);159160return 0;161}162BRCMF_EXPORT_SYMBOL_GPL(brcmf_fwvid_unregister_vendor);163#else164static inline int brcmf_fwvid_request_module(enum brcmf_fwvendor fwvid)165{166return 0;167}168#endif169170int brcmf_fwvid_attach(struct brcmf_pub *drvr)171{172enum brcmf_fwvendor fwvid = drvr->bus_if->fwvid;173int ret;174175if (fwvid >= ARRAY_SIZE(fwvid_list))176return -ERANGE;177178brcmf_dbg(TRACE, "mod=%s: enter: dev %s\n", fwvid_list[fwvid].name,179dev_name(drvr->bus_if->dev));180181mutex_lock(&fwvid_list_lock);182183ret = brcmf_fwvid_request_module(fwvid);184if (ret)185return ret;186187drvr->vops = fwvid_list[fwvid].vops;188list_add(&drvr->bus_if->list, &fwvid_list[fwvid].drvr_list);189190mutex_unlock(&fwvid_list_lock);191192return ret;193}194195void brcmf_fwvid_detach(struct brcmf_pub *drvr)196{197enum brcmf_fwvendor fwvid = drvr->bus_if->fwvid;198199if (fwvid >= ARRAY_SIZE(fwvid_list))200return;201202brcmf_dbg(TRACE, "mod=%s: enter: dev %s\n", fwvid_list[fwvid].name,203dev_name(drvr->bus_if->dev));204205mutex_lock(&fwvid_list_lock);206207if (drvr->vops) {208drvr->vops = NULL;209list_del(&drvr->bus_if->list);210}211mutex_unlock(&fwvid_list_lock);212}213214const char *brcmf_fwvid_vendor_name(struct brcmf_pub *drvr)215{216return fwvid_list[drvr->bus_if->fwvid].name;217}218219220