xref: /linux/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c (revision f74f1ec22dc232be0296739148d126e9158eadf9)
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"
22*f74f1ec2SArend van Spriel #include "cyw/vops.h"
23d6a5c562SArend van Spriel 
24d6a5c562SArend van Spriel struct brcmf_fwvid_entry {
25d6a5c562SArend van Spriel 	const char *name;
26d6a5c562SArend van Spriel 	const struct brcmf_fwvid_ops *vops;
27d6a5c562SArend van Spriel 	struct list_head drvr_list;
28d6a5c562SArend van Spriel #if IS_MODULE(CONFIG_BRCMFMAC)
29d6a5c562SArend van Spriel 	struct module *vmod;
30d6a5c562SArend van Spriel 	struct completion reg_done;
31d6a5c562SArend van Spriel #endif
32d6a5c562SArend van Spriel };
33d6a5c562SArend van Spriel 
34d6a5c562SArend van Spriel static DEFINE_MUTEX(fwvid_list_lock);
35d6a5c562SArend van Spriel 
36d6a5c562SArend van Spriel #if IS_MODULE(CONFIG_BRCMFMAC)
37d6a5c562SArend van Spriel #define FWVID_ENTRY_INIT(_vid, _name) \
38d6a5c562SArend van Spriel 	[BRCMF_FWVENDOR_ ## _vid] = { \
39d6a5c562SArend van Spriel 		.name = #_name, \
40d6a5c562SArend van Spriel 		.reg_done = COMPLETION_INITIALIZER(fwvid_list[BRCMF_FWVENDOR_ ## _vid].reg_done), \
41d6a5c562SArend van Spriel 		.drvr_list = LIST_HEAD_INIT(fwvid_list[BRCMF_FWVENDOR_ ## _vid].drvr_list), \
42d6a5c562SArend van Spriel 	}
43d6a5c562SArend van Spriel #else
44d6a5c562SArend van Spriel #define FWVID_ENTRY_INIT(_vid, _name) \
45d6a5c562SArend van Spriel 	[BRCMF_FWVENDOR_ ## _vid] = { \
46d6a5c562SArend van Spriel 		.name = #_name, \
47d6a5c562SArend van Spriel 		.drvr_list = LIST_HEAD_INIT(fwvid_list[BRCMF_FWVENDOR_ ## _vid].drvr_list), \
48d6a5c562SArend van Spriel 		.vops = _vid ## _VOPS \
49d6a5c562SArend van Spriel 	}
50d6a5c562SArend van Spriel #endif /* IS_MODULE(CONFIG_BRCMFMAC) */
51d6a5c562SArend van Spriel 
52d6a5c562SArend van Spriel static struct brcmf_fwvid_entry fwvid_list[BRCMF_FWVENDOR_NUM] = {
53d6a5c562SArend van Spriel 	FWVID_ENTRY_INIT(WCC, wcc),
54*f74f1ec2SArend van Spriel 	FWVID_ENTRY_INIT(CYW, cyw),
55d6a5c562SArend van Spriel };
56d6a5c562SArend van Spriel 
57d6a5c562SArend van Spriel #if IS_MODULE(CONFIG_BRCMFMAC)
58d6a5c562SArend van Spriel static int brcmf_fwvid_request_module(enum brcmf_fwvendor fwvid)
59d6a5c562SArend van Spriel {
60d6a5c562SArend van Spriel 	int ret;
61d6a5c562SArend van Spriel 
62d6a5c562SArend van Spriel 	if (!fwvid_list[fwvid].vmod) {
63d6a5c562SArend van Spriel 		struct completion *reg_done = &fwvid_list[fwvid].reg_done;
64d6a5c562SArend van Spriel 
65d6a5c562SArend van Spriel 		mutex_unlock(&fwvid_list_lock);
66d6a5c562SArend van Spriel 
67d6a5c562SArend van Spriel 		ret = request_module("brcmfmac-%s", fwvid_list[fwvid].name);
68d6a5c562SArend van Spriel 		if (ret)
69d6a5c562SArend van Spriel 			goto fail;
70d6a5c562SArend van Spriel 
71d6a5c562SArend van Spriel 		ret = wait_for_completion_interruptible(reg_done);
72d6a5c562SArend van Spriel 		if (ret)
73d6a5c562SArend van Spriel 			goto fail;
74d6a5c562SArend van Spriel 
75d6a5c562SArend van Spriel 		mutex_lock(&fwvid_list_lock);
76d6a5c562SArend van Spriel 	}
77d6a5c562SArend van Spriel 	return 0;
78d6a5c562SArend van Spriel 
79d6a5c562SArend van Spriel fail:
80d6a5c562SArend van Spriel 	brcmf_err("mod=%s: failed %d\n", fwvid_list[fwvid].name, ret);
81d6a5c562SArend van Spriel 	return ret;
82d6a5c562SArend van Spriel }
83d6a5c562SArend van Spriel 
84d6a5c562SArend van Spriel int brcmf_fwvid_register_vendor(enum brcmf_fwvendor fwvid, struct module *vmod,
85d6a5c562SArend van Spriel 				const struct brcmf_fwvid_ops *vops)
86d6a5c562SArend van Spriel {
87d6a5c562SArend van Spriel 	if (fwvid >= BRCMF_FWVENDOR_NUM)
88d6a5c562SArend van Spriel 		return -ERANGE;
89d6a5c562SArend van Spriel 
90d6a5c562SArend van Spriel 	if (WARN_ON(!vmod) || WARN_ON(!vops) ||
91d6a5c562SArend van Spriel 	    WARN_ON(!vops->attach) || WARN_ON(!vops->detach))
92d6a5c562SArend van Spriel 		return -EINVAL;
93d6a5c562SArend van Spriel 
94d6a5c562SArend van Spriel 	if (WARN_ON(fwvid_list[fwvid].vmod))
95d6a5c562SArend van Spriel 		return -EEXIST;
96d6a5c562SArend van Spriel 
97d6a5c562SArend van Spriel 	brcmf_dbg(TRACE, "mod=%s: enter\n", fwvid_list[fwvid].name);
98d6a5c562SArend van Spriel 
99d6a5c562SArend van Spriel 	mutex_lock(&fwvid_list_lock);
100d6a5c562SArend van Spriel 
101d6a5c562SArend van Spriel 	fwvid_list[fwvid].vmod = vmod;
102d6a5c562SArend van Spriel 	fwvid_list[fwvid].vops = vops;
103d6a5c562SArend van Spriel 
104d6a5c562SArend van Spriel 	mutex_unlock(&fwvid_list_lock);
105d6a5c562SArend van Spriel 
106d6a5c562SArend van Spriel 	complete_all(&fwvid_list[fwvid].reg_done);
107d6a5c562SArend van Spriel 
108d6a5c562SArend van Spriel 	return 0;
109d6a5c562SArend van Spriel }
110d6a5c562SArend van Spriel EXPORT_SYMBOL(brcmf_fwvid_register_vendor);
111d6a5c562SArend van Spriel 
112d6a5c562SArend van Spriel int brcmf_fwvid_unregister_vendor(enum brcmf_fwvendor fwvid, struct module *mod)
113d6a5c562SArend van Spriel {
114d6a5c562SArend van Spriel 	struct brcmf_bus *bus, *tmp;
115d6a5c562SArend van Spriel 
116d6a5c562SArend van Spriel 	if (fwvid >= BRCMF_FWVENDOR_NUM)
117d6a5c562SArend van Spriel 		return -ERANGE;
118d6a5c562SArend van Spriel 
119d6a5c562SArend van Spriel 	if (WARN_ON(fwvid_list[fwvid].vmod != mod))
120d6a5c562SArend van Spriel 		return -ENOENT;
121d6a5c562SArend van Spriel 
122d6a5c562SArend van Spriel 	mutex_lock(&fwvid_list_lock);
123d6a5c562SArend van Spriel 
124d6a5c562SArend van Spriel 	list_for_each_entry_safe(bus, tmp, &fwvid_list[fwvid].drvr_list, list) {
125d6a5c562SArend van Spriel 		mutex_unlock(&fwvid_list_lock);
126d6a5c562SArend van Spriel 
127d6a5c562SArend van Spriel 		brcmf_dbg(INFO, "mod=%s: removing %s\n", fwvid_list[fwvid].name,
128d6a5c562SArend van Spriel 			  dev_name(bus->dev));
129d6a5c562SArend van Spriel 		brcmf_bus_remove(bus);
130d6a5c562SArend van Spriel 
131d6a5c562SArend van Spriel 		mutex_lock(&fwvid_list_lock);
132d6a5c562SArend van Spriel 	}
133d6a5c562SArend van Spriel 
134d6a5c562SArend van Spriel 	fwvid_list[fwvid].vmod = NULL;
135d6a5c562SArend van Spriel 	fwvid_list[fwvid].vops = NULL;
136d6a5c562SArend van Spriel 	reinit_completion(&fwvid_list[fwvid].reg_done);
137d6a5c562SArend van Spriel 
138d6a5c562SArend van Spriel 	brcmf_dbg(TRACE, "mod=%s: exit\n", fwvid_list[fwvid].name);
139d6a5c562SArend van Spriel 	mutex_unlock(&fwvid_list_lock);
140d6a5c562SArend van Spriel 
141d6a5c562SArend van Spriel 	return 0;
142d6a5c562SArend van Spriel }
143d6a5c562SArend van Spriel EXPORT_SYMBOL(brcmf_fwvid_unregister_vendor);
144d6a5c562SArend van Spriel #else
145d6a5c562SArend van Spriel static inline int brcmf_fwvid_request_module(enum brcmf_fwvendor fwvid)
146d6a5c562SArend van Spriel {
147d6a5c562SArend van Spriel 	return 0;
148d6a5c562SArend van Spriel }
149d6a5c562SArend van Spriel #endif
150d6a5c562SArend van Spriel 
151d6a5c562SArend van Spriel int brcmf_fwvid_attach_ops(struct brcmf_pub *drvr)
152d6a5c562SArend van Spriel {
153d6a5c562SArend van Spriel 	enum brcmf_fwvendor fwvid = drvr->bus_if->fwvid;
154d6a5c562SArend van Spriel 	int ret;
155d6a5c562SArend van Spriel 
156d6a5c562SArend van Spriel 	if (fwvid >= ARRAY_SIZE(fwvid_list))
157d6a5c562SArend van Spriel 		return -ERANGE;
158d6a5c562SArend van Spriel 
159d6a5c562SArend van Spriel 	brcmf_dbg(TRACE, "mod=%s: enter: dev %s\n", fwvid_list[fwvid].name,
160d6a5c562SArend van Spriel 		  dev_name(drvr->bus_if->dev));
161d6a5c562SArend van Spriel 
162d6a5c562SArend van Spriel 	mutex_lock(&fwvid_list_lock);
163d6a5c562SArend van Spriel 
164d6a5c562SArend van Spriel 	ret = brcmf_fwvid_request_module(fwvid);
165d6a5c562SArend van Spriel 	if (ret)
166d6a5c562SArend van Spriel 		return ret;
167d6a5c562SArend van Spriel 
168d6a5c562SArend van Spriel 	drvr->vops = fwvid_list[fwvid].vops;
169d6a5c562SArend van Spriel 	list_add(&drvr->bus_if->list, &fwvid_list[fwvid].drvr_list);
170d6a5c562SArend van Spriel 
171d6a5c562SArend van Spriel 	mutex_unlock(&fwvid_list_lock);
172d6a5c562SArend van Spriel 
173d6a5c562SArend van Spriel 	return ret;
174d6a5c562SArend van Spriel }
175d6a5c562SArend van Spriel 
176d6a5c562SArend van Spriel void brcmf_fwvid_detach_ops(struct brcmf_pub *drvr)
177d6a5c562SArend van Spriel {
178d6a5c562SArend van Spriel 	enum brcmf_fwvendor fwvid = drvr->bus_if->fwvid;
179d6a5c562SArend van Spriel 
180d6a5c562SArend van Spriel 	if (fwvid >= ARRAY_SIZE(fwvid_list))
181d6a5c562SArend van Spriel 		return;
182d6a5c562SArend van Spriel 
183d6a5c562SArend van Spriel 	brcmf_dbg(TRACE, "mod=%s: enter: dev %s\n", fwvid_list[fwvid].name,
184d6a5c562SArend van Spriel 		  dev_name(drvr->bus_if->dev));
185d6a5c562SArend van Spriel 
186d6a5c562SArend van Spriel 	mutex_lock(&fwvid_list_lock);
187d6a5c562SArend van Spriel 
188d6a5c562SArend van Spriel 	drvr->vops = NULL;
189d6a5c562SArend van Spriel 	list_del(&drvr->bus_if->list);
190d6a5c562SArend van Spriel 
191d6a5c562SArend van Spriel 	mutex_unlock(&fwvid_list_lock);
192d6a5c562SArend van Spriel }
193