1 /* $NetBSD: subr_autoconf.c,v 1.49 2000/02/01 04:01:19 danw 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.3 (Berkeley) 5/17/94 47 */ 48 49 #include <sys/param.h> 50 #include <sys/device.h> 51 #include <sys/malloc.h> 52 #include <sys/systm.h> 53 #include <sys/kernel.h> 54 #include <sys/errno.h> 55 #include <sys/proc.h> 56 #include <machine/limits.h> 57 58 /* 59 * Autoconfiguration subroutines. 60 */ 61 62 /* 63 * ioconf.c exports exactly two names: cfdata and cfroots. All system 64 * devices and drivers are found via these tables. 65 */ 66 extern struct cfdata cfdata[]; 67 extern short cfroots[]; 68 69 #define ROOT ((struct device *)NULL) 70 71 struct matchinfo { 72 cfmatch_t fn; 73 struct device *parent; 74 void *aux; 75 struct cfdata *match; 76 int pri; 77 }; 78 79 static char *number __P((char *, int)); 80 static void mapply __P((struct matchinfo *, struct cfdata *)); 81 82 struct deferred_config { 83 TAILQ_ENTRY(deferred_config) dc_queue; 84 struct device *dc_dev; 85 void (*dc_func) __P((struct device *)); 86 }; 87 88 TAILQ_HEAD(deferred_config_head, deferred_config); 89 90 struct deferred_config_head deferred_config_queue; 91 struct deferred_config_head interrupt_config_queue; 92 93 static void config_process_deferred __P((struct deferred_config_head *, 94 struct device *)); 95 96 struct devicelist alldevs; /* list of all devices */ 97 struct evcntlist allevents; /* list of all event counters */ 98 99 __volatile int config_pending; /* semaphore for mountroot */ 100 101 /* 102 * Configure the system's hardware. 103 */ 104 void 105 configure() 106 { 107 108 TAILQ_INIT(&deferred_config_queue); 109 TAILQ_INIT(&interrupt_config_queue); 110 TAILQ_INIT(&alldevs); 111 TAILQ_INIT(&allevents); 112 113 /* 114 * Do the machine-dependent portion of autoconfiguration. This 115 * sets the configuration machinery here in motion by "finding" 116 * the root bus. When this function returns, we expect interrupts 117 * to be enabled. 118 */ 119 cpu_configure(); 120 121 /* 122 * Now that we've found all the hardware, start the real time 123 * and statistics clocks. 124 */ 125 initclocks(); 126 127 cold = 0; /* clocks are running, we're warm now! */ 128 129 /* 130 * Now callback to finish configuration for devices which want 131 * to do this once interrupts are enabled. 132 */ 133 config_process_deferred(&interrupt_config_queue, NULL); 134 } 135 136 /* 137 * Apply the matching function and choose the best. This is used 138 * a few times and we want to keep the code small. 139 */ 140 static void 141 mapply(m, cf) 142 register struct matchinfo *m; 143 register struct cfdata *cf; 144 { 145 register int pri; 146 147 if (m->fn != NULL) 148 pri = (*m->fn)(m->parent, cf, m->aux); 149 else { 150 if (cf->cf_attach->ca_match == NULL) { 151 panic("mapply: no match function for '%s' device\n", 152 cf->cf_driver->cd_name); 153 } 154 pri = (*cf->cf_attach->ca_match)(m->parent, cf, m->aux); 155 } 156 if (pri > m->pri) { 157 m->match = cf; 158 m->pri = pri; 159 } 160 } 161 162 /* 163 * Iterate over all potential children of some device, calling the given 164 * function (default being the child's match function) for each one. 165 * Nonzero returns are matches; the highest value returned is considered 166 * the best match. Return the `found child' if we got a match, or NULL 167 * otherwise. The `aux' pointer is simply passed on through. 168 * 169 * Note that this function is designed so that it can be used to apply 170 * an arbitrary function to all potential children (its return value 171 * can be ignored). 172 */ 173 struct cfdata * 174 config_search(fn, parent, aux) 175 cfmatch_t fn; 176 register struct device *parent; 177 void *aux; 178 { 179 register struct cfdata *cf; 180 register short *p; 181 struct matchinfo m; 182 183 m.fn = fn; 184 m.parent = parent; 185 m.aux = aux; 186 m.match = NULL; 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 /* 203 * Find the given root device. 204 * This is much like config_search, but there is no parent. 205 */ 206 struct cfdata * 207 config_rootsearch(fn, rootname, aux) 208 register cfmatch_t fn; 209 register char *rootname; 210 register void *aux; 211 { 212 register struct cfdata *cf; 213 register short *p; 214 struct matchinfo m; 215 216 m.fn = fn; 217 m.parent = ROOT; 218 m.aux = aux; 219 m.match = NULL; 220 m.pri = 0; 221 /* 222 * Look at root entries for matching name. We do not bother 223 * with found-state here since only one root should ever be 224 * searched (and it must be done first). 225 */ 226 for (p = cfroots; *p >= 0; p++) { 227 cf = &cfdata[*p]; 228 if (strcmp(cf->cf_driver->cd_name, rootname) == 0) 229 mapply(&m, cf); 230 } 231 return (m.match); 232 } 233 234 static char *msgs[3] = { "", " not configured\n", " unsupported\n" }; 235 236 /* 237 * The given `aux' argument describes a device that has been found 238 * on the given parent, but not necessarily configured. Locate the 239 * configuration data for that device (using the submatch function 240 * provided, or using candidates' cd_match configuration driver 241 * functions) and attach it, and return true. If the device was 242 * not configured, call the given `print' function and return 0. 243 */ 244 struct device * 245 config_found_sm(parent, aux, print, submatch) 246 struct device *parent; 247 void *aux; 248 cfprint_t print; 249 cfmatch_t submatch; 250 { 251 struct cfdata *cf; 252 253 if ((cf = config_search(submatch, parent, aux)) != NULL) 254 return (config_attach(parent, cf, aux, print)); 255 if (print) 256 printf(msgs[(*print)(aux, parent->dv_xname)]); 257 return (NULL); 258 } 259 260 /* 261 * As above, but for root devices. 262 */ 263 struct device * 264 config_rootfound(rootname, aux) 265 char *rootname; 266 void *aux; 267 { 268 struct cfdata *cf; 269 270 if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) 271 return (config_attach(ROOT, cf, aux, (cfprint_t)NULL)); 272 printf("root device %s not configured\n", rootname); 273 return (NULL); 274 } 275 276 /* just like sprintf(buf, "%d") except that it works from the end */ 277 static char * 278 number(ep, n) 279 register char *ep; 280 register int n; 281 { 282 283 *--ep = 0; 284 while (n >= 10) { 285 *--ep = (n % 10) + '0'; 286 n /= 10; 287 } 288 *--ep = n + '0'; 289 return (ep); 290 } 291 292 /* 293 * Attach a found device. Allocates memory for device variables. 294 */ 295 struct device * 296 config_attach(parent, cf, aux, print) 297 register struct device *parent; 298 register struct cfdata *cf; 299 register void *aux; 300 cfprint_t print; 301 { 302 register struct device *dev; 303 register struct cfdriver *cd; 304 register struct cfattach *ca; 305 register size_t lname, lunit; 306 register char *xunit; 307 int myunit; 308 char num[10]; 309 310 cd = cf->cf_driver; 311 ca = cf->cf_attach; 312 if (ca->ca_devsize < sizeof(struct device)) 313 panic("config_attach"); 314 #ifndef __BROKEN_CONFIG_UNIT_USAGE 315 if (cf->cf_fstate == FSTATE_STAR) { 316 for (myunit = cf->cf_unit; myunit < cd->cd_ndevs; myunit++) 317 if (cd->cd_devs[myunit] == NULL) 318 break; 319 /* 320 * myunit is now the unit of the first NULL device pointer, 321 * or max(cd->cd_ndevs,cf->cf_unit). 322 */ 323 } else { 324 myunit = cf->cf_unit; 325 #else /* __BROKEN_CONFIG_UNIT_USAGE */ 326 myunit = cf->cf_unit; 327 if (cf->cf_fstate == FSTATE_STAR) 328 cf->cf_unit++; 329 else { 330 #endif /* __BROKEN_CONFIG_UNIT_USAGE */ 331 KASSERT(cf->cf_fstate == FSTATE_NOTFOUND); 332 cf->cf_fstate = FSTATE_FOUND; 333 } 334 335 /* compute length of name and decimal expansion of unit number */ 336 lname = strlen(cd->cd_name); 337 xunit = number(&num[sizeof(num)], myunit); 338 lunit = &num[sizeof(num)] - xunit; 339 if (lname + lunit >= sizeof(dev->dv_xname)) 340 panic("config_attach: device name too long"); 341 342 /* get memory for all device vars */ 343 dev = (struct device *)malloc(ca->ca_devsize, M_DEVBUF, 344 cold ? M_NOWAIT : M_WAITOK); 345 if (!dev) 346 panic("config_attach: memory allocation for device softc failed"); 347 memset(dev, 0, ca->ca_devsize); 348 TAILQ_INSERT_TAIL(&alldevs, dev, dv_list); /* link up */ 349 dev->dv_class = cd->cd_class; 350 dev->dv_cfdata = cf; 351 dev->dv_unit = myunit; 352 memcpy(dev->dv_xname, cd->cd_name, lname); 353 memcpy(dev->dv_xname + lname, xunit, lunit); 354 dev->dv_parent = parent; 355 dev->dv_flags = DVF_ACTIVE; /* always initially active */ 356 357 if (parent == ROOT) 358 printf("%s (root)", dev->dv_xname); 359 else { 360 printf("%s at %s", dev->dv_xname, parent->dv_xname); 361 if (print) 362 (void) (*print)(aux, (char *)0); 363 } 364 365 /* put this device in the devices array */ 366 if (dev->dv_unit >= cd->cd_ndevs) { 367 /* 368 * Need to expand the array. 369 */ 370 int old = cd->cd_ndevs, new; 371 void **nsp; 372 373 if (old == 0) 374 new = MINALLOCSIZE / sizeof(void *); 375 else 376 new = old * 2; 377 while (new <= dev->dv_unit) 378 new *= 2; 379 cd->cd_ndevs = new; 380 nsp = malloc(new * sizeof(void *), M_DEVBUF, 381 cold ? M_NOWAIT : M_WAITOK); 382 if (nsp == 0) 383 panic("config_attach: %sing dev array", 384 old != 0 ? "expand" : "creat"); 385 memset(nsp + old, 0, (new - old) * sizeof(void *)); 386 if (old != 0) { 387 memcpy(nsp, cd->cd_devs, old * sizeof(void *)); 388 free(cd->cd_devs, M_DEVBUF); 389 } 390 cd->cd_devs = nsp; 391 } 392 if (cd->cd_devs[dev->dv_unit]) 393 panic("config_attach: duplicate %s", dev->dv_xname); 394 cd->cd_devs[dev->dv_unit] = dev; 395 396 /* 397 * Before attaching, clobber any unfound devices that are 398 * otherwise identical. 399 */ 400 #ifdef __BROKEN_CONFIG_UNIT_USAGE 401 /* bump the unit number on all starred cfdata for this device. */ 402 #endif /* __BROKEN_CONFIG_UNIT_USAGE */ 403 for (cf = cfdata; cf->cf_driver; cf++) 404 if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit) { 405 if (cf->cf_fstate == FSTATE_NOTFOUND) 406 cf->cf_fstate = FSTATE_FOUND; 407 #ifdef __BROKEN_CONFIG_UNIT_USAGE 408 if (cf->cf_fstate == FSTATE_STAR) 409 cf->cf_unit++; 410 #endif /* __BROKEN_CONFIG_UNIT_USAGE */ 411 } 412 #ifdef __HAVE_DEVICE_REGISTER 413 device_register(dev, aux); 414 #endif 415 (*ca->ca_attach)(parent, dev, aux); 416 config_process_deferred(&deferred_config_queue, dev); 417 return (dev); 418 } 419 420 /* 421 * Detach a device. Optionally forced (e.g. because of hardware 422 * removal) and quiet. Returns zero if successful, non-zero 423 * (an error code) otherwise. 424 * 425 * Note that this code wants to be run from a process context, so 426 * that the detach can sleep to allow processes which have a device 427 * open to run and unwind their stacks. 428 */ 429 int 430 config_detach(dev, flags) 431 struct device *dev; 432 int flags; 433 { 434 struct cfdata *cf; 435 struct cfattach *ca; 436 struct cfdriver *cd; 437 #ifdef DIAGNOSTIC 438 struct device *d; 439 #endif 440 int rv = 0, i; 441 442 cf = dev->dv_cfdata; 443 #ifdef DIAGNOSTIC 444 if (cf->cf_fstate != FSTATE_FOUND && cf->cf_fstate != FSTATE_STAR) 445 panic("config_detach: bad device fstate"); 446 #endif 447 ca = cf->cf_attach; 448 cd = cf->cf_driver; 449 450 /* 451 * Ensure the device is deactivated. If the device doesn't 452 * have an activation entry point, we allow DVF_ACTIVE to 453 * remain set. Otherwise, if DVF_ACTIVE is still set, the 454 * device is busy, and the detach fails. 455 */ 456 if (ca->ca_activate != NULL) 457 rv = config_deactivate(dev); 458 459 /* 460 * Try to detach the device. If that's not possible, then 461 * we either panic() (for the forced but failed case), or 462 * return an error. 463 */ 464 if (rv == 0) { 465 if (ca->ca_detach != NULL) 466 rv = (*ca->ca_detach)(dev, flags); 467 else 468 rv = EOPNOTSUPP; 469 } 470 if (rv != 0) { 471 if ((flags & DETACH_FORCE) == 0) 472 return (rv); 473 else 474 panic("config_detach: forced detach of %s failed (%d)", 475 dev->dv_xname, rv); 476 } 477 478 /* 479 * The device has now been successfully detached. 480 */ 481 482 #ifdef DIAGNOSTIC 483 /* 484 * Sanity: If you're successfully detached, you should have no 485 * children. (Note that because children must be attached 486 * after parents, we only need to search the latter part of 487 * the list.) 488 */ 489 for (d = TAILQ_NEXT(dev, dv_list); d != NULL; 490 d = TAILQ_NEXT(d, dv_list)) { 491 if (d->dv_parent == dev) { 492 printf("config_detach: detached device %s" 493 " has children %s\n", dev->dv_xname, d->dv_xname); 494 panic("config_detach"); 495 } 496 } 497 #endif 498 499 /* 500 * Mark cfdata to show that the unit can be reused, if possible. 501 */ 502 #ifdef __BROKEN_CONFIG_UNIT_USAGE 503 /* 504 * Note that we can only re-use a starred unit number if the unit 505 * being detached had the last assigned unit number. 506 */ 507 #endif /* __BROKEN_CONFIG_UNIT_USAGE */ 508 for (cf = cfdata; cf->cf_driver; cf++) { 509 if (cf->cf_driver == cd) { 510 if (cf->cf_fstate == FSTATE_FOUND && 511 cf->cf_unit == dev->dv_unit) 512 cf->cf_fstate = FSTATE_NOTFOUND; 513 #ifdef __BROKEN_CONFIG_UNIT_USAGE 514 if (cf->cf_fstate == FSTATE_STAR && 515 cf->cf_unit == dev->dv_unit + 1) 516 cf->cf_unit--; 517 #endif /* __BROKEN_CONFIG_UNIT_USAGE */ 518 } 519 } 520 521 /* 522 * Unlink from device list. 523 */ 524 TAILQ_REMOVE(&alldevs, dev, dv_list); 525 526 /* 527 * Remove from cfdriver's array, tell the world, and free softc. 528 */ 529 cd->cd_devs[dev->dv_unit] = NULL; 530 if ((flags & DETACH_QUIET) == 0) 531 printf("%s detached\n", dev->dv_xname); 532 free(dev, M_DEVBUF); 533 534 /* 535 * If the device now has no units in use, deallocate its softc array. 536 */ 537 for (i = 0; i < cd->cd_ndevs; i++) 538 if (cd->cd_devs[i] != NULL) 539 break; 540 if (i == cd->cd_ndevs) { /* nothing found; deallocate */ 541 free(cd->cd_devs, M_DEVBUF); 542 cd->cd_devs = NULL; 543 cd->cd_ndevs = 0; 544 } 545 546 /* 547 * Return success. 548 */ 549 return (0); 550 } 551 552 int 553 config_activate(dev) 554 struct device *dev; 555 { 556 struct cfattach *ca = dev->dv_cfdata->cf_attach; 557 int rv = 0, oflags = dev->dv_flags; 558 559 if (ca->ca_activate == NULL) 560 return (EOPNOTSUPP); 561 562 if ((dev->dv_flags & DVF_ACTIVE) == 0) { 563 dev->dv_flags |= DVF_ACTIVE; 564 rv = (*ca->ca_activate)(dev, DVACT_ACTIVATE); 565 if (rv) 566 dev->dv_flags = oflags; 567 } 568 return (rv); 569 } 570 571 int 572 config_deactivate(dev) 573 struct device *dev; 574 { 575 struct cfattach *ca = dev->dv_cfdata->cf_attach; 576 int rv = 0, oflags = dev->dv_flags; 577 578 if (ca->ca_activate == NULL) 579 return (EOPNOTSUPP); 580 581 if (dev->dv_flags & DVF_ACTIVE) { 582 dev->dv_flags &= ~DVF_ACTIVE; 583 rv = (*ca->ca_activate)(dev, DVACT_DEACTIVATE); 584 if (rv) 585 dev->dv_flags = oflags; 586 } 587 return (rv); 588 } 589 590 /* 591 * Defer the configuration of the specified device until all 592 * of its parent's devices have been attached. 593 */ 594 void 595 config_defer(dev, func) 596 struct device *dev; 597 void (*func) __P((struct device *)); 598 { 599 struct deferred_config *dc; 600 601 if (dev->dv_parent == NULL) 602 panic("config_defer: can't defer config of a root device"); 603 604 #ifdef DIAGNOSTIC 605 for (dc = TAILQ_FIRST(&deferred_config_queue); dc != NULL; 606 dc = TAILQ_NEXT(dc, dc_queue)) { 607 if (dc->dc_dev == dev) 608 panic("config_defer: deferred twice"); 609 } 610 #endif 611 612 dc = malloc(sizeof(*dc), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); 613 if (dc == NULL) 614 panic("config_defer: unable to allocate callback"); 615 616 dc->dc_dev = dev; 617 dc->dc_func = func; 618 TAILQ_INSERT_TAIL(&deferred_config_queue, dc, dc_queue); 619 config_pending_incr(); 620 } 621 622 /* 623 * Defer some autoconfiguration for a device until after interrupts 624 * are enabled. 625 */ 626 void 627 config_interrupts(dev, func) 628 struct device *dev; 629 void (*func) __P((struct device *)); 630 { 631 struct deferred_config *dc; 632 633 /* 634 * If interrupts are enabled, callback now. 635 */ 636 if (cold == 0) { 637 (*func)(dev); 638 return; 639 } 640 641 #ifdef DIAGNOSTIC 642 for (dc = TAILQ_FIRST(&interrupt_config_queue); dc != NULL; 643 dc = TAILQ_NEXT(dc, dc_queue)) { 644 if (dc->dc_dev == dev) 645 panic("config_interrupts: deferred twice"); 646 } 647 #endif 648 649 dc = malloc(sizeof(*dc), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); 650 if (dc == NULL) 651 panic("config_interrupts: unable to allocate callback"); 652 653 dc->dc_dev = dev; 654 dc->dc_func = func; 655 TAILQ_INSERT_TAIL(&interrupt_config_queue, dc, dc_queue); 656 config_pending_incr(); 657 } 658 659 /* 660 * Process a deferred configuration queue. 661 */ 662 static void 663 config_process_deferred(queue, parent) 664 struct deferred_config_head *queue; 665 struct device *parent; 666 { 667 struct deferred_config *dc, *ndc; 668 669 for (dc = TAILQ_FIRST(queue); dc != NULL; dc = ndc) { 670 ndc = TAILQ_NEXT(dc, dc_queue); 671 if (parent == NULL || dc->dc_dev->dv_parent == parent) { 672 TAILQ_REMOVE(queue, dc, dc_queue); 673 (*dc->dc_func)(dc->dc_dev); 674 free(dc, M_DEVBUF); 675 config_pending_decr(); 676 } 677 } 678 } 679 680 /* 681 * Manipulate the config_pending semaphore. 682 */ 683 void 684 config_pending_incr() 685 { 686 687 config_pending++; 688 } 689 690 void 691 config_pending_decr() 692 { 693 694 #ifdef DIAGNOSTIC 695 if (config_pending == 0) 696 panic("config_pending_decr: config_pending == 0"); 697 #endif 698 config_pending--; 699 if (config_pending == 0) 700 wakeup((void *)&config_pending); 701 } 702 703 /* 704 * Attach an event. These must come from initially-zero space (see 705 * commented-out assignments below), but that occurs naturally for 706 * device instance variables. 707 */ 708 void 709 evcnt_attach(dev, name, ev) 710 struct device *dev; 711 const char *name; 712 struct evcnt *ev; 713 { 714 715 #ifdef DIAGNOSTIC 716 if (strlen(name) >= sizeof(ev->ev_name)) 717 panic("evcnt_attach"); 718 #endif 719 /* ev->ev_next = NULL; */ 720 ev->ev_dev = dev; 721 /* ev->ev_count = 0; */ 722 strcpy(ev->ev_name, name); 723 TAILQ_INSERT_TAIL(&allevents, ev, ev_list); 724 } 725 726 /* 727 * Detach an event. 728 */ 729 void 730 evcnt_detach(ev) 731 struct evcnt *ev; 732 { 733 734 TAILQ_REMOVE(&allevents, ev, ev_list); 735 } 736