brintos

brintos / linux-shallow public Read only

0
0
Text · 4.4 KiB · 41eafcd Raw
201 lines · c
1// SPDX-License-Identifier: ISC2/*3 * Copyright (c) 2022 Broadcom Corporation4 */5#include <linux/errno.h>6#include <linux/export.h>7#include <linux/module.h>8#include <linux/kmod.h>9#include <linux/list.h>10#include <linux/completion.h>11#include <linux/mutex.h>12#include <linux/printk.h>13#include <linux/jiffies.h>14#include <linux/workqueue.h>15 16#include "core.h"17#include "bus.h"18#include "debug.h"19#include "fwvid.h"20 21#include "wcc/vops.h"22#include "cyw/vops.h"23#include "bca/vops.h"24 25struct brcmf_fwvid_entry {26	const char *name;27	const struct brcmf_fwvid_ops *vops;28	struct list_head drvr_list;29#if IS_MODULE(CONFIG_BRCMFMAC)30	struct module *vmod;31	struct completion reg_done;32#endif33};34 35static DEFINE_MUTEX(fwvid_list_lock);36 37#if IS_MODULE(CONFIG_BRCMFMAC)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#else45#define FWVID_ENTRY_INIT(_vid, _name) \46	[BRCMF_FWVENDOR_ ## _vid] = { \47		.name = #_name, \48		.drvr_list = LIST_HEAD_INIT(fwvid_list[BRCMF_FWVENDOR_ ## _vid].drvr_list), \49		.vops = _vid ## _VOPS \50	}51#endif /* IS_MODULE(CONFIG_BRCMFMAC) */52 53static struct brcmf_fwvid_entry fwvid_list[BRCMF_FWVENDOR_NUM] = {54	FWVID_ENTRY_INIT(WCC, wcc),55	FWVID_ENTRY_INIT(CYW, cyw),56	FWVID_ENTRY_INIT(BCA, bca),57};58 59#if IS_MODULE(CONFIG_BRCMFMAC)60static int brcmf_fwvid_request_module(enum brcmf_fwvendor fwvid)61{62	int ret;63 64	if (!fwvid_list[fwvid].vmod) {65		struct completion *reg_done = &fwvid_list[fwvid].reg_done;66 67		mutex_unlock(&fwvid_list_lock);68 69		ret = request_module("brcmfmac-%s", fwvid_list[fwvid].name);70		if (ret)71			goto fail;72 73		ret = wait_for_completion_interruptible(reg_done);74		if (ret)75			goto fail;76 77		mutex_lock(&fwvid_list_lock);78	}79	return 0;80 81fail:82	brcmf_err("mod=%s: failed %d\n", fwvid_list[fwvid].name, ret);83	return ret;84}85 86int brcmf_fwvid_register_vendor(enum brcmf_fwvendor fwvid, struct module *vmod,87				const struct brcmf_fwvid_ops *vops)88{89	if (fwvid >= BRCMF_FWVENDOR_NUM)90		return -ERANGE;91 92	if (WARN_ON(!vmod) || WARN_ON(!vops) ||93	    WARN_ON(!vops->alloc_fweh_info))94		return -EINVAL;95 96	if (WARN_ON(fwvid_list[fwvid].vmod))97		return -EEXIST;98 99	brcmf_dbg(TRACE, "mod=%s: enter\n", fwvid_list[fwvid].name);100 101	mutex_lock(&fwvid_list_lock);102 103	fwvid_list[fwvid].vmod = vmod;104	fwvid_list[fwvid].vops = vops;105 106	mutex_unlock(&fwvid_list_lock);107 108	complete_all(&fwvid_list[fwvid].reg_done);109 110	return 0;111}112BRCMF_EXPORT_SYMBOL_GPL(brcmf_fwvid_register_vendor);113 114int brcmf_fwvid_unregister_vendor(enum brcmf_fwvendor fwvid, struct module *mod)115{116	struct brcmf_bus *bus, *tmp;117 118	if (fwvid >= BRCMF_FWVENDOR_NUM)119		return -ERANGE;120 121	if (WARN_ON(fwvid_list[fwvid].vmod != mod))122		return -ENOENT;123 124	mutex_lock(&fwvid_list_lock);125 126	list_for_each_entry_safe(bus, tmp, &fwvid_list[fwvid].drvr_list, list) {127		mutex_unlock(&fwvid_list_lock);128 129		brcmf_dbg(INFO, "mod=%s: removing %s\n", fwvid_list[fwvid].name,130			  dev_name(bus->dev));131		brcmf_bus_remove(bus);132 133		mutex_lock(&fwvid_list_lock);134	}135 136	fwvid_list[fwvid].vmod = NULL;137	fwvid_list[fwvid].vops = NULL;138	reinit_completion(&fwvid_list[fwvid].reg_done);139 140	brcmf_dbg(TRACE, "mod=%s: exit\n", fwvid_list[fwvid].name);141	mutex_unlock(&fwvid_list_lock);142 143	return 0;144}145BRCMF_EXPORT_SYMBOL_GPL(brcmf_fwvid_unregister_vendor);146#else147static inline int brcmf_fwvid_request_module(enum brcmf_fwvendor fwvid)148{149	return 0;150}151#endif152 153int brcmf_fwvid_attach(struct brcmf_pub *drvr)154{155	enum brcmf_fwvendor fwvid = drvr->bus_if->fwvid;156	int ret;157 158	if (fwvid >= ARRAY_SIZE(fwvid_list))159		return -ERANGE;160 161	brcmf_dbg(TRACE, "mod=%s: enter: dev %s\n", fwvid_list[fwvid].name,162		  dev_name(drvr->bus_if->dev));163 164	mutex_lock(&fwvid_list_lock);165 166	ret = brcmf_fwvid_request_module(fwvid);167	if (ret)168		return ret;169 170	drvr->vops = fwvid_list[fwvid].vops;171	list_add(&drvr->bus_if->list, &fwvid_list[fwvid].drvr_list);172 173	mutex_unlock(&fwvid_list_lock);174 175	return ret;176}177 178void brcmf_fwvid_detach(struct brcmf_pub *drvr)179{180	enum brcmf_fwvendor fwvid = drvr->bus_if->fwvid;181 182	if (fwvid >= ARRAY_SIZE(fwvid_list))183		return;184 185	brcmf_dbg(TRACE, "mod=%s: enter: dev %s\n", fwvid_list[fwvid].name,186		  dev_name(drvr->bus_if->dev));187 188	mutex_lock(&fwvid_list_lock);189 190	if (drvr->vops) {191		drvr->vops = NULL;192		list_del(&drvr->bus_if->list);193	}194	mutex_unlock(&fwvid_list_lock);195}196 197const char *brcmf_fwvid_vendor_name(struct brcmf_pub *drvr)198{199	return fwvid_list[drvr->bus_if->fwvid].name;200}201