xref: /netbsd/sys/arch/hpc/hpc/config_hook.c (revision bf9ec67e)
1 /*	$NetBSD: config_hook.c,v 1.4 2002/05/12 07:41:22 takemura 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/param.h>
38 #include <sys/device.h>
39 #include <sys/malloc.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 
43 #include <machine/config_hook.h>
44 
45 struct hook_rec {
46 	CIRCLEQ_ENTRY(hook_rec) hr_link;
47 	void *hr_ctx;
48 	int hr_type;
49 	long hr_id;
50 	enum config_hook_mode hr_mode;
51 	int (*hr_func)(void *, int, long, void *);
52 };
53 
54 CIRCLEQ_HEAD(hook_list, hook_rec);
55 struct hook_list hook_lists[CONFIG_HOOK_NTYPES];
56 struct hook_list call_list;
57 
58 void
59 config_hook_init()
60 {
61 	int i;
62 
63 	for (i = 0; i < CONFIG_HOOK_NTYPES; i++) {
64 		CIRCLEQ_INIT(&hook_lists[i]);
65 	}
66 	CIRCLEQ_INIT(&call_list);
67 }
68 
69 config_hook_tag
70 config_hook(int type, long id, enum config_hook_mode mode,
71     int (*func)(void *, int, long, void *), void *ctx)
72 {
73 	struct hook_rec *hr, *cr, *prev_hr;
74 	int s;
75 
76 	/* check type value */
77 	if (type < 0 || CONFIG_HOOK_NTYPES <= type) {
78 		panic("config_hook: invalid hook type");
79 	}
80 
81 	/* check mode compatibility */
82 	prev_hr = NULL;
83 	CIRCLEQ_FOREACH(hr, &hook_lists[type], hr_link) {
84 		if (hr->hr_id == id) {
85 			if (hr->hr_mode != mode) {
86 				panic("config_hook: incompatible mode on "
87 				    "type=%d/id=%ld != %d",
88 				    type, id, hr->hr_mode);
89 			}
90 			prev_hr = hr;
91 		}
92 	}
93 	switch (mode) {
94 	case CONFIG_HOOK_SHARE:
95 		/* nothing to do */
96 		break;
97 	case CONFIG_HOOK_REPLACE:
98 		if (prev_hr != NULL) {
99 			printf("config_hook: type=%d/id=%ld is replaced",
100 			    type, id);
101 			s = splhigh();
102 			CIRCLEQ_REMOVE(&hook_lists[type], prev_hr, hr_link);
103 			prev_hr->hr_link.cqe_next = NULL;
104 			splx(s);
105 		}
106 		break;
107 	case CONFIG_HOOK_EXCLUSIVE:
108 		if (prev_hr != NULL) {
109 			panic("config_hook: type=%d/id=%ld is already "
110 			    "hooked(%p)", type, id, prev_hr);
111 		}
112 		break;
113 	default:
114 		break;
115 	}
116 
117 	/* allocate new record */
118 	hr = malloc(sizeof(*hr), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
119 	if (hr == NULL)
120 		panic("config_hook: malloc failed");
121 
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 	CIRCLEQ_INSERT_HEAD(&hook_lists[type], hr, hr_link);
130 
131 	/* update call list */
132 	CIRCLEQ_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
151 config_unhook(config_hook_tag hrx)
152 {
153 	int s;
154 	struct hook_rec *hr = (struct hook_rec*)hrx, *cr;
155 
156 	if (hr->hr_link.cqe_next != NULL) {
157 		s = splhigh();
158 		CIRCLEQ_REMOVE(&hook_lists[hr->hr_type], hr, hr_link);
159 		hr->hr_link.cqe_next = NULL;
160 		/* update call list */
161 		CIRCLEQ_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 	free(hr, M_DEVBUF);
169 }
170 
171 int
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 		CIRCLEQ_FOREACH_REVERSE(hr, &hook_lists[type], hr_link) {
185 			if (hr->hr_id == id)
186 				res = (*hr->hr_func)(hr->hr_ctx, type, id,msg);
187 		}
188 	} else {
189 		CIRCLEQ_FOREACH(hr, &hook_lists[type], hr_link) {
190 			if (hr->hr_id == id)
191 				res = (*hr->hr_func)(hr->hr_ctx, type, id,msg);
192 		}
193 	}
194 
195 	return (res);
196 }
197 
198 config_hook_tag
199 config_connect(int type, long id)
200 {
201 	int s;
202 	struct hook_rec *cr, *hr;
203 
204 	/* check type value */
205 	if (type < 0 || CONFIG_HOOK_NTYPES <= type) {
206 		panic("config_hook: invalid hook type");
207 	}
208 
209 	/* allocate new record */
210 	cr = malloc(sizeof(*hr), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
211 	if (cr == NULL)
212 		panic("config_connect: malloc failed");
213 
214 	cr->hr_func = NULL;
215 	cr->hr_type = type;
216 	cr->hr_id = id;
217 
218 	s = splhigh();
219 	/* insert the record into the call list */
220 	CIRCLEQ_INSERT_HEAD(&call_list, cr, hr_link);
221 
222 	/* scan hook list */
223 	CIRCLEQ_FOREACH(hr, &hook_lists[type], hr_link) {
224 		if (hr->hr_id == id) {
225 			if (hr->hr_mode == CONFIG_HOOK_SHARE)
226 				panic("config_connect: can't connect with "
227 				    "shared hook, type=%d id=%ld\n", type, id);
228 			cr->hr_ctx = hr->hr_ctx;
229 			cr->hr_func = hr->hr_func;
230 			cr->hr_mode = hr->hr_mode;
231 		}
232 	}
233 	splx(s);
234 
235 	return (cr);
236 }
237 
238 void
239 config_disconnect(config_call_tag crx)
240 {
241 	int s;
242 	struct hook_rec *cr = (struct hook_rec*)crx;
243 
244 	s = splhigh();
245 	CIRCLEQ_REMOVE(&call_list, cr, hr_link);
246 	splx(s);
247 
248 	free(cr, M_DEVBUF);
249 }
250 
251 int
252 config_connected_call(config_call_tag crx, void *msg)
253 {
254 	int res;
255 	struct hook_rec *cr = (struct hook_rec*)crx;
256 
257 	if (cr->hr_func != NULL)
258 		res = (*cr->hr_func)(cr->hr_ctx, cr->hr_type, cr->hr_id, msg);
259 	else
260 		res = -1;
261 
262 	return (res);
263 }
264