1 /* $NetBSD: subr_autoconf.c,v 1.27 1997/09/20 14:16:43 drochner Exp $ */ 2 3 /* 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This software was developed by the Computer Systems Engineering group 8 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 9 * contributed to Berkeley. 10 * 11 * All advertising materials mentioning features or use of this software 12 * must display the following acknowledgement: 13 * This product includes software developed by the University of 14 * California, Lawrence Berkeley Laboratories. 15 * 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions 18 * are met: 19 * 1. Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution. 24 * 3. All advertising materials mentioning features or use of this software 25 * must display the following acknowledgement: 26 * This product includes software developed by the University of 27 * California, Berkeley and its contributors. 28 * 4. Neither the name of the University nor the names of its contributors 29 * may be used to endorse or promote products derived from this software 30 * without specific prior written permission. 31 * 32 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 35 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 42 * SUCH DAMAGE. 43 * 44 * from: Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp (LBL) 45 * 46 * @(#)subr_autoconf.c 8.1 (Berkeley) 6/10/93 47 */ 48 49 #include <sys/param.h> 50 #include <sys/device.h> 51 #include <sys/malloc.h> 52 #include <sys/systm.h> 53 #include <machine/limits.h> 54 55 /* 56 * Autoconfiguration subroutines. 57 */ 58 59 /* 60 * ioconf.c exports exactly two names: cfdata and cfroots. All system 61 * devices and drivers are found via these tables. 62 */ 63 extern struct cfdata cfdata[]; 64 extern short cfroots[]; 65 66 #define ROOT ((struct device *)NULL) 67 68 #ifdef __BROKEN_INDIRECT_CONFIG 69 struct device *config_make_softc __P((struct device *, struct cfdata *)); 70 #endif 71 72 struct matchinfo { 73 cfmatch_t fn; 74 struct device *parent; 75 void *aux; 76 #ifdef __BROKEN_INDIRECT_CONFIG 77 void *match; 78 int indirect; 79 #else 80 struct cfdata *match; 81 #endif 82 int pri; 83 }; 84 85 static char *number __P((char *, int)); 86 static void mapply __P((struct matchinfo *, struct cfdata *)); 87 88 struct devicelist alldevs; /* list of all devices */ 89 struct evcntlist allevents; /* list of all event counters */ 90 91 /* 92 * Initialize autoconfiguration data structures. 93 */ 94 void 95 config_init() 96 { 97 98 TAILQ_INIT(&alldevs); 99 TAILQ_INIT(&allevents); 100 } 101 102 /* 103 * Apply the matching function and choose the best. This is used 104 * a few times and we want to keep the code small. 105 */ 106 static void 107 mapply(m, cf) 108 register struct matchinfo *m; 109 register struct cfdata *cf; 110 { 111 register int pri; 112 #ifdef __BROKEN_INDIRECT_CONFIG 113 void *match; 114 115 if (m->indirect) 116 match = config_make_softc(m->parent, cf); 117 else 118 match = cf; 119 #endif 120 121 if (m->fn != NULL) 122 #ifdef __BROKEN_INDIRECT_CONFIG 123 pri = (*m->fn)(m->parent, match, m->aux); 124 #else 125 pri = (*m->fn)(m->parent, cf, m->aux); 126 #endif 127 else { 128 if (cf->cf_attach->ca_match == NULL) { 129 panic("mapply: no match function for '%s' device\n", 130 cf->cf_driver->cd_name); 131 } 132 #ifdef __BROKEN_INDIRECT_CONFIG 133 pri = (*cf->cf_attach->ca_match)(m->parent, match, m->aux); 134 #else 135 pri = (*cf->cf_attach->ca_match)(m->parent, cf, m->aux); 136 #endif 137 } 138 if (pri > m->pri) { 139 #ifdef __BROKEN_INDIRECT_CONFIG 140 if (m->indirect && m->match) 141 free(m->match, M_DEVBUF); 142 m->match = match; 143 #else 144 m->match = cf; 145 #endif 146 m->pri = pri; 147 #ifdef __BROKEN_INDIRECT_CONFIG 148 } else { 149 if (m->indirect) 150 free(match, M_DEVBUF); 151 #endif 152 } 153 } 154 155 /* 156 * Iterate over all potential children of some device, calling the given 157 * function (default being the child's match function) for each one. 158 * Nonzero returns are matches; the highest value returned is considered 159 * the best match. Return the `found child' if we got a match, or NULL 160 * otherwise. The `aux' pointer is simply passed on through. 161 * 162 * Note that this function is designed so that it can be used to apply 163 * an arbitrary function to all potential children (its return value 164 * can be ignored). 165 */ 166 #ifdef __BROKEN_INDIRECT_CONFIG 167 void * 168 #else 169 struct cfdata * 170 #endif 171 config_search(fn, parent, aux) 172 cfmatch_t fn; 173 register struct device *parent; 174 void *aux; 175 { 176 register struct cfdata *cf; 177 register short *p; 178 struct matchinfo m; 179 180 m.fn = fn; 181 m.parent = parent; 182 m.aux = aux; 183 m.match = NULL; 184 #ifdef __BROKEN_INDIRECT_CONFIG 185 m.indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect; 186 #endif 187 m.pri = 0; 188 for (cf = cfdata; cf->cf_driver; cf++) { 189 /* 190 * Skip cf if no longer eligible, otherwise scan through 191 * parents for one matching `parent', and try match function. 192 */ 193 if (cf->cf_fstate == FSTATE_FOUND) 194 continue; 195 for (p = cf->cf_parents; *p >= 0; p++) 196 if (parent->dv_cfdata == &cfdata[*p]) 197 mapply(&m, cf); 198 } 199 return (m.match); 200 } 201 202 #ifdef __BROKEN_INDIRECT_CONFIG 203 /* 204 * Iterate over all potential children of some device, calling the given 205 * function for each one. 206 * 207 * Note that this function is designed so that it can be used to apply 208 * an arbitrary function to all potential children (its return value 209 * can be ignored). 210 */ 211 void 212 config_scan(fn, parent) 213 cfscan_t fn; 214 register struct device *parent; 215 { 216 register struct cfdata *cf; 217 register short *p; 218 void *match; 219 int indirect; 220 221 indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect; 222 for (cf = cfdata; cf->cf_driver; cf++) { 223 /* 224 * Skip cf if no longer eligible, otherwise scan through 225 * parents for one matching `parent', and try match function. 226 */ 227 if (cf->cf_fstate == FSTATE_FOUND) 228 continue; 229 for (p = cf->cf_parents; *p >= 0; p++) 230 if (parent->dv_cfdata == &cfdata[*p]) { 231 if (indirect) 232 match = config_make_softc(parent, cf); 233 else 234 match = cf; 235 (*fn)(parent, match); 236 } 237 } 238 } 239 #endif /* __BROKEN_INDIRECT_CONFIG */ 240 241 /* 242 * Find the given root device. 243 * This is much like config_search, but there is no parent. 244 */ 245 #ifdef __BROKEN_INDIRECT_CONFIG 246 void * 247 #else 248 struct cfdata * 249 #endif 250 config_rootsearch(fn, rootname, aux) 251 register cfmatch_t fn; 252 register char *rootname; 253 register void *aux; 254 { 255 register struct cfdata *cf; 256 register short *p; 257 struct matchinfo m; 258 259 m.fn = fn; 260 m.parent = ROOT; 261 m.aux = aux; 262 m.match = NULL; 263 #ifdef __BROKEN_INDIRECT_CONFIG 264 m.indirect = 0; 265 #endif 266 m.pri = 0; 267 /* 268 * Look at root entries for matching name. We do not bother 269 * with found-state here since only one root should ever be 270 * searched (and it must be done first). 271 */ 272 for (p = cfroots; *p >= 0; p++) { 273 cf = &cfdata[*p]; 274 if (strcmp(cf->cf_driver->cd_name, rootname) == 0) 275 mapply(&m, cf); 276 } 277 return (m.match); 278 } 279 280 static char *msgs[3] = { "", " not configured\n", " unsupported\n" }; 281 282 /* 283 * The given `aux' argument describes a device that has been found 284 * on the given parent, but not necessarily configured. Locate the 285 * configuration data for that device (using the submatch function 286 * provided, or using candidates' cd_match configuration driver 287 * functions) and attach it, and return true. If the device was 288 * not configured, call the given `print' function and return 0. 289 */ 290 struct device * 291 config_found_sm(parent, aux, print, submatch) 292 struct device *parent; 293 void *aux; 294 cfprint_t print; 295 cfmatch_t submatch; 296 { 297 #ifdef __BROKEN_INDIRECT_CONFIG 298 void *match; 299 300 if ((match = config_search(submatch, parent, aux)) != NULL) 301 return (config_attach(parent, match, aux, print)); 302 #else 303 struct cfdata *cf; 304 305 if ((cf = config_search(submatch, parent, aux)) != NULL) 306 return (config_attach(parent, cf, aux, print)); 307 #endif 308 if (print) 309 printf(msgs[(*print)(aux, parent->dv_xname)]); 310 return (NULL); 311 } 312 313 /* 314 * As above, but for root devices. 315 */ 316 struct device * 317 config_rootfound(rootname, aux) 318 char *rootname; 319 void *aux; 320 { 321 #ifdef __BROKEN_INDIRECT_CONFIG 322 void *match; 323 324 if ((match = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) 325 return (config_attach(ROOT, match, aux, (cfprint_t)NULL)); 326 #else 327 struct cfdata *cf; 328 329 if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) 330 return (config_attach(ROOT, cf, aux, (cfprint_t)NULL)); 331 #endif 332 printf("root device %s not configured\n", rootname); 333 return (NULL); 334 } 335 336 /* just like sprintf(buf, "%d") except that it works from the end */ 337 static char * 338 number(ep, n) 339 register char *ep; 340 register int n; 341 { 342 343 *--ep = 0; 344 while (n >= 10) { 345 *--ep = (n % 10) + '0'; 346 n /= 10; 347 } 348 *--ep = n + '0'; 349 return (ep); 350 } 351 352 /* 353 * Attach a found device. Allocates memory for device variables. 354 */ 355 #ifdef __BROKEN_INDIRECT_CONFIG 356 struct device * 357 config_attach(parent, match, aux, print) 358 register struct device *parent; 359 void *match; 360 register void *aux; 361 cfprint_t print; 362 { 363 register struct cfdata *cf; 364 register struct device *dev; 365 register struct cfdriver *cd; 366 register struct cfattach *ca; 367 368 if (parent && parent->dv_cfdata->cf_driver->cd_indirect) { 369 dev = match; 370 cf = dev->dv_cfdata; 371 } else { 372 cf = match; 373 dev = config_make_softc(parent, cf); 374 } 375 376 cd = cf->cf_driver; 377 ca = cf->cf_attach; 378 cd->cd_devs[cf->cf_unit] = dev; 379 380 if (cf->cf_fstate == FSTATE_STAR) 381 cf->cf_unit++; 382 else { 383 KASSERT(cf->cf_fstate == FSTATE_NOTFOUND); 384 cf->cf_fstate = FSTATE_FOUND; 385 } 386 387 TAILQ_INSERT_TAIL(&alldevs, dev, dv_list); 388 389 if (parent == ROOT) 390 printf("%s (root)", dev->dv_xname); 391 else { 392 printf("%s at %s", dev->dv_xname, parent->dv_xname); 393 if (print) 394 (void) (*print)(aux, (char *)0); 395 } 396 397 /* 398 * Before attaching, clobber any unfound devices that are 399 * otherwise identical, or bump the unit number on all starred 400 * cfdata for this device. 401 */ 402 for (cf = cfdata; cf->cf_driver; cf++) 403 if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit) { 404 if (cf->cf_fstate == FSTATE_NOTFOUND) 405 cf->cf_fstate = FSTATE_FOUND; 406 if (cf->cf_fstate == FSTATE_STAR) 407 cf->cf_unit++; 408 } 409 #if defined(__alpha__) || defined(hp300) || defined(__i386__) 410 device_register(dev, aux); 411 #endif 412 (*ca->ca_attach)(parent, dev, aux); 413 return (dev); 414 } 415 416 struct device * 417 config_make_softc(parent, cf) 418 struct device *parent; 419 struct cfdata *cf; 420 { 421 register struct device *dev; 422 register struct cfdriver *cd; 423 register struct cfattach *ca; 424 register size_t lname, lunit; 425 register char *xunit; 426 char num[10]; 427 428 cd = cf->cf_driver; 429 ca = cf->cf_attach; 430 if (ca->ca_devsize < sizeof(struct device)) 431 panic("config_make_softc"); 432 433 /* compute length of name and decimal expansion of unit number */ 434 lname = strlen(cd->cd_name); 435 xunit = number(&num[sizeof num], cf->cf_unit); 436 lunit = &num[sizeof num] - xunit; 437 if (lname + lunit >= sizeof(dev->dv_xname)) 438 panic("config_attach: device name too long"); 439 440 /* get memory for all device vars */ 441 dev = (struct device *)malloc(ca->ca_devsize, M_DEVBUF, M_NOWAIT); 442 if (!dev) 443 panic("config_attach: memory allocation for device softc failed"); 444 bzero(dev, ca->ca_devsize); 445 dev->dv_class = cd->cd_class; 446 dev->dv_cfdata = cf; 447 dev->dv_unit = cf->cf_unit; 448 bcopy(cd->cd_name, dev->dv_xname, lname); 449 bcopy(xunit, dev->dv_xname + lname, lunit); 450 dev->dv_parent = parent; 451 452 /* put this device in the devices array */ 453 if (dev->dv_unit >= cd->cd_ndevs) { 454 /* 455 * Need to expand the array. 456 */ 457 int old = cd->cd_ndevs, new; 458 void **nsp; 459 460 if (old == 0) 461 new = MINALLOCSIZE / sizeof(void *); 462 else 463 new = old * 2; 464 while (new <= dev->dv_unit) 465 new *= 2; 466 cd->cd_ndevs = new; 467 nsp = malloc(new * sizeof(void *), M_DEVBUF, M_NOWAIT); 468 if (nsp == 0) 469 panic("config_attach: %sing dev array", 470 old != 0 ? "expand" : "creat"); 471 bzero(nsp + old, (new - old) * sizeof(void *)); 472 if (old != 0) { 473 bcopy(cd->cd_devs, nsp, old * sizeof(void *)); 474 free(cd->cd_devs, M_DEVBUF); 475 } 476 cd->cd_devs = nsp; 477 } 478 if (cd->cd_devs[dev->dv_unit]) 479 panic("config_attach: duplicate %s", dev->dv_xname); 480 481 return (dev); 482 } 483 #else /* __BROKEN_INDIRECT_CONFIG */ 484 struct device * 485 config_attach(parent, cf, aux, print) 486 register struct device *parent; 487 register struct cfdata *cf; 488 register void *aux; 489 cfprint_t print; 490 { 491 register struct device *dev; 492 register struct cfdriver *cd; 493 register struct cfattach *ca; 494 register size_t lname, lunit; 495 register char *xunit; 496 int myunit; 497 char num[10]; 498 499 cd = cf->cf_driver; 500 ca = cf->cf_attach; 501 if (ca->ca_devsize < sizeof(struct device)) 502 panic("config_attach"); 503 myunit = cf->cf_unit; 504 if (cf->cf_fstate == FSTATE_STAR) 505 cf->cf_unit++; 506 else { 507 KASSERT(cf->cf_fstate == FSTATE_NOTFOUND); 508 cf->cf_fstate = FSTATE_FOUND; 509 } 510 511 /* compute length of name and decimal expansion of unit number */ 512 lname = strlen(cd->cd_name); 513 xunit = number(&num[sizeof num], myunit); 514 lunit = &num[sizeof num] - xunit; 515 if (lname + lunit >= sizeof(dev->dv_xname)) 516 panic("config_attach: device name too long"); 517 518 /* get memory for all device vars */ 519 dev = (struct device *)malloc(ca->ca_devsize, M_DEVBUF, M_NOWAIT); 520 if (!dev) 521 panic("config_attach: memory allocation for device softc failed"); 522 bzero(dev, ca->ca_devsize); 523 TAILQ_INSERT_TAIL(&alldevs, dev, dv_list); /* link up */ 524 dev->dv_class = cd->cd_class; 525 dev->dv_cfdata = cf; 526 dev->dv_unit = myunit; 527 bcopy(cd->cd_name, dev->dv_xname, lname); 528 bcopy(xunit, dev->dv_xname + lname, lunit); 529 dev->dv_parent = parent; 530 if (parent == ROOT) 531 printf("%s (root)", dev->dv_xname); 532 else { 533 printf("%s at %s", dev->dv_xname, parent->dv_xname); 534 if (print) 535 (void) (*print)(aux, (char *)0); 536 } 537 538 /* put this device in the devices array */ 539 if (dev->dv_unit >= cd->cd_ndevs) { 540 /* 541 * Need to expand the array. 542 */ 543 int old = cd->cd_ndevs, new; 544 void **nsp; 545 546 if (old == 0) 547 new = MINALLOCSIZE / sizeof(void *); 548 else 549 new = old * 2; 550 while (new <= dev->dv_unit) 551 new *= 2; 552 cd->cd_ndevs = new; 553 nsp = malloc(new * sizeof(void *), M_DEVBUF, M_NOWAIT); 554 if (nsp == 0) 555 panic("config_attach: %sing dev array", 556 old != 0 ? "expand" : "creat"); 557 bzero(nsp + old, (new - old) * sizeof(void *)); 558 if (old != 0) { 559 bcopy(cd->cd_devs, nsp, old * sizeof(void *)); 560 free(cd->cd_devs, M_DEVBUF); 561 } 562 cd->cd_devs = nsp; 563 } 564 if (cd->cd_devs[dev->dv_unit]) 565 panic("config_attach: duplicate %s", dev->dv_xname); 566 cd->cd_devs[dev->dv_unit] = dev; 567 568 /* 569 * Before attaching, clobber any unfound devices that are 570 * otherwise identical, or bump the unit number on all starred 571 * cfdata for this device. 572 */ 573 for (cf = cfdata; cf->cf_driver; cf++) 574 if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit) { 575 if (cf->cf_fstate == FSTATE_NOTFOUND) 576 cf->cf_fstate = FSTATE_FOUND; 577 if (cf->cf_fstate == FSTATE_STAR) 578 cf->cf_unit++; 579 } 580 #if defined(__alpha__) || defined(hp300) || defined(__i386__) 581 device_register(dev, aux); 582 #endif 583 (*ca->ca_attach)(parent, dev, aux); 584 return (dev); 585 } 586 #endif /* __BROKEN_INDIRECT_CONFIG */ 587 588 /* 589 * Attach an event. These must come from initially-zero space (see 590 * commented-out assignments below), but that occurs naturally for 591 * device instance variables. 592 */ 593 void 594 evcnt_attach(dev, name, ev) 595 struct device *dev; 596 const char *name; 597 struct evcnt *ev; 598 { 599 600 #ifdef DIAGNOSTIC 601 if (strlen(name) >= sizeof(ev->ev_name)) 602 panic("evcnt_attach"); 603 #endif 604 /* ev->ev_next = NULL; */ 605 ev->ev_dev = dev; 606 /* ev->ev_count = 0; */ 607 strcpy(ev->ev_name, name); 608 TAILQ_INSERT_TAIL(&allevents, ev, ev_list); 609 } 610