1 /* $NetBSD: config_hook.c,v 1.5 2002/09/27 15:36:03 provos 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", 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