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