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