1 /* $NetBSD: config_hook.c,v 1.12 2020/11/21 21:08:32 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 1999-2001
5 * Shin Takemura and PocketBSD Project. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the PocketBSD project
18 * and its contributors.
19 * 4. Neither the name of the project nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 */
36
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: config_hook.c,v 1.12 2020/11/21 21:08:32 thorpej Exp $");
39
40 #include <sys/param.h>
41 #include <sys/device.h>
42 #include <sys/kmem.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45
46 #include <machine/config_hook.h>
47
48 struct hook_rec {
49 TAILQ_ENTRY(hook_rec) hr_link;
50 void *hr_ctx;
51 int hr_type;
52 long hr_id;
53 enum config_hook_mode hr_mode;
54 int (*hr_func)(void *, int, long, void *);
55 };
56
57 TAILQ_HEAD(hook_list, hook_rec);
58 struct hook_list hook_lists[CONFIG_HOOK_NTYPES];
59 struct hook_list call_list;
60
61 void
config_hook_init(void)62 config_hook_init(void)
63 {
64 int i;
65
66 for (i = 0; i < CONFIG_HOOK_NTYPES; i++) {
67 TAILQ_INIT(&hook_lists[i]);
68 }
69 TAILQ_INIT(&call_list);
70 }
71
72 config_hook_tag
config_hook(int type,long id,enum config_hook_mode mode,int (* func)(void *,int,long,void *),void * ctx)73 config_hook(int type, long id, enum config_hook_mode mode,
74 int (*func)(void *, int, long, void *), void *ctx)
75 {
76 struct hook_rec *hr, *cr, *prev_hr;
77 int s;
78
79 /* check type value */
80 if (type < 0 || CONFIG_HOOK_NTYPES <= type) {
81 panic("config_hook: invalid hook type");
82 }
83
84 /* check mode compatibility */
85 prev_hr = NULL;
86 TAILQ_FOREACH(hr, &hook_lists[type], hr_link) {
87 if (hr->hr_id == id) {
88 if (hr->hr_mode != mode) {
89 panic("config_hook: incompatible mode on "
90 "type=%d/id=%ld != %d",
91 type, id, hr->hr_mode);
92 }
93 prev_hr = hr;
94 }
95 }
96 switch (mode) {
97 case CONFIG_HOOK_SHARE:
98 /* nothing to do */
99 break;
100 case CONFIG_HOOK_REPLACE:
101 if (prev_hr != NULL) {
102 printf("config_hook: type=%d/id=%ld is replaced",
103 type, id);
104 s = splhigh();
105 TAILQ_REMOVE(&hook_lists[type], prev_hr, hr_link);
106 TAILQ_NEXT(prev_hr, hr_link) = NULL;
107 splx(s);
108 }
109 break;
110 case CONFIG_HOOK_EXCLUSIVE:
111 if (prev_hr != NULL) {
112 panic("config_hook: type=%d/id=%ld is already "
113 "hooked(%p)", type, id, prev_hr);
114 }
115 break;
116 default:
117 break;
118 }
119
120 /* allocate new record */
121 hr = kmem_alloc(sizeof(*hr), KM_SLEEP);
122 hr->hr_ctx = ctx;
123 hr->hr_type = type;
124 hr->hr_id = id;
125 hr->hr_func = func;
126 hr->hr_mode = mode;
127
128 s = splhigh();
129 TAILQ_INSERT_HEAD(&hook_lists[type], hr, hr_link);
130
131 /* update call list */
132 TAILQ_FOREACH(cr, &call_list, hr_link) {
133 if (cr->hr_type == type && cr->hr_id == id) {
134 if (cr->hr_func != NULL &&
135 cr->hr_mode != mode) {
136 panic("config_hook: incompatible mode on "
137 "type=%d/id=%ld != %d",
138 type, id, cr->hr_mode);
139 }
140 cr->hr_ctx = ctx;
141 cr->hr_func = func;
142 cr->hr_mode = mode;
143 }
144 }
145 splx(s);
146
147 return (hr);
148 }
149
150 void
config_unhook(config_hook_tag hrx)151 config_unhook(config_hook_tag hrx)
152 {
153 int s;
154 struct hook_rec *hr = (struct hook_rec*)hrx, *cr;
155
156 if (TAILQ_NEXT(hr, hr_link) != NULL) {
157 s = splhigh();
158 TAILQ_REMOVE(&hook_lists[hr->hr_type], hr, hr_link);
159 TAILQ_NEXT(hr, hr_link) = NULL;
160 /* update call list */
161 TAILQ_FOREACH(cr, &call_list, hr_link) {
162 if (cr->hr_type == hr->hr_type &&
163 cr->hr_id == hr->hr_id)
164 cr->hr_func = NULL;
165 }
166 splx(s);
167 }
168 kmem_free(hr, sizeof(*hr));
169 }
170
171 int
__config_hook_call(int type,long id,void * msg,int reverse)172 __config_hook_call(int type, long id, void *msg, int reverse)
173 {
174 int res;
175 struct hook_rec *hr;
176
177 /* Check type value. */
178 if (type < 0 || CONFIG_HOOK_NTYPES <= type) {
179 panic("config_hook: invalid hook type");
180 }
181
182 res = -1;
183 if (reverse) {
184 TAILQ_FOREACH_REVERSE(hr, &hook_lists[type], hook_list,
185 hr_link) {
186 if (hr->hr_id == id)
187 res = (*hr->hr_func)(hr->hr_ctx, type, id,msg);
188 }
189 } else {
190 TAILQ_FOREACH(hr, &hook_lists[type], hr_link) {
191 if (hr->hr_id == id)
192 res = (*hr->hr_func)(hr->hr_ctx, type, id,msg);
193 }
194 }
195
196 return (res);
197 }
198
199 config_hook_tag
config_connect(int type,long id)200 config_connect(int type, long id)
201 {
202 int s;
203 struct hook_rec *cr, *hr;
204
205 /* check type value */
206 if (type < 0 || CONFIG_HOOK_NTYPES <= type) {
207 panic("config_hook: invalid hook type");
208 }
209
210 /* allocate new record */
211 cr = kmem_alloc(sizeof(*hr), KM_SLEEP);
212 cr->hr_func = NULL;
213 cr->hr_type = type;
214 cr->hr_id = id;
215
216 s = splhigh();
217 /* insert the record into the call list */
218 TAILQ_INSERT_HEAD(&call_list, cr, hr_link);
219
220 /* scan hook list */
221 TAILQ_FOREACH(hr, &hook_lists[type], hr_link) {
222 if (hr->hr_id == id) {
223 if (hr->hr_mode == CONFIG_HOOK_SHARE)
224 panic("config_connect: can't connect with "
225 "shared hook, type=%d id=%ld", type, id);
226 cr->hr_ctx = hr->hr_ctx;
227 cr->hr_func = hr->hr_func;
228 cr->hr_mode = hr->hr_mode;
229 }
230 }
231 splx(s);
232
233 return (cr);
234 }
235
236 void
config_disconnect(config_call_tag crx)237 config_disconnect(config_call_tag crx)
238 {
239 int s;
240 struct hook_rec *cr = (struct hook_rec*)crx;
241
242 s = splhigh();
243 TAILQ_REMOVE(&call_list, cr, hr_link);
244 splx(s);
245
246 kmem_free(cr, sizeof(*cr));
247 }
248
249 int
config_connected_call(config_call_tag crx,void * msg)250 config_connected_call(config_call_tag crx, void *msg)
251 {
252 int res;
253 struct hook_rec *cr = (struct hook_rec*)crx;
254
255 if (cr->hr_func != NULL)
256 res = (*cr->hr_func)(cr->hr_ctx, cr->hr_type, cr->hr_id, msg);
257 else
258 res = -1;
259
260 return (res);
261 }
262