File Index
Files and directories
Sign in
FILE

fwvid.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c C
  1// SPDX-License-Identifier: ISC
  2/*
  3 * Copyright (c) 2022 Broadcom Corporation
  4 */
  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#endif
 33};
 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#else
 45#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#else
147static inline int brcmf_fwvid_request_module(enum brcmf_fwvendor fwvid)
148{
149	return 0;
150}
151#endif
152
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}
~/ Read-only file preview.