Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/broadcom/brcm80211/brcmfmac/fwvid.c
178665 views
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)
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
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
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
165
static inline int brcmf_fwvid_request_module(enum brcmf_fwvendor fwvid)
166
{
167
return 0;
168
}
169
#endif
170
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
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
215
const char *brcmf_fwvid_vendor_name(struct brcmf_pub *drvr)
216
{
217
return fwvid_list[drvr->bus_if->fwvid].name;
218
}
219
220