xref: /netbsd-src/sys/arch/hpc/hpc/config_hook.c (revision d24b629e0976b45067a913b7fab3ef9bdc18e087)
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