subr_autoconf.c revision 1.47 1 /* $NetBSD: subr_autoconf.c,v 1.47 2000/01/24 18:03:19 thorpej 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 #if defined(__alpha__) || defined(hp300) || defined(__i386__) || \
413 defined(__sparc__) || defined(__vax__) || defined(x68k)
414 device_register(dev, aux);
415 #endif
416 (*ca->ca_attach)(parent, dev, aux);
417 config_process_deferred(&deferred_config_queue, dev);
418 return (dev);
419 }
420
421 /*
422 * Detach a device. Optionally forced (e.g. because of hardware
423 * removal) and quiet. Returns zero if successful, non-zero
424 * (an error code) otherwise.
425 *
426 * Note that this code wants to be run from a process context, so
427 * that the detach can sleep to allow processes which have a device
428 * open to run and unwind their stacks.
429 */
430 int
431 config_detach(dev, flags)
432 struct device *dev;
433 int flags;
434 {
435 struct cfdata *cf;
436 struct cfattach *ca;
437 struct cfdriver *cd;
438 #ifdef DIAGNOSTIC
439 struct device *d;
440 #endif
441 int rv = 0, i;
442
443 cf = dev->dv_cfdata;
444 #ifdef DIAGNOSTIC
445 if (cf->cf_fstate != FSTATE_FOUND && cf->cf_fstate != FSTATE_STAR)
446 panic("config_detach: bad device fstate");
447 #endif
448 ca = cf->cf_attach;
449 cd = cf->cf_driver;
450
451 /*
452 * Ensure the device is deactivated. If the device doesn't
453 * have an activation entry point, we allow DVF_ACTIVE to
454 * remain set. Otherwise, if DVF_ACTIVE is still set, the
455 * device is busy, and the detach fails.
456 */
457 if (ca->ca_activate != NULL)
458 rv = config_deactivate(dev);
459
460 /*
461 * Try to detach the device. If that's not possible, then
462 * we either panic() (for the forced but failed case), or
463 * return an error.
464 */
465 if (rv == 0) {
466 if (ca->ca_detach != NULL)
467 rv = (*ca->ca_detach)(dev, flags);
468 else
469 rv = EOPNOTSUPP;
470 }
471 if (rv != 0) {
472 if ((flags & DETACH_FORCE) == 0)
473 return (rv);
474 else
475 panic("config_detach: forced detach of %s failed (%d)",
476 dev->dv_xname, rv);
477 }
478
479 /*
480 * The device has now been successfully detached.
481 */
482
483 #ifdef DIAGNOSTIC
484 /*
485 * Sanity: If you're successfully detached, you should have no
486 * children. (Note that because children must be attached
487 * after parents, we only need to search the latter part of
488 * the list.)
489 */
490 for (d = TAILQ_NEXT(dev, dv_list); d != NULL;
491 d = TAILQ_NEXT(d, dv_list)) {
492 if (d->dv_parent == dev)
493 panic("config_detach: detached device has children");
494 }
495 #endif
496
497 /*
498 * Mark cfdata to show that the unit can be reused, if possible.
499 */
500 #ifdef __BROKEN_CONFIG_UNIT_USAGE
501 /*
502 * Note that we can only re-use a starred unit number if the unit
503 * being detached had the last assigned unit number.
504 */
505 #endif /* __BROKEN_CONFIG_UNIT_USAGE */
506 for (cf = cfdata; cf->cf_driver; cf++) {
507 if (cf->cf_driver == cd) {
508 if (cf->cf_fstate == FSTATE_FOUND &&
509 cf->cf_unit == dev->dv_unit)
510 cf->cf_fstate = FSTATE_NOTFOUND;
511 #ifdef __BROKEN_CONFIG_UNIT_USAGE
512 if (cf->cf_fstate == FSTATE_STAR &&
513 cf->cf_unit == dev->dv_unit + 1)
514 cf->cf_unit--;
515 #endif /* __BROKEN_CONFIG_UNIT_USAGE */
516 }
517 }
518
519 /*
520 * Unlink from device list.
521 */
522 TAILQ_REMOVE(&alldevs, dev, dv_list);
523
524 /*
525 * Remove from cfdriver's array, tell the world, and free softc.
526 */
527 cd->cd_devs[dev->dv_unit] = NULL;
528 if ((flags & DETACH_QUIET) == 0)
529 printf("%s detached\n", dev->dv_xname);
530 free(dev, M_DEVBUF);
531
532 /*
533 * If the device now has no units in use, deallocate its softc array.
534 */
535 for (i = 0; i < cd->cd_ndevs; i++)
536 if (cd->cd_devs[i] != NULL)
537 break;
538 if (i == cd->cd_ndevs) { /* nothing found; deallocate */
539 free(cd->cd_devs, M_DEVBUF);
540 cd->cd_devs = NULL;
541 cd->cd_ndevs = 0;
542 }
543
544 /*
545 * Return success.
546 */
547 return (0);
548 }
549
550 int
551 config_activate(dev)
552 struct device *dev;
553 {
554 struct cfattach *ca = dev->dv_cfdata->cf_attach;
555 int rv = 0, oflags = dev->dv_flags;
556
557 if (ca->ca_activate == NULL)
558 return (EOPNOTSUPP);
559
560 if ((dev->dv_flags & DVF_ACTIVE) == 0) {
561 dev->dv_flags |= DVF_ACTIVE;
562 rv = (*ca->ca_activate)(dev, DVACT_ACTIVATE);
563 if (rv)
564 dev->dv_flags = oflags;
565 }
566 return (rv);
567 }
568
569 int
570 config_deactivate(dev)
571 struct device *dev;
572 {
573 struct cfattach *ca = dev->dv_cfdata->cf_attach;
574 int rv = 0, oflags = dev->dv_flags;
575
576 if (ca->ca_activate == NULL)
577 return (EOPNOTSUPP);
578
579 if (dev->dv_flags & DVF_ACTIVE) {
580 dev->dv_flags &= ~DVF_ACTIVE;
581 rv = (*ca->ca_activate)(dev, DVACT_DEACTIVATE);
582 if (rv)
583 dev->dv_flags = oflags;
584 }
585 return (rv);
586 }
587
588 /*
589 * Defer the configuration of the specified device until all
590 * of its parent's devices have been attached.
591 */
592 void
593 config_defer(dev, func)
594 struct device *dev;
595 void (*func) __P((struct device *));
596 {
597 struct deferred_config *dc;
598
599 if (dev->dv_parent == NULL)
600 panic("config_defer: can't defer config of a root device");
601
602 #ifdef DIAGNOSTIC
603 for (dc = TAILQ_FIRST(&deferred_config_queue); dc != NULL;
604 dc = TAILQ_NEXT(dc, dc_queue)) {
605 if (dc->dc_dev == dev)
606 panic("config_defer: deferred twice");
607 }
608 #endif
609
610 dc = malloc(sizeof(*dc), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
611 if (dc == NULL)
612 panic("config_defer: unable to allocate callback");
613
614 dc->dc_dev = dev;
615 dc->dc_func = func;
616 TAILQ_INSERT_TAIL(&deferred_config_queue, dc, dc_queue);
617 config_pending_incr();
618 }
619
620 /*
621 * Defer some autoconfiguration for a device until after interrupts
622 * are enabled.
623 */
624 void
625 config_interrupts(dev, func)
626 struct device *dev;
627 void (*func) __P((struct device *));
628 {
629 struct deferred_config *dc;
630
631 /*
632 * If interrupts are enabled, callback now.
633 */
634 if (cold == 0) {
635 (*func)(dev);
636 return;
637 }
638
639 #ifdef DIAGNOSTIC
640 for (dc = TAILQ_FIRST(&interrupt_config_queue); dc != NULL;
641 dc = TAILQ_NEXT(dc, dc_queue)) {
642 if (dc->dc_dev == dev)
643 panic("config_interrupts: deferred twice");
644 }
645 #endif
646
647 dc = malloc(sizeof(*dc), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
648 if (dc == NULL)
649 panic("config_interrupts: unable to allocate callback");
650
651 dc->dc_dev = dev;
652 dc->dc_func = func;
653 TAILQ_INSERT_TAIL(&interrupt_config_queue, dc, dc_queue);
654 config_pending_incr();
655 }
656
657 /*
658 * Process a deferred configuration queue.
659 */
660 static void
661 config_process_deferred(queue, parent)
662 struct deferred_config_head *queue;
663 struct device *parent;
664 {
665 struct deferred_config *dc, *ndc;
666
667 for (dc = TAILQ_FIRST(queue); dc != NULL; dc = ndc) {
668 ndc = TAILQ_NEXT(dc, dc_queue);
669 if (parent == NULL || dc->dc_dev->dv_parent == parent) {
670 TAILQ_REMOVE(queue, dc, dc_queue);
671 (*dc->dc_func)(dc->dc_dev);
672 free(dc, M_DEVBUF);
673 config_pending_decr();
674 }
675 }
676 }
677
678 /*
679 * Manipulate the config_pending semaphore.
680 */
681 void
682 config_pending_incr()
683 {
684
685 config_pending++;
686 }
687
688 void
689 config_pending_decr()
690 {
691
692 #ifdef DIAGNOSTIC
693 if (config_pending == 0)
694 panic("config_pending_decr: config_pending == 0");
695 #endif
696 config_pending--;
697 if (config_pending == 0)
698 wakeup((void *)&config_pending);
699 }
700
701 /*
702 * Attach an event. These must come from initially-zero space (see
703 * commented-out assignments below), but that occurs naturally for
704 * device instance variables.
705 */
706 void
707 evcnt_attach(dev, name, ev)
708 struct device *dev;
709 const char *name;
710 struct evcnt *ev;
711 {
712
713 #ifdef DIAGNOSTIC
714 if (strlen(name) >= sizeof(ev->ev_name))
715 panic("evcnt_attach");
716 #endif
717 /* ev->ev_next = NULL; */
718 ev->ev_dev = dev;
719 /* ev->ev_count = 0; */
720 strcpy(ev->ev_name, name);
721 TAILQ_INSERT_TAIL(&allevents, ev, ev_list);
722 }
723
724 /*
725 * Detach an event.
726 */
727 void
728 evcnt_detach(ev)
729 struct evcnt *ev;
730 {
731
732 TAILQ_REMOVE(&allevents, ev, ev_list);
733 }
734