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