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