subr_autoconf.c revision 29680
1/* 2 * Copyright (c) 1992, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This software was developed by the Computer Systems Engineering group 6 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 7 * contributed to Berkeley. 8 * 9 * All advertising materials mentioning features or use of this software 10 * must display the following acknowledgement: 11 * This product includes software developed by the University of 12 * California, Lawrence Berkeley Laboratories. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 3. All advertising materials mentioning features or use of this software 23 * must display the following acknowledgement: 24 * This product includes software developed by the University of 25 * California, Berkeley and its contributors. 26 * 4. Neither the name of the University nor the names of its contributors 27 * may be used to endorse or promote products derived from this software 28 * without specific prior written permission. 29 * 30 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 * SUCH DAMAGE. 41 * 42 * @(#)subr_autoconf.c 8.1 (Berkeley) 6/10/93 43 * 44 * $Id: subr_autoconf.c,v 1.4 1997/02/22 09:39:15 peter Exp $ 45 */ 46 47#include <sys/param.h> 48#include <sys/kernel.h> 49#include <sys/queue.h> 50#include <sys/systm.h> 51#include <sys/device.h> 52#include <sys/malloc.h> 53 54/* 55 * Autoconfiguration subroutines. 56 */ 57 58#ifdef UNUSED 59/* 60 * ioconf.c exports exactly two names: cfdata and cfroots. All system 61 * devices and drivers are found via these tables. 62 */ 63extern struct cfdata cfdata[]; 64extern short cfroots[]; 65 66#define ROOT ((struct device *)NULL) 67 68struct matchinfo { 69 cfmatch_t fn; 70 struct device *parent; 71 void *aux; 72 struct cfdata *match; 73 int pri; 74}; 75 76/* 77 * Apply the matching function and choose the best. This is used 78 * a few times and we want to keep the code small. 79 */ 80static void 81mapply(m, cf) 82 register struct matchinfo *m; 83 register struct cfdata *cf; 84{ 85 register int pri; 86 87 if (m->fn != NULL) 88 pri = (*m->fn)(m->parent, cf, m->aux); 89 else 90 pri = (*cf->cf_driver->cd_match)(m->parent, cf, m->aux); 91 if (pri > m->pri) { 92 m->match = cf; 93 m->pri = pri; 94 } 95} 96 97/* 98 * Iterate over all potential children of some device, calling the given 99 * function (default being the child's match function) for each one. 100 * Nonzero returns are matches; the highest value returned is considered 101 * the best match. Return the `found child' if we got a match, or NULL 102 * otherwise. The `aux' pointer is simply passed on through. 103 * 104 * Note that this function is designed so that it can be used to apply 105 * an arbitrary function to all potential children (its return value 106 * can be ignored). 107 */ 108struct cfdata * 109config_search(fn, parent, aux) 110 cfmatch_t fn; 111 register struct device *parent; 112 void *aux; 113{ 114 register struct cfdata *cf; 115 register short *p; 116 struct matchinfo m; 117 118 m.fn = fn; 119 m.parent = parent; 120 m.aux = aux; 121 m.match = NULL; 122 m.pri = 0; 123 for (cf = cfdata; cf->cf_driver; cf++) { 124 /* 125 * Skip cf if no longer eligible, otherwise scan through 126 * parents for one matching `parent', and try match function. 127 */ 128 if (cf->cf_fstate == FSTATE_FOUND) 129 continue; 130 for (p = cf->cf_parents; *p >= 0; p++) 131 if (parent->dv_cfdata == &cfdata[*p]) 132 mapply(&m, cf); 133 } 134 return (m.match); 135} 136 137/* 138 * Find the given root device. 139 * This is much like config_search, but there is no parent. 140 */ 141struct cfdata * 142config_rootsearch(fn, rootname, aux) 143 register cfmatch_t fn; 144 register char *rootname; 145 register void *aux; 146{ 147 register struct cfdata *cf; 148 register short *p; 149 struct matchinfo m; 150 151 m.fn = fn; 152 m.parent = ROOT; 153 m.aux = aux; 154 m.match = NULL; 155 m.pri = 0; 156 /* 157 * Look at root entries for matching name. We do not bother 158 * with found-state here since only one root should ever be 159 * searched (and it must be done first). 160 */ 161 for (p = cfroots; *p >= 0; p++) { 162 cf = &cfdata[*p]; 163 if (strcmp(cf->cf_driver->cd_name, rootname) == 0) 164 mapply(&m, cf); 165 } 166 return (m.match); 167} 168 169static char *msgs[3] = { "", " not configured\n", " unsupported\n" }; 170 171/* 172 * The given `aux' argument describes a device that has been found 173 * on the given parent, but not necessarily configured. Locate the 174 * configuration data for that device (using the cd_match configuration 175 * driver function) and attach it, and return true. If the device was 176 * not configured, call the given `print' function and return 0. 177 */ 178int 179config_found(parent, aux, print) 180 struct device *parent; 181 void *aux; 182 cfprint_t print; 183{ 184 struct cfdata *cf; 185 186 if ((cf = config_search((cfmatch_t)NULL, parent, aux)) != NULL) { 187 config_attach(parent, cf, aux, print); 188 return (1); 189 } 190 printf(msgs[(*print)(aux, parent->dv_xname)]); 191 return (0); 192} 193 194/* 195 * As above, but for root devices. 196 */ 197int 198config_rootfound(rootname, aux) 199 char *rootname; 200 void *aux; 201{ 202 struct cfdata *cf; 203 204 if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) { 205 config_attach(ROOT, cf, aux, (cfprint_t)NULL); 206 return (1); 207 } 208 printf("root device %s not configured\n", rootname); 209 return (0); 210} 211 212/* just like sprintf(buf, "%d") except that it works from the end */ 213static char * 214number(ep, n) 215 register char *ep; 216 register int n; 217{ 218 219 *--ep = 0; 220 while (n >= 10) { 221 *--ep = (n % 10) + '0'; 222 n /= 10; 223 } 224 *--ep = n + '0'; 225 return (ep); 226} 227 228/* 229 * Attach a found device. Allocates memory for device variables. 230 */ 231void 232config_attach(parent, cf, aux, print) 233 register struct device *parent; 234 register struct cfdata *cf; 235 register void *aux; 236 cfprint_t print; 237{ 238 register struct device *dev; 239 register struct cfdriver *cd; 240 register size_t lname, lunit; 241 register char *xunit; 242 int myunit; 243 char num[10]; 244 static struct device **nextp = &alldevs; 245 246 cd = cf->cf_driver; 247 if (cd->cd_devsize < sizeof(struct device)) 248 panic("config_attach"); 249 myunit = cf->cf_unit; 250 if (cf->cf_fstate == FSTATE_NOTFOUND) 251 cf->cf_fstate = FSTATE_FOUND; 252 else 253 cf->cf_unit++; 254 255 /* compute length of name and decimal expansion of unit number */ 256 lname = strlen(cd->cd_name); 257 xunit = number(&num[sizeof num], myunit); 258 lunit = &num[sizeof num] - xunit; 259 if (lname + lunit >= sizeof(dev->dv_xname)) 260 panic("config_attach: device name too long"); 261 262 /* get memory for all device vars */ 263 dev = (struct device *)malloc(cd->cd_devsize, M_DEVBUF, M_WAITOK); 264 /* XXX cannot wait! */ 265 bzero(dev, cd->cd_devsize); 266 *nextp = dev; /* link up */ 267 nextp = &dev->dv_next; 268 dev->dv_class = cd->cd_class; 269 dev->dv_cfdata = cf; 270 dev->dv_unit = myunit; 271 bcopy(cd->cd_name, dev->dv_xname, lname); 272 bcopy(xunit, dev->dv_xname + lname, lunit); 273 dev->dv_parent = parent; 274 if (parent == ROOT) 275 printf("%s (root)", dev->dv_xname); 276 else { 277 printf("%s at %s", dev->dv_xname, parent->dv_xname); 278 (void) (*print)(aux, (char *)0); 279 } 280 281 /* put this device in the devices array */ 282 if (dev->dv_unit >= cd->cd_ndevs) { 283 /* 284 * Need to expand the array. 285 */ 286 int old = cd->cd_ndevs, oldbytes, new, newbytes; 287 void **nsp; 288 289 if (old == 0) { 290 nsp = malloc(MINALLOCSIZE, M_DEVBUF, M_WAITOK); /*XXX*/ 291 bzero(nsp, MINALLOCSIZE); 292 cd->cd_ndevs = MINALLOCSIZE / sizeof(void *); 293 } else { 294 new = cd->cd_ndevs; 295 do { 296 new *= 2; 297 } while (new <= dev->dv_unit); 298 cd->cd_ndevs = new; 299 oldbytes = old * sizeof(void *); 300 newbytes = new * sizeof(void *); 301 nsp = malloc(newbytes, M_DEVBUF, M_WAITOK); /*XXX*/ 302 bcopy(cd->cd_devs, nsp, oldbytes); 303 bzero(&nsp[old], newbytes - oldbytes); 304 free(cd->cd_devs, M_DEVBUF); 305 } 306 cd->cd_devs = nsp; 307 } 308 if (cd->cd_devs[dev->dv_unit]) 309 panic("config_attach: duplicate %s", dev->dv_xname); 310 cd->cd_devs[dev->dv_unit] = dev; 311 312 /* 313 * Before attaching, clobber any unfound devices that are 314 * otherwise identical. 315 */ 316 for (cf = cfdata; cf->cf_driver; cf++) 317 if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit && 318 cf->cf_fstate == FSTATE_NOTFOUND) 319 cf->cf_fstate = FSTATE_FOUND; 320 (*cd->cd_attach)(parent, dev, aux); 321} 322 323/* 324 * Attach an event. These must come from initially-zero space (see 325 * commented-out assignments below), but that occurs naturally for 326 * device instance variables. 327 */ 328void 329evcnt_attach(dev, name, ev) 330 struct device *dev; 331 const char *name; 332 struct evcnt *ev; 333{ 334 static struct evcnt **nextp = &allevents; 335 336#ifdef DIAGNOSTIC 337 if (strlen(name) >= sizeof(ev->ev_name)) 338 panic("evcnt_attach"); 339#endif 340 /* ev->ev_next = NULL; */ 341 ev->ev_dev = dev; 342 /* ev->ev_count = 0; */ 343 strcpy(ev->ev_name, name); 344 *nextp = ev; 345 nextp = &ev->ev_next; 346} 347 348#endif 349 350/* 351 * "Interrupt driven config" functions. 352 */ 353static TAILQ_HEAD(, intr_config_hook) intr_config_hook_list = 354 TAILQ_HEAD_INITIALIZER(intr_config_hook_list); 355 356 357/* ARGSUSED */ 358static void run_interrupt_driven_config_hooks __P((void *dummy)); 359static void 360run_interrupt_driven_config_hooks(dummy) 361 void *dummy; 362{ 363 struct intr_config_hook *hook; 364 365 for (hook = intr_config_hook_list.tqh_first; hook != NULL; 366 hook = hook->ich_links.tqe_next) { 367 (*hook->ich_func)(hook->ich_arg); 368 } 369 370 while (intr_config_hook_list.tqh_first != NULL) { 371 tsleep(&intr_config_hook_list, PCONFIG, "conifhk", 0); 372 } 373} 374SYSINIT(intr_config_hooks, SI_SUB_INT_CONFIG_HOOKS, SI_ORDER_FIRST, 375 run_interrupt_driven_config_hooks, NULL) 376 377/* 378 * Register a hook that will be called after "cold" 379 * autoconfiguration is complete and interrupts can 380 * be used to complete initialization. 381 */ 382int 383config_intrhook_establish(hook) 384 struct intr_config_hook *hook; 385{ 386 struct intr_config_hook *hook_entry; 387 388 for (hook_entry = intr_config_hook_list.tqh_first; hook_entry != NULL; 389 hook_entry = hook_entry->ich_links.tqe_next) 390 if (hook_entry == hook) 391 break; 392 if (hook_entry != NULL) { 393 printf("config_intrhook_establish: establishing an " 394 "already established hook.\n"); 395 return (1); 396 } 397 TAILQ_INSERT_TAIL(&intr_config_hook_list, hook, ich_links); 398 if (cold == 0) 399 /* XXX Sufficient for LKMs loaded after initial config??? */ 400 run_interrupt_driven_config_hooks(NULL); 401 return (0); 402} 403 404void 405config_intrhook_disestablish(hook) 406 struct intr_config_hook *hook; 407{ 408 struct intr_config_hook *hook_entry; 409 410 for (hook_entry = intr_config_hook_list.tqh_first; hook_entry != NULL; 411 hook_entry = hook_entry->ich_links.tqe_next) 412 if (hook_entry == hook) 413 break; 414 if (hook_entry == NULL) 415 panic("config_intrhook_disestablish: disestablishing an " 416 "unestablished hook"); 417 418 TAILQ_REMOVE(&intr_config_hook_list, hook, ich_links); 419 /* Wakeup anyone watching the list */ 420 wakeup(&intr_config_hook_list); 421} 422