2002-09-24 03:16:06 +04:00
|
|
|
/* $NetBSD: subr_autoconf.c,v 1.65 2002/09/23 23:16:06 thorpej Exp $ */
|
2000-06-04 23:14:14 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 1996, 2000 Christopher G. Demetriou
|
|
|
|
* All rights reserved.
|
2000-06-14 02:36:16 +04:00
|
|
|
*
|
2000-06-04 23:14:14 +04:00
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
2000-06-14 02:36:16 +04:00
|
|
|
* This product includes software developed for the
|
|
|
|
* NetBSD Project. See http://www.netbsd.org/ for
|
|
|
|
* information about NetBSD.
|
2000-06-04 23:14:14 +04:00
|
|
|
* 4. The name of the author may not be used to endorse or promote products
|
2000-06-14 02:36:16 +04:00
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
*
|
2000-06-04 23:14:14 +04:00
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2000-06-14 02:36:16 +04:00
|
|
|
*
|
|
|
|
* --(license Id: LICENSE.proto,v 1.1 2000/06/13 21:40:26 cgd Exp )--
|
2000-06-04 23:14:14 +04:00
|
|
|
*/
|
1994-06-29 10:29:24 +04:00
|
|
|
|
1993-08-13 17:19:41 +04:00
|
|
|
/*
|
1994-05-20 08:15:04 +04:00
|
|
|
* Copyright (c) 1992, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
1993-08-13 17:19:41 +04:00
|
|
|
*
|
|
|
|
* This software was developed by the Computer Systems Engineering group
|
|
|
|
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
|
|
|
|
* contributed to Berkeley.
|
|
|
|
*
|
|
|
|
* All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
|
|
|
* This product includes software developed by the University of
|
|
|
|
* California, Lawrence Berkeley Laboratories.
|
|
|
|
*
|
1994-05-20 08:15:04 +04:00
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
|
|
|
* This product includes software developed by the University of
|
|
|
|
* California, Berkeley and its contributors.
|
|
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
1993-08-13 17:19:41 +04:00
|
|
|
*
|
1994-05-20 08:15:04 +04:00
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
1993-08-13 17:19:41 +04:00
|
|
|
*
|
1994-05-20 08:15:04 +04:00
|
|
|
* from: Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp (LBL)
|
1994-06-29 10:29:24 +04:00
|
|
|
*
|
1998-03-01 05:20:01 +03:00
|
|
|
* @(#)subr_autoconf.c 8.3 (Berkeley) 5/17/94
|
1993-08-13 17:19:41 +04:00
|
|
|
*/
|
|
|
|
|
2000-06-02 05:31:52 +04:00
|
|
|
#include <sys/cdefs.h>
|
2002-09-24 03:16:06 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: subr_autoconf.c,v 1.65 2002/09/23 23:16:06 thorpej Exp $");
|
2002-02-15 14:18:26 +03:00
|
|
|
|
|
|
|
#include "opt_ddb.h"
|
2000-06-02 05:31:52 +04:00
|
|
|
|
1993-12-18 06:59:02 +03:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/device.h>
|
|
|
|
#include <sys/malloc.h>
|
1996-02-04 05:15:01 +03:00
|
|
|
#include <sys/systm.h>
|
1999-09-18 00:11:56 +04:00
|
|
|
#include <sys/kernel.h>
|
1998-11-17 11:38:07 +03:00
|
|
|
#include <sys/errno.h>
|
2000-01-24 21:03:19 +03:00
|
|
|
#include <sys/proc.h>
|
1994-11-04 09:40:11 +03:00
|
|
|
#include <machine/limits.h>
|
1993-08-13 17:19:41 +04:00
|
|
|
|
2001-07-01 06:56:20 +04:00
|
|
|
#include "opt_userconf.h"
|
|
|
|
#ifdef USERCONF
|
|
|
|
#include <sys/userconf.h>
|
|
|
|
#include <sys/reboot.h>
|
|
|
|
#endif
|
|
|
|
|
1993-08-13 17:19:41 +04:00
|
|
|
/*
|
|
|
|
* Autoconfiguration subroutines.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ioconf.c exports exactly two names: cfdata and cfroots. All system
|
|
|
|
* devices and drivers are found via these tables.
|
|
|
|
*/
|
|
|
|
extern struct cfdata cfdata[];
|
|
|
|
extern short cfroots[];
|
|
|
|
|
2002-09-24 03:16:06 +04:00
|
|
|
/*
|
|
|
|
* List of cfdata tables. We always have one such list -- the one
|
|
|
|
* built statically when the kernel was configured.
|
|
|
|
*/
|
|
|
|
struct cftablelist allcftables;
|
|
|
|
static struct cftable initcftable;
|
|
|
|
|
1993-08-13 17:19:41 +04:00
|
|
|
#define ROOT ((struct device *)NULL)
|
|
|
|
|
1994-11-04 09:40:11 +03:00
|
|
|
struct matchinfo {
|
|
|
|
cfmatch_t fn;
|
|
|
|
struct device *parent;
|
1996-12-05 03:09:10 +03:00
|
|
|
void *aux;
|
|
|
|
struct cfdata *match;
|
|
|
|
int pri;
|
1994-11-04 09:40:11 +03:00
|
|
|
};
|
|
|
|
|
2000-06-02 05:31:52 +04:00
|
|
|
static char *number(char *, int);
|
|
|
|
static void mapply(struct matchinfo *, struct cfdata *);
|
1996-02-04 05:15:01 +03:00
|
|
|
|
1998-06-09 22:46:12 +04:00
|
|
|
struct deferred_config {
|
|
|
|
TAILQ_ENTRY(deferred_config) dc_queue;
|
|
|
|
struct device *dc_dev;
|
2000-06-02 05:31:52 +04:00
|
|
|
void (*dc_func)(struct device *);
|
1998-06-09 22:46:12 +04:00
|
|
|
};
|
|
|
|
|
1999-09-15 23:37:08 +04:00
|
|
|
TAILQ_HEAD(deferred_config_head, deferred_config);
|
1998-06-09 22:46:12 +04:00
|
|
|
|
1999-09-15 23:37:08 +04:00
|
|
|
struct deferred_config_head deferred_config_queue;
|
|
|
|
struct deferred_config_head interrupt_config_queue;
|
|
|
|
|
2000-06-02 05:31:52 +04:00
|
|
|
static void config_process_deferred(struct deferred_config_head *,
|
|
|
|
struct device *);
|
1998-06-09 22:46:12 +04:00
|
|
|
|
2001-05-28 20:40:31 +04:00
|
|
|
/* list of all devices */
|
|
|
|
struct devicelist alldevs;
|
|
|
|
|
|
|
|
/* list of all events */
|
|
|
|
struct evcntlist allevents = TAILQ_HEAD_INITIALIZER(allevents);
|
1996-04-04 04:25:44 +04:00
|
|
|
|
2000-01-24 21:03:19 +03:00
|
|
|
__volatile int config_pending; /* semaphore for mountroot */
|
|
|
|
|
1996-04-04 04:25:44 +04:00
|
|
|
/*
|
1999-09-15 22:10:33 +04:00
|
|
|
* Configure the system's hardware.
|
1996-04-04 04:25:44 +04:00
|
|
|
*/
|
|
|
|
void
|
2000-06-02 05:31:52 +04:00
|
|
|
configure(void)
|
1996-04-04 04:25:44 +04:00
|
|
|
{
|
|
|
|
|
2002-09-24 03:16:06 +04:00
|
|
|
TAILQ_INIT(&allcftables);
|
|
|
|
|
|
|
|
initcftable.ct_cfdata = cfdata;
|
|
|
|
TAILQ_INSERT_TAIL(&allcftables, &initcftable, ct_list);
|
|
|
|
|
1998-06-09 22:46:12 +04:00
|
|
|
TAILQ_INIT(&deferred_config_queue);
|
1999-09-15 23:37:08 +04:00
|
|
|
TAILQ_INIT(&interrupt_config_queue);
|
1999-09-15 22:10:33 +04:00
|
|
|
TAILQ_INIT(&alldevs);
|
|
|
|
|
2001-07-01 06:56:20 +04:00
|
|
|
#ifdef USERCONF
|
|
|
|
if (boothowto & RB_USERCONF)
|
|
|
|
user_config();
|
|
|
|
#endif
|
|
|
|
|
1999-09-15 22:10:33 +04:00
|
|
|
/*
|
|
|
|
* Do the machine-dependent portion of autoconfiguration. This
|
|
|
|
* sets the configuration machinery here in motion by "finding"
|
|
|
|
* the root bus. When this function returns, we expect interrupts
|
|
|
|
* to be enabled.
|
|
|
|
*/
|
|
|
|
cpu_configure();
|
1999-09-18 00:11:56 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now that we've found all the hardware, start the real time
|
|
|
|
* and statistics clocks.
|
|
|
|
*/
|
|
|
|
initclocks();
|
|
|
|
|
|
|
|
cold = 0; /* clocks are running, we're warm now! */
|
1999-09-15 23:37:08 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now callback to finish configuration for devices which want
|
|
|
|
* to do this once interrupts are enabled.
|
|
|
|
*/
|
|
|
|
config_process_deferred(&interrupt_config_queue, NULL);
|
1996-04-04 04:25:44 +04:00
|
|
|
}
|
|
|
|
|
1993-08-13 17:19:41 +04:00
|
|
|
/*
|
|
|
|
* Apply the matching function and choose the best. This is used
|
|
|
|
* a few times and we want to keep the code small.
|
|
|
|
*/
|
1994-11-04 09:40:11 +03:00
|
|
|
static void
|
2000-06-02 05:31:52 +04:00
|
|
|
mapply(struct matchinfo *m, struct cfdata *cf)
|
1993-08-13 17:19:41 +04:00
|
|
|
{
|
2000-03-28 21:30:10 +04:00
|
|
|
int pri;
|
1993-08-13 17:19:41 +04:00
|
|
|
|
|
|
|
if (m->fn != NULL)
|
1996-12-05 03:09:10 +03:00
|
|
|
pri = (*m->fn)(m->parent, cf, m->aux);
|
1993-08-16 03:01:58 +04:00
|
|
|
else {
|
1996-03-17 03:57:14 +03:00
|
|
|
if (cf->cf_attach->ca_match == NULL) {
|
1994-11-04 09:40:11 +03:00
|
|
|
panic("mapply: no match function for '%s' device\n",
|
1994-11-04 01:01:09 +03:00
|
|
|
cf->cf_driver->cd_name);
|
1993-08-16 03:01:58 +04:00
|
|
|
}
|
1996-12-05 03:09:10 +03:00
|
|
|
pri = (*cf->cf_attach->ca_match)(m->parent, cf, m->aux);
|
1993-08-16 03:01:58 +04:00
|
|
|
}
|
1993-08-13 17:19:41 +04:00
|
|
|
if (pri > m->pri) {
|
1996-12-05 03:09:10 +03:00
|
|
|
m->match = cf;
|
1993-08-13 17:19:41 +04:00
|
|
|
m->pri = pri;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Iterate over all potential children of some device, calling the given
|
|
|
|
* function (default being the child's match function) for each one.
|
|
|
|
* Nonzero returns are matches; the highest value returned is considered
|
|
|
|
* the best match. Return the `found child' if we got a match, or NULL
|
|
|
|
* otherwise. The `aux' pointer is simply passed on through.
|
|
|
|
*
|
|
|
|
* Note that this function is designed so that it can be used to apply
|
|
|
|
* an arbitrary function to all potential children (its return value
|
|
|
|
* can be ignored).
|
|
|
|
*/
|
1996-12-05 03:09:10 +03:00
|
|
|
struct cfdata *
|
2000-06-02 05:31:52 +04:00
|
|
|
config_search(cfmatch_t fn, struct device *parent, void *aux)
|
1993-08-13 17:19:41 +04:00
|
|
|
{
|
2002-09-24 03:16:06 +04:00
|
|
|
struct cftable *ct;
|
2000-03-28 21:30:10 +04:00
|
|
|
struct cfdata *cf;
|
|
|
|
short *p;
|
1993-08-13 17:19:41 +04:00
|
|
|
struct matchinfo m;
|
|
|
|
|
|
|
|
m.fn = fn;
|
|
|
|
m.parent = parent;
|
1994-11-04 06:07:17 +03:00
|
|
|
m.aux = aux;
|
1996-12-05 03:09:10 +03:00
|
|
|
m.match = NULL;
|
1993-08-13 17:19:41 +04:00
|
|
|
m.pri = 0;
|
2002-09-24 03:16:06 +04:00
|
|
|
|
|
|
|
TAILQ_FOREACH(ct, &allcftables, ct_list) {
|
|
|
|
for (cf = ct->ct_cfdata; cf->cf_driver; cf++) {
|
|
|
|
/*
|
|
|
|
* Skip cf if no longer eligible, otherwise scan
|
|
|
|
* through parents for one matching `parent', and
|
|
|
|
* try match function.
|
|
|
|
*/
|
|
|
|
if (cf->cf_fstate == FSTATE_FOUND)
|
|
|
|
continue;
|
|
|
|
if (cf->cf_fstate == FSTATE_DNOTFOUND ||
|
|
|
|
cf->cf_fstate == FSTATE_DSTAR)
|
|
|
|
continue;
|
|
|
|
for (p = cf->cf_parents; *p >= 0; p++)
|
|
|
|
if (parent->dv_cfdata == &(ct->ct_cfdata)[*p])
|
|
|
|
mapply(&m, cf);
|
|
|
|
}
|
1993-08-13 17:19:41 +04:00
|
|
|
}
|
|
|
|
return (m.match);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the given root device.
|
|
|
|
* This is much like config_search, but there is no parent.
|
2002-09-24 03:16:06 +04:00
|
|
|
* Don't bother with multiple cfdata tables; the root node
|
|
|
|
* must always be in the initial table.
|
1993-08-13 17:19:41 +04:00
|
|
|
*/
|
1996-12-05 03:09:10 +03:00
|
|
|
struct cfdata *
|
2000-06-02 05:48:50 +04:00
|
|
|
config_rootsearch(cfmatch_t fn, const char *rootname, void *aux)
|
1993-08-13 17:19:41 +04:00
|
|
|
{
|
2000-03-28 21:30:10 +04:00
|
|
|
struct cfdata *cf;
|
|
|
|
short *p;
|
1993-08-13 17:19:41 +04:00
|
|
|
struct matchinfo m;
|
|
|
|
|
|
|
|
m.fn = fn;
|
|
|
|
m.parent = ROOT;
|
1994-11-04 06:07:17 +03:00
|
|
|
m.aux = aux;
|
1996-12-05 03:09:10 +03:00
|
|
|
m.match = NULL;
|
1993-08-13 17:19:41 +04:00
|
|
|
m.pri = 0;
|
|
|
|
/*
|
|
|
|
* Look at root entries for matching name. We do not bother
|
|
|
|
* with found-state here since only one root should ever be
|
|
|
|
* searched (and it must be done first).
|
|
|
|
*/
|
|
|
|
for (p = cfroots; *p >= 0; p++) {
|
|
|
|
cf = &cfdata[*p];
|
|
|
|
if (strcmp(cf->cf_driver->cd_name, rootname) == 0)
|
1994-11-04 09:40:11 +03:00
|
|
|
mapply(&m, cf);
|
1993-08-13 17:19:41 +04:00
|
|
|
}
|
|
|
|
return (m.match);
|
|
|
|
}
|
|
|
|
|
2000-06-02 05:48:50 +04:00
|
|
|
static const char *msgs[3] = { "", " not configured\n", " unsupported\n" };
|
1993-08-13 17:19:41 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The given `aux' argument describes a device that has been found
|
|
|
|
* on the given parent, but not necessarily configured. Locate the
|
1996-02-28 00:45:46 +03:00
|
|
|
* configuration data for that device (using the submatch function
|
|
|
|
* provided, or using candidates' cd_match configuration driver
|
|
|
|
* functions) and attach it, and return true. If the device was
|
1993-08-13 17:19:41 +04:00
|
|
|
* not configured, call the given `print' function and return 0.
|
|
|
|
*/
|
1996-04-04 10:06:18 +04:00
|
|
|
struct device *
|
2000-06-02 05:31:52 +04:00
|
|
|
config_found_sm(struct device *parent, void *aux, cfprint_t print,
|
|
|
|
cfmatch_t submatch)
|
1993-08-13 17:19:41 +04:00
|
|
|
{
|
1996-12-05 03:09:10 +03:00
|
|
|
struct cfdata *cf;
|
|
|
|
|
|
|
|
if ((cf = config_search(submatch, parent, aux)) != NULL)
|
|
|
|
return (config_attach(parent, cf, aux, print));
|
1993-08-16 03:01:58 +04:00
|
|
|
if (print)
|
2000-07-08 22:11:02 +04:00
|
|
|
printf("%s", msgs[(*print)(aux, parent->dv_xname)]);
|
1996-04-04 10:06:18 +04:00
|
|
|
return (NULL);
|
1993-08-13 17:19:41 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* As above, but for root devices.
|
|
|
|
*/
|
1996-04-04 10:06:18 +04:00
|
|
|
struct device *
|
2000-06-02 05:48:50 +04:00
|
|
|
config_rootfound(const char *rootname, void *aux)
|
1993-08-13 17:19:41 +04:00
|
|
|
{
|
1996-12-05 03:09:10 +03:00
|
|
|
struct cfdata *cf;
|
|
|
|
|
|
|
|
if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL)
|
|
|
|
return (config_attach(ROOT, cf, aux, (cfprint_t)NULL));
|
1996-10-13 06:32:29 +04:00
|
|
|
printf("root device %s not configured\n", rootname);
|
1996-04-04 10:06:18 +04:00
|
|
|
return (NULL);
|
1993-08-13 17:19:41 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* just like sprintf(buf, "%d") except that it works from the end */
|
|
|
|
static char *
|
2000-06-02 05:31:52 +04:00
|
|
|
number(char *ep, int n)
|
1993-08-13 17:19:41 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
*--ep = 0;
|
|
|
|
while (n >= 10) {
|
|
|
|
*--ep = (n % 10) + '0';
|
|
|
|
n /= 10;
|
|
|
|
}
|
|
|
|
*--ep = n + '0';
|
|
|
|
return (ep);
|
|
|
|
}
|
|
|
|
|
2001-12-02 05:40:57 +03:00
|
|
|
/*
|
|
|
|
* Expand the size of the cd_devs array if necessary.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
config_makeroom(int n, struct cfdriver *cd)
|
|
|
|
{
|
|
|
|
int old, new;
|
|
|
|
void **nsp;
|
|
|
|
|
|
|
|
if (n < cd->cd_ndevs)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Need to expand the array.
|
|
|
|
*/
|
|
|
|
old = cd->cd_ndevs;
|
2001-12-10 03:33:04 +03:00
|
|
|
if (old == 0)
|
2001-12-02 05:40:57 +03:00
|
|
|
new = MINALLOCSIZE / sizeof(void *);
|
|
|
|
else
|
|
|
|
new = old * 2;
|
|
|
|
while (new <= n)
|
|
|
|
new *= 2;
|
|
|
|
cd->cd_ndevs = new;
|
|
|
|
nsp = malloc(new * sizeof(void *), M_DEVBUF,
|
|
|
|
cold ? M_NOWAIT : M_WAITOK);
|
2001-12-05 19:12:42 +03:00
|
|
|
if (nsp == NULL)
|
2001-12-02 05:40:57 +03:00
|
|
|
panic("config_attach: %sing dev array",
|
|
|
|
old != 0 ? "expand" : "creat");
|
|
|
|
memset(nsp + old, 0, (new - old) * sizeof(void *));
|
2001-12-10 03:33:04 +03:00
|
|
|
if (old != 0) {
|
2001-12-02 05:40:57 +03:00
|
|
|
memcpy(nsp, cd->cd_devs, old * sizeof(void *));
|
|
|
|
free(cd->cd_devs, M_DEVBUF);
|
|
|
|
}
|
|
|
|
cd->cd_devs = nsp;
|
|
|
|
}
|
|
|
|
|
1993-08-13 17:19:41 +04:00
|
|
|
/*
|
|
|
|
* Attach a found device. Allocates memory for device variables.
|
|
|
|
*/
|
1996-12-05 03:09:10 +03:00
|
|
|
struct device *
|
2000-06-02 05:31:52 +04:00
|
|
|
config_attach(struct device *parent, struct cfdata *cf, void *aux,
|
|
|
|
cfprint_t print)
|
1996-12-05 03:09:10 +03:00
|
|
|
{
|
2000-03-28 21:30:10 +04:00
|
|
|
struct device *dev;
|
2002-09-24 03:16:06 +04:00
|
|
|
struct cftable *ct;
|
2000-03-28 21:30:10 +04:00
|
|
|
struct cfdriver *cd;
|
|
|
|
struct cfattach *ca;
|
|
|
|
size_t lname, lunit;
|
2000-06-02 05:48:50 +04:00
|
|
|
const char *xunit;
|
1996-12-05 03:09:10 +03:00
|
|
|
int myunit;
|
|
|
|
char num[10];
|
|
|
|
|
|
|
|
cd = cf->cf_driver;
|
|
|
|
ca = cf->cf_attach;
|
|
|
|
if (ca->ca_devsize < sizeof(struct device))
|
|
|
|
panic("config_attach");
|
2000-01-18 10:45:04 +03:00
|
|
|
#ifndef __BROKEN_CONFIG_UNIT_USAGE
|
avoid 'marching unit numbers' for cloning devices. (e.g., previously,
if you com* at pcmcia?, and com3 and com4 as pcmcia cards, and removed
and reinserted the card that was com3, it would become com5. if you then
removed and reinserted com4, it would become com6. etc.) Now, instead
of incrementing FSTATE_STAR configuration entries for a driver when
a cloning instance is attached, leave it alone, and scan the device softc
array (starting at the first cloning unit number) for units which are
available for use. This wastes a tiny bit of time (can require a linear
scan of the softc table for the device), but device attachment should be
relatively infrequent and the number of units of each type of device
is never particularly large anyway.
1999-12-30 04:03:43 +03:00
|
|
|
if (cf->cf_fstate == FSTATE_STAR) {
|
|
|
|
for (myunit = cf->cf_unit; myunit < cd->cd_ndevs; myunit++)
|
|
|
|
if (cd->cd_devs[myunit] == NULL)
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
* myunit is now the unit of the first NULL device pointer,
|
|
|
|
* or max(cd->cd_ndevs,cf->cf_unit).
|
|
|
|
*/
|
|
|
|
} else {
|
|
|
|
myunit = cf->cf_unit;
|
2000-01-18 10:45:04 +03:00
|
|
|
#else /* __BROKEN_CONFIG_UNIT_USAGE */
|
|
|
|
myunit = cf->cf_unit;
|
|
|
|
if (cf->cf_fstate == FSTATE_STAR)
|
|
|
|
cf->cf_unit++;
|
|
|
|
else {
|
|
|
|
#endif /* __BROKEN_CONFIG_UNIT_USAGE */
|
1996-12-05 03:09:10 +03:00
|
|
|
KASSERT(cf->cf_fstate == FSTATE_NOTFOUND);
|
|
|
|
cf->cf_fstate = FSTATE_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* compute length of name and decimal expansion of unit number */
|
|
|
|
lname = strlen(cd->cd_name);
|
1998-08-01 02:50:48 +04:00
|
|
|
xunit = number(&num[sizeof(num)], myunit);
|
|
|
|
lunit = &num[sizeof(num)] - xunit;
|
2002-07-10 23:04:09 +04:00
|
|
|
if (lname + lunit > sizeof(dev->dv_xname))
|
1996-12-05 03:09:10 +03:00
|
|
|
panic("config_attach: device name too long");
|
|
|
|
|
|
|
|
/* get memory for all device vars */
|
1999-09-18 00:11:56 +04:00
|
|
|
dev = (struct device *)malloc(ca->ca_devsize, M_DEVBUF,
|
|
|
|
cold ? M_NOWAIT : M_WAITOK);
|
1996-12-05 03:09:10 +03:00
|
|
|
if (!dev)
|
|
|
|
panic("config_attach: memory allocation for device softc failed");
|
Abolition of bcopy, ovbcopy, bcmp, and bzero, phase one.
bcopy(x, y, z) -> memcpy(y, x, z)
ovbcopy(x, y, z) -> memmove(y, x, z)
bcmp(x, y, z) -> memcmp(x, y, z)
bzero(x, y) -> memset(x, 0, y)
1998-08-04 08:03:10 +04:00
|
|
|
memset(dev, 0, ca->ca_devsize);
|
1996-12-05 03:09:10 +03:00
|
|
|
TAILQ_INSERT_TAIL(&alldevs, dev, dv_list); /* link up */
|
|
|
|
dev->dv_class = cd->cd_class;
|
|
|
|
dev->dv_cfdata = cf;
|
|
|
|
dev->dv_unit = myunit;
|
Abolition of bcopy, ovbcopy, bcmp, and bzero, phase one.
bcopy(x, y, z) -> memcpy(y, x, z)
ovbcopy(x, y, z) -> memmove(y, x, z)
bcmp(x, y, z) -> memcmp(x, y, z)
bzero(x, y) -> memset(x, 0, y)
1998-08-04 08:03:10 +04:00
|
|
|
memcpy(dev->dv_xname, cd->cd_name, lname);
|
|
|
|
memcpy(dev->dv_xname + lname, xunit, lunit);
|
1996-12-05 03:09:10 +03:00
|
|
|
dev->dv_parent = parent;
|
1998-11-17 11:38:07 +03:00
|
|
|
dev->dv_flags = DVF_ACTIVE; /* always initially active */
|
1998-06-09 22:46:12 +04:00
|
|
|
|
1996-12-05 03:09:10 +03:00
|
|
|
if (parent == ROOT)
|
|
|
|
printf("%s (root)", dev->dv_xname);
|
|
|
|
else {
|
|
|
|
printf("%s at %s", dev->dv_xname, parent->dv_xname);
|
|
|
|
if (print)
|
2000-06-02 05:48:50 +04:00
|
|
|
(void) (*print)(aux, NULL);
|
1996-12-05 03:09:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* put this device in the devices array */
|
2001-12-02 05:40:57 +03:00
|
|
|
config_makeroom(dev->dv_unit, cd);
|
1996-12-05 03:09:10 +03:00
|
|
|
if (cd->cd_devs[dev->dv_unit])
|
|
|
|
panic("config_attach: duplicate %s", dev->dv_xname);
|
|
|
|
cd->cd_devs[dev->dv_unit] = dev;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Before attaching, clobber any unfound devices that are
|
avoid 'marching unit numbers' for cloning devices. (e.g., previously,
if you com* at pcmcia?, and com3 and com4 as pcmcia cards, and removed
and reinserted the card that was com3, it would become com5. if you then
removed and reinserted com4, it would become com6. etc.) Now, instead
of incrementing FSTATE_STAR configuration entries for a driver when
a cloning instance is attached, leave it alone, and scan the device softc
array (starting at the first cloning unit number) for units which are
available for use. This wastes a tiny bit of time (can require a linear
scan of the softc table for the device), but device attachment should be
relatively infrequent and the number of units of each type of device
is never particularly large anyway.
1999-12-30 04:03:43 +03:00
|
|
|
* otherwise identical.
|
1996-12-05 03:09:10 +03:00
|
|
|
*/
|
2000-01-18 10:45:04 +03:00
|
|
|
#ifdef __BROKEN_CONFIG_UNIT_USAGE
|
|
|
|
/* bump the unit number on all starred cfdata for this device. */
|
|
|
|
#endif /* __BROKEN_CONFIG_UNIT_USAGE */
|
2002-09-24 03:16:06 +04:00
|
|
|
TAILQ_FOREACH(ct, &allcftables, ct_list) {
|
|
|
|
for (cf = ct->ct_cfdata; cf->cf_driver; cf++) {
|
|
|
|
if (cf->cf_driver == cd &&
|
|
|
|
cf->cf_unit == dev->dv_unit) {
|
|
|
|
if (cf->cf_fstate == FSTATE_NOTFOUND)
|
|
|
|
cf->cf_fstate = FSTATE_FOUND;
|
2000-01-18 10:45:04 +03:00
|
|
|
#ifdef __BROKEN_CONFIG_UNIT_USAGE
|
2002-09-24 03:16:06 +04:00
|
|
|
if (cf->cf_fstate == FSTATE_STAR)
|
|
|
|
cf->cf_unit++;
|
2000-01-18 10:45:04 +03:00
|
|
|
#endif /* __BROKEN_CONFIG_UNIT_USAGE */
|
2002-09-24 03:16:06 +04:00
|
|
|
}
|
1996-12-05 03:09:10 +03:00
|
|
|
}
|
2002-09-24 03:16:06 +04:00
|
|
|
}
|
2000-02-01 07:01:19 +03:00
|
|
|
#ifdef __HAVE_DEVICE_REGISTER
|
1996-12-05 03:09:10 +03:00
|
|
|
device_register(dev, aux);
|
|
|
|
#endif
|
|
|
|
(*ca->ca_attach)(parent, dev, aux);
|
1999-09-15 23:37:08 +04:00
|
|
|
config_process_deferred(&deferred_config_queue, dev);
|
1996-12-05 03:09:10 +03:00
|
|
|
return (dev);
|
|
|
|
}
|
1993-08-13 17:19:41 +04:00
|
|
|
|
1998-11-17 11:38:07 +03:00
|
|
|
/*
|
|
|
|
* Detach a device. Optionally forced (e.g. because of hardware
|
|
|
|
* removal) and quiet. Returns zero if successful, non-zero
|
|
|
|
* (an error code) otherwise.
|
|
|
|
*
|
|
|
|
* Note that this code wants to be run from a process context, so
|
|
|
|
* that the detach can sleep to allow processes which have a device
|
|
|
|
* open to run and unwind their stacks.
|
|
|
|
*/
|
|
|
|
int
|
2000-06-02 05:31:52 +04:00
|
|
|
config_detach(struct device *dev, int flags)
|
1998-11-17 11:38:07 +03:00
|
|
|
{
|
2002-09-24 03:16:06 +04:00
|
|
|
struct cftable *ct;
|
1998-11-17 11:38:07 +03:00
|
|
|
struct cfdata *cf;
|
|
|
|
struct cfattach *ca;
|
|
|
|
struct cfdriver *cd;
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
struct device *d;
|
|
|
|
#endif
|
1998-11-18 21:40:54 +03:00
|
|
|
int rv = 0, i;
|
1998-11-17 11:38:07 +03:00
|
|
|
|
|
|
|
cf = dev->dv_cfdata;
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if (cf->cf_fstate != FSTATE_FOUND && cf->cf_fstate != FSTATE_STAR)
|
|
|
|
panic("config_detach: bad device fstate");
|
|
|
|
#endif
|
|
|
|
ca = cf->cf_attach;
|
|
|
|
cd = cf->cf_driver;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ensure the device is deactivated. If the device doesn't
|
|
|
|
* have an activation entry point, we allow DVF_ACTIVE to
|
|
|
|
* remain set. Otherwise, if DVF_ACTIVE is still set, the
|
|
|
|
* device is busy, and the detach fails.
|
|
|
|
*/
|
1998-11-18 21:40:08 +03:00
|
|
|
if (ca->ca_activate != NULL)
|
|
|
|
rv = config_deactivate(dev);
|
1998-11-17 11:38:07 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to detach the device. If that's not possible, then
|
|
|
|
* we either panic() (for the forced but failed case), or
|
|
|
|
* return an error.
|
|
|
|
*/
|
|
|
|
if (rv == 0) {
|
|
|
|
if (ca->ca_detach != NULL)
|
|
|
|
rv = (*ca->ca_detach)(dev, flags);
|
|
|
|
else
|
|
|
|
rv = EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
if (rv != 0) {
|
|
|
|
if ((flags & DETACH_FORCE) == 0)
|
|
|
|
return (rv);
|
|
|
|
else
|
|
|
|
panic("config_detach: forced detach of %s failed (%d)",
|
|
|
|
dev->dv_xname, rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The device has now been successfully detached.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
/*
|
|
|
|
* Sanity: If you're successfully detached, you should have no
|
|
|
|
* children. (Note that because children must be attached
|
|
|
|
* after parents, we only need to search the latter part of
|
|
|
|
* the list.)
|
|
|
|
*/
|
|
|
|
for (d = TAILQ_NEXT(dev, dv_list); d != NULL;
|
2000-01-25 16:23:26 +03:00
|
|
|
d = TAILQ_NEXT(d, dv_list)) {
|
|
|
|
if (d->dv_parent == dev) {
|
|
|
|
printf("config_detach: detached device %s"
|
|
|
|
" has children %s\n", dev->dv_xname, d->dv_xname);
|
|
|
|
panic("config_detach");
|
|
|
|
}
|
1998-11-17 11:38:07 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark cfdata to show that the unit can be reused, if possible.
|
|
|
|
*/
|
2000-01-18 10:45:04 +03:00
|
|
|
#ifdef __BROKEN_CONFIG_UNIT_USAGE
|
|
|
|
/*
|
|
|
|
* Note that we can only re-use a starred unit number if the unit
|
|
|
|
* being detached had the last assigned unit number.
|
|
|
|
*/
|
|
|
|
#endif /* __BROKEN_CONFIG_UNIT_USAGE */
|
2002-09-24 03:16:06 +04:00
|
|
|
TAILQ_FOREACH(ct, &allcftables, ct_list) {
|
|
|
|
for (cf = ct->ct_cfdata; cf->cf_driver; cf++) {
|
|
|
|
if (cf->cf_driver == cd) {
|
|
|
|
if (cf->cf_fstate == FSTATE_FOUND &&
|
|
|
|
cf->cf_unit == dev->dv_unit)
|
|
|
|
cf->cf_fstate = FSTATE_NOTFOUND;
|
2000-01-18 10:45:04 +03:00
|
|
|
#ifdef __BROKEN_CONFIG_UNIT_USAGE
|
2002-09-24 03:16:06 +04:00
|
|
|
if (cf->cf_fstate == FSTATE_STAR &&
|
|
|
|
cf->cf_unit == dev->dv_unit + 1)
|
|
|
|
cf->cf_unit--;
|
2000-01-18 10:45:04 +03:00
|
|
|
#endif /* __BROKEN_CONFIG_UNIT_USAGE */
|
2002-09-24 03:16:06 +04:00
|
|
|
}
|
1998-11-17 11:38:07 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unlink from device list.
|
|
|
|
*/
|
|
|
|
TAILQ_REMOVE(&alldevs, dev, dv_list);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove from cfdriver's array, tell the world, and free softc.
|
|
|
|
*/
|
|
|
|
cd->cd_devs[dev->dv_unit] = NULL;
|
|
|
|
if ((flags & DETACH_QUIET) == 0)
|
|
|
|
printf("%s detached\n", dev->dv_xname);
|
|
|
|
free(dev, M_DEVBUF);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the device now has no units in use, deallocate its softc array.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < cd->cd_ndevs; i++)
|
|
|
|
if (cd->cd_devs[i] != NULL)
|
|
|
|
break;
|
|
|
|
if (i == cd->cd_ndevs) { /* nothing found; deallocate */
|
|
|
|
free(cd->cd_devs, M_DEVBUF);
|
|
|
|
cd->cd_devs = NULL;
|
|
|
|
cd->cd_ndevs = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return success.
|
|
|
|
*/
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2000-06-02 05:31:52 +04:00
|
|
|
config_activate(struct device *dev)
|
1998-11-17 11:38:07 +03:00
|
|
|
{
|
|
|
|
struct cfattach *ca = dev->dv_cfdata->cf_attach;
|
1998-11-18 21:38:07 +03:00
|
|
|
int rv = 0, oflags = dev->dv_flags;
|
1998-11-17 11:38:07 +03:00
|
|
|
|
|
|
|
if (ca->ca_activate == NULL)
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
|
|
|
|
if ((dev->dv_flags & DVF_ACTIVE) == 0) {
|
|
|
|
dev->dv_flags |= DVF_ACTIVE;
|
|
|
|
rv = (*ca->ca_activate)(dev, DVACT_ACTIVATE);
|
1998-11-18 21:38:07 +03:00
|
|
|
if (rv)
|
|
|
|
dev->dv_flags = oflags;
|
1998-11-17 11:38:07 +03:00
|
|
|
}
|
|
|
|
return (rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2000-06-02 05:31:52 +04:00
|
|
|
config_deactivate(struct device *dev)
|
1998-11-17 11:38:07 +03:00
|
|
|
{
|
|
|
|
struct cfattach *ca = dev->dv_cfdata->cf_attach;
|
1998-11-18 21:38:07 +03:00
|
|
|
int rv = 0, oflags = dev->dv_flags;
|
1998-11-17 11:38:07 +03:00
|
|
|
|
|
|
|
if (ca->ca_activate == NULL)
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
|
|
|
|
if (dev->dv_flags & DVF_ACTIVE) {
|
|
|
|
dev->dv_flags &= ~DVF_ACTIVE;
|
|
|
|
rv = (*ca->ca_activate)(dev, DVACT_DEACTIVATE);
|
1998-11-18 21:38:07 +03:00
|
|
|
if (rv)
|
|
|
|
dev->dv_flags = oflags;
|
1998-11-17 11:38:07 +03:00
|
|
|
}
|
|
|
|
return (rv);
|
|
|
|
}
|
|
|
|
|
1998-06-09 22:46:12 +04:00
|
|
|
/*
|
|
|
|
* Defer the configuration of the specified device until all
|
|
|
|
* of its parent's devices have been attached.
|
|
|
|
*/
|
|
|
|
void
|
2000-06-02 05:31:52 +04:00
|
|
|
config_defer(struct device *dev, void (*func)(struct device *))
|
1998-06-09 22:46:12 +04:00
|
|
|
{
|
|
|
|
struct deferred_config *dc;
|
|
|
|
|
|
|
|
if (dev->dv_parent == NULL)
|
|
|
|
panic("config_defer: can't defer config of a root device");
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
for (dc = TAILQ_FIRST(&deferred_config_queue); dc != NULL;
|
|
|
|
dc = TAILQ_NEXT(dc, dc_queue)) {
|
|
|
|
if (dc->dc_dev == dev)
|
|
|
|
panic("config_defer: deferred twice");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
1999-09-18 00:11:56 +04:00
|
|
|
dc = malloc(sizeof(*dc), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
|
|
|
|
if (dc == NULL)
|
|
|
|
panic("config_defer: unable to allocate callback");
|
1998-06-09 22:46:12 +04:00
|
|
|
|
|
|
|
dc->dc_dev = dev;
|
|
|
|
dc->dc_func = func;
|
|
|
|
TAILQ_INSERT_TAIL(&deferred_config_queue, dc, dc_queue);
|
2000-01-24 21:03:19 +03:00
|
|
|
config_pending_incr();
|
1998-06-09 22:46:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-09-15 23:37:08 +04:00
|
|
|
* Defer some autoconfiguration for a device until after interrupts
|
|
|
|
* are enabled.
|
|
|
|
*/
|
|
|
|
void
|
2000-06-02 05:31:52 +04:00
|
|
|
config_interrupts(struct device *dev, void (*func)(struct device *))
|
1999-09-15 23:37:08 +04:00
|
|
|
{
|
|
|
|
struct deferred_config *dc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If interrupts are enabled, callback now.
|
|
|
|
*/
|
1999-09-18 00:11:56 +04:00
|
|
|
if (cold == 0) {
|
1999-09-15 23:37:08 +04:00
|
|
|
(*func)(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
for (dc = TAILQ_FIRST(&interrupt_config_queue); dc != NULL;
|
|
|
|
dc = TAILQ_NEXT(dc, dc_queue)) {
|
|
|
|
if (dc->dc_dev == dev)
|
|
|
|
panic("config_interrupts: deferred twice");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
1999-09-18 00:11:56 +04:00
|
|
|
dc = malloc(sizeof(*dc), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
|
|
|
|
if (dc == NULL)
|
|
|
|
panic("config_interrupts: unable to allocate callback");
|
1999-09-15 23:37:08 +04:00
|
|
|
|
|
|
|
dc->dc_dev = dev;
|
|
|
|
dc->dc_func = func;
|
|
|
|
TAILQ_INSERT_TAIL(&interrupt_config_queue, dc, dc_queue);
|
2000-01-24 21:03:19 +03:00
|
|
|
config_pending_incr();
|
1999-09-15 23:37:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Process a deferred configuration queue.
|
1998-06-09 22:46:12 +04:00
|
|
|
*/
|
|
|
|
static void
|
2000-06-02 05:31:52 +04:00
|
|
|
config_process_deferred(struct deferred_config_head *queue,
|
|
|
|
struct device *parent)
|
1998-06-09 22:46:12 +04:00
|
|
|
{
|
|
|
|
struct deferred_config *dc, *ndc;
|
|
|
|
|
1999-09-15 23:37:08 +04:00
|
|
|
for (dc = TAILQ_FIRST(queue); dc != NULL; dc = ndc) {
|
1998-06-09 22:46:12 +04:00
|
|
|
ndc = TAILQ_NEXT(dc, dc_queue);
|
1999-09-15 23:37:08 +04:00
|
|
|
if (parent == NULL || dc->dc_dev->dv_parent == parent) {
|
|
|
|
TAILQ_REMOVE(queue, dc, dc_queue);
|
1998-06-09 22:46:12 +04:00
|
|
|
(*dc->dc_func)(dc->dc_dev);
|
|
|
|
free(dc, M_DEVBUF);
|
2000-01-24 21:03:19 +03:00
|
|
|
config_pending_decr();
|
1998-06-09 22:46:12 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-01-24 21:03:19 +03:00
|
|
|
/*
|
|
|
|
* Manipulate the config_pending semaphore.
|
|
|
|
*/
|
|
|
|
void
|
2000-06-02 05:31:52 +04:00
|
|
|
config_pending_incr(void)
|
2000-01-24 21:03:19 +03:00
|
|
|
{
|
|
|
|
|
|
|
|
config_pending++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2000-06-02 05:31:52 +04:00
|
|
|
config_pending_decr(void)
|
2000-01-24 21:03:19 +03:00
|
|
|
{
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if (config_pending == 0)
|
|
|
|
panic("config_pending_decr: config_pending == 0");
|
|
|
|
#endif
|
|
|
|
config_pending--;
|
|
|
|
if (config_pending == 0)
|
|
|
|
wakeup((void *)&config_pending);
|
|
|
|
}
|
|
|
|
|
1993-08-13 17:19:41 +04:00
|
|
|
/*
|
2000-06-04 23:14:14 +04:00
|
|
|
* Attach a statically-initialized event. The type and string pointers
|
|
|
|
* are already set up.
|
1993-08-13 17:19:41 +04:00
|
|
|
*/
|
|
|
|
void
|
2000-06-04 23:14:14 +04:00
|
|
|
evcnt_attach_static(struct evcnt *ev)
|
1993-08-13 17:19:41 +04:00
|
|
|
{
|
2000-06-04 23:14:14 +04:00
|
|
|
int len;
|
1993-08-13 17:19:41 +04:00
|
|
|
|
2000-06-04 23:14:14 +04:00
|
|
|
len = strlen(ev->ev_group);
|
1993-08-13 17:19:41 +04:00
|
|
|
#ifdef DIAGNOSTIC
|
2000-06-04 23:14:14 +04:00
|
|
|
if (len >= EVCNT_STRING_MAX) /* ..._MAX includes NUL */
|
|
|
|
panic("evcnt_attach_static: group length (%s)", ev->ev_group);
|
1993-08-13 17:19:41 +04:00
|
|
|
#endif
|
2000-06-04 23:14:14 +04:00
|
|
|
ev->ev_grouplen = len;
|
|
|
|
|
|
|
|
len = strlen(ev->ev_name);
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if (len >= EVCNT_STRING_MAX) /* ..._MAX includes NUL */
|
|
|
|
panic("evcnt_attach_static: name length (%s)", ev->ev_name);
|
|
|
|
#endif
|
|
|
|
ev->ev_namelen = len;
|
|
|
|
|
1996-04-04 04:25:44 +04:00
|
|
|
TAILQ_INSERT_TAIL(&allevents, ev, ev_list);
|
1993-08-13 17:19:41 +04:00
|
|
|
}
|
1998-11-17 11:38:07 +03:00
|
|
|
|
2000-06-04 23:14:14 +04:00
|
|
|
/*
|
|
|
|
* Attach a dynamically-initialized event. Zero it, set up the type
|
|
|
|
* and string pointers and then act like it was statically initialized.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
evcnt_attach_dynamic(struct evcnt *ev, int type, const struct evcnt *parent,
|
|
|
|
const char *group, const char *name)
|
|
|
|
{
|
|
|
|
|
|
|
|
memset(ev, 0, sizeof *ev);
|
|
|
|
ev->ev_type = type;
|
|
|
|
ev->ev_parent = parent;
|
|
|
|
ev->ev_group = group;
|
|
|
|
ev->ev_name = name;
|
|
|
|
evcnt_attach_static(ev);
|
|
|
|
}
|
|
|
|
|
1998-11-17 11:38:07 +03:00
|
|
|
/*
|
|
|
|
* Detach an event.
|
|
|
|
*/
|
|
|
|
void
|
2000-06-02 05:31:52 +04:00
|
|
|
evcnt_detach(struct evcnt *ev)
|
1998-11-17 11:38:07 +03:00
|
|
|
{
|
|
|
|
|
|
|
|
TAILQ_REMOVE(&allevents, ev, ev_list);
|
|
|
|
}
|
2002-02-15 14:18:26 +03:00
|
|
|
|
|
|
|
#ifdef DDB
|
|
|
|
void
|
|
|
|
event_print(int full, void (*pr)(const char *, ...))
|
|
|
|
{
|
|
|
|
struct evcnt *evp;
|
|
|
|
|
|
|
|
TAILQ_FOREACH(evp, &allevents, ev_list) {
|
|
|
|
if (evp->ev_count == 0 && !full)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
(*pr)("evcnt type %d: %s %s = %lld\n", evp->ev_type,
|
|
|
|
evp->ev_group, evp->ev_name, evp->ev_count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* DDB */
|