xref: /netbsd/sys/arch/hpc/hpc/config_hook.c (revision 6550d01e)
1 /*	$NetBSD: config_hook.c,v 1.8 2009/03/18 10:22:28 cegger 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.8 2009/03/18 10:22:28 cegger Exp $");
39 
40 #include <sys/param.h>
41 #include <sys/device.h>
42 #include <sys/malloc.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 
46 #include <machine/config_hook.h>
47 
48 struct hook_rec {
49 	CIRCLEQ_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 CIRCLEQ_HEAD(hook_list, hook_rec);
58 struct hook_list hook_lists[CONFIG_HOOK_NTYPES];
59 struct hook_list call_list;
60 
61 void
62 config_hook_init(void)
63 {
64 	int i;
65 
66 	for (i = 0; i < CONFIG_HOOK_NTYPES; i++) {
67 		CIRCLEQ_INIT(&hook_lists[i]);
68 	}
69 	CIRCLEQ_INIT(&call_list);
70 }
71 
72 config_hook_tag
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 	CIRCLEQ_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 			CIRCLEQ_REMOVE(&hook_lists[type], prev_hr, hr_link);
106 			prev_hr->hr_link.cqe_next = 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 = malloc(sizeof(*hr), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
122 	if (hr == NULL)
123 		panic("config_hook: malloc failed");
124 
125 	hr->hr_ctx = ctx;
126 	hr->hr_type = type;
127 	hr->hr_id = id;
128 	hr->hr_func = func;
129 	hr->hr_mode = mode;
130 
131 	s = splhigh();
132 	CIRCLEQ_INSERT_HEAD(&hook_lists[type], hr, hr_link);
133 
134 	/* update call list */
135 	CIRCLEQ_FOREACH(cr, &call_list, hr_link) {
136 		if (cr->hr_type == type && cr->hr_id == id) {
137 			if (cr->hr_func != NULL &&
138 			    cr->hr_mode != mode) {
139 				panic("config_hook: incompatible mode on "
140 				    "type=%d/id=%ld != %d",
141 				    type, id, cr->hr_mode);
142 			}
143 			cr->hr_ctx = ctx;
144 			cr->hr_func = func;
145 			cr->hr_mode = mode;
146 		}
147 	}
148 	splx(s);
149 
150 	return (hr);
151 }
152 
153 void
154 config_unhook(config_hook_tag hrx)
155 {
156 	int s;
157 	struct hook_rec *hr = (struct hook_rec*)hrx, *cr;
158 
159 	if (hr->hr_link.cqe_next != NULL) {
160 		s = splhigh();
161 		CIRCLEQ_REMOVE(&hook_lists[hr->hr_type], hr, hr_link);
162 		hr->hr_link.cqe_next = NULL;
163 		/* update call list */
164 		CIRCLEQ_FOREACH(cr, &call_list, hr_link) {
165 			if (cr->hr_type == hr->hr_type &&
166 			    cr->hr_id == hr->hr_id)
167 				cr->hr_func = NULL;
168 		}
169 		splx(s);
170 	}
171 	free(hr, M_DEVBUF);
172 }
173 
174 int
175 __config_hook_call(int type, long id, void *msg, int reverse)
176 {
177 	int res;
178 	struct hook_rec *hr;
179 
180 	/* Check type value. */
181 	if (type < 0 || CONFIG_HOOK_NTYPES <= type) {
182 		panic("config_hook: invalid hook type");
183 	}
184 
185 	res = -1;
186 	if (reverse) {
187 		CIRCLEQ_FOREACH_REVERSE(hr, &hook_lists[type], hr_link) {
188 			if (hr->hr_id == id)
189 				res = (*hr->hr_func)(hr->hr_ctx, type, id,msg);
190 		}
191 	} else {
192 		CIRCLEQ_FOREACH(hr, &hook_lists[type], hr_link) {
193 			if (hr->hr_id == id)
194 				res = (*hr->hr_func)(hr->hr_ctx, type, id,msg);
195 		}
196 	}
197 
198 	return (res);
199 }
200 
201 config_hook_tag
202 config_connect(int type, long id)
203 {
204 	int s;
205 	struct hook_rec *cr, *hr;
206 
207 	/* check type value */
208 	if (type < 0 || CONFIG_HOOK_NTYPES <= type) {
209 		panic("config_hook: invalid hook type");
210 	}
211 
212 	/* allocate new record */
213 	cr = malloc(sizeof(*hr), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
214 	if (cr == NULL)
215 		panic("config_connect: malloc failed");
216 
217 	cr->hr_func = NULL;
218 	cr->hr_type = type;
219 	cr->hr_id = id;
220 
221 	s = splhigh();
222 	/* insert the record into the call list */
223 	CIRCLEQ_INSERT_HEAD(&call_list, cr, hr_link);
224 
225 	/* scan hook list */
226 	CIRCLEQ_FOREACH(hr, &hook_lists[type], hr_link) {
227 		if (hr->hr_id == id) {
228 			if (hr->hr_mode == CONFIG_HOOK_SHARE)
229 				panic("config_connect: can't connect with "
230 				    "shared hook, type=%d id=%ld", type, id);
231 			cr->hr_ctx = hr->hr_ctx;
232 			cr->hr_func = hr->hr_func;
233 			cr->hr_mode = hr->hr_mode;
234 		}
235 	}
236 	splx(s);
237 
238 	return (cr);
239 }
240 
241 void
242 config_disconnect(config_call_tag crx)
243 {
244 	int s;
245 	struct hook_rec *cr = (struct hook_rec*)crx;
246 
247 	s = splhigh();
248 	CIRCLEQ_REMOVE(&call_list, cr, hr_link);
249 	splx(s);
250 
251 	free(cr, M_DEVBUF);
252 }
253 
254 int
255 config_connected_call(config_call_tag crx, void *msg)
256 {
257 	int res;
258 	struct hook_rec *cr = (struct hook_rec*)crx;
259 
260 	if (cr->hr_func != NULL)
261 		res = (*cr->hr_func)(cr->hr_ctx, cr->hr_type, cr->hr_id, msg);
262 	else
263 		res = -1;
264 
265 	return (res);
266 }
267