subr_autoconf.c revision 1.13
144229Sdavidn/*	$OpenBSD: subr_autoconf.c,v 1.13 1996/10/18 14:46:35 niklas Exp $	*/
244229Sdavidn/*	$NetBSD: subr_autoconf.c,v 1.21 1996/04/04 06:06:18 cgd Exp $	*/
344229Sdavidn
444229Sdavidn/*
544229Sdavidn * Copyright (c) 1992, 1993
644229Sdavidn *	The Regents of the University of California.  All rights reserved.
744229Sdavidn *
844229Sdavidn * This software was developed by the Computer Systems Engineering group
944229Sdavidn * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
1044229Sdavidn * contributed to Berkeley.
1144229Sdavidn *
1244229Sdavidn * All advertising materials mentioning features or use of this software
1344229Sdavidn * must display the following acknowledgement:
1444229Sdavidn *	This product includes software developed by the University of
1544229Sdavidn *	California, Lawrence Berkeley Laboratories.
1644229Sdavidn *
1744229Sdavidn * Redistribution and use in source and binary forms, with or without
1844229Sdavidn * modification, are permitted provided that the following conditions
1944229Sdavidn * are met:
2044229Sdavidn * 1. Redistributions of source code must retain the above copyright
2144229Sdavidn *    notice, this list of conditions and the following disclaimer.
2244229Sdavidn * 2. Redistributions in binary form must reproduce the above copyright
2344229Sdavidn *    notice, this list of conditions and the following disclaimer in the
2444229Sdavidn *    documentation and/or other materials provided with the distribution.
2544229Sdavidn * 3. All advertising materials mentioning features or use of this software
2644229Sdavidn *    must display the following acknowledgement:
2744229Sdavidn *	This product includes software developed by the University of
2844229Sdavidn *	California, Berkeley and its contributors.
2944229Sdavidn * 4. Neither the name of the University nor the names of its contributors
3044229Sdavidn *    may be used to endorse or promote products derived from this software
3144229Sdavidn *    without specific prior written permission.
3244229Sdavidn *
3344229Sdavidn * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
3444229Sdavidn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3544229Sdavidn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3644229Sdavidn * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3744229Sdavidn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3844229Sdavidn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3944229Sdavidn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4044229Sdavidn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4144229Sdavidn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
4244229Sdavidn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
4344229Sdavidn * SUCH DAMAGE.
4444229Sdavidn *
4544229Sdavidn * from: Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp  (LBL)
4644229Sdavidn *
4744229Sdavidn *	@(#)subr_autoconf.c	8.1 (Berkeley) 6/10/93
4844229Sdavidn */
4944229Sdavidn
5044229Sdavidn#include <sys/param.h>
5144229Sdavidn#include <sys/device.h>
5244229Sdavidn#include <sys/malloc.h>
5344229Sdavidn#include <sys/systm.h>
5444229Sdavidn#include <machine/limits.h>
5544229Sdavidn/* Extra stuff from Matthias Drochner <drochner@zelux6.zel.kfa-juelich.de> */
5644229Sdavidn#include <sys/queue.h>
5744229Sdavidn
5844229Sdavidn/* Bleh!  Need device_register proto */
5944229Sdavidn#ifdef __alpha__
6044229Sdavidn#include <machine/autoconf.h>
6144229Sdavidn#endif /* __alpha__ */
6244229Sdavidn
6344229Sdavidn/*
6444229Sdavidn * Autoconfiguration subroutines.
6544229Sdavidn */
6644229Sdavidn
6744229Sdavidn/*
6844229Sdavidn * ioconf.c exports exactly two names: cfdata and cfroots.  All system
6944229Sdavidn * devices and drivers are found via these tables.
7044229Sdavidn */
7144229Sdavidnextern struct cfdata cfdata[];
7244229Sdavidnextern short cfroots[];
7344229Sdavidn
7444229Sdavidn#define	ROOT ((struct device *)NULL)
7544229Sdavidn
7644229Sdavidnstruct matchinfo {
7744229Sdavidn	cfmatch_t fn;
7844229Sdavidn	struct	device *parent;
7944229Sdavidn	void	*match, *aux;
8044229Sdavidn	int	indirect, pri;
8144229Sdavidn};
8244229Sdavidn
8344229Sdavidnstruct cftable_head allcftables;
8444229Sdavidn
8544229Sdavidnstatic struct cftable staticcftable = {
8644229Sdavidn	cfdata
8744229Sdavidn};
8844229Sdavidn
8944229Sdavidnstatic char *number __P((char *, int));
9044229Sdavidnstatic void mapply __P((struct matchinfo *, struct cfdata *));
9144229Sdavidn
9244229Sdavidnstruct devicelist alldevs;		/* list of all devices */
9344229Sdavidnstruct evcntlist allevents;		/* list of all event counters */
9444229Sdavidn
9544229Sdavidn/*
9644229Sdavidn * Initialize autoconfiguration data structures.  This occurs before console
9744229Sdavidn * initialization as that might require use of this subsystem.  Furthermore
9844229Sdavidn * this means that malloc et al. isn't yet available.
9944229Sdavidn */
10044229Sdavidnvoid
10144229Sdavidnconfig_init()
10244229Sdavidn{
10344229Sdavidn
10444229Sdavidn	TAILQ_INIT(&alldevs);
10544229Sdavidn	TAILQ_INIT(&allevents);
10644229Sdavidn	TAILQ_INIT(&allcftables);
10744229Sdavidn	TAILQ_INSERT_TAIL(&allcftables, &staticcftable, list);
10844229Sdavidn}
10944229Sdavidn
11044229Sdavidn/*
11144229Sdavidn * Apply the matching function and choose the best.  This is used
11244229Sdavidn * a few times and we want to keep the code small.
11344229Sdavidn */
11444229Sdavidnstatic void
11544229Sdavidnmapply(m, cf)
11644229Sdavidn	register struct matchinfo *m;
11744229Sdavidn	register struct cfdata *cf;
11844229Sdavidn{
11944229Sdavidn	register int pri;
12044229Sdavidn	void *match;
12144229Sdavidn
12244229Sdavidn	if (m->indirect)
12344229Sdavidn		match = config_make_softc(m->parent, cf);
12444229Sdavidn	else
12544229Sdavidn		match = cf;
12644229Sdavidn
12744229Sdavidn	if (m->fn != NULL)
12844229Sdavidn		pri = (*m->fn)(m->parent, match, m->aux);
12944229Sdavidn	else {
13044229Sdavidn	        if (cf->cf_attach->ca_match == NULL) {
13144229Sdavidn			panic("mapply: no match function for '%s' device\n",
13244229Sdavidn			    cf->cf_driver->cd_name);
13344229Sdavidn		}
13444229Sdavidn		pri = (*cf->cf_attach->ca_match)(m->parent, match, m->aux);
13544229Sdavidn	}
13644229Sdavidn
13744229Sdavidn	if (pri > m->pri) {
13844229Sdavidn		if (m->indirect && m->match)
13944229Sdavidn			free(m->match, M_DEVBUF);
14044229Sdavidn		m->match = match;
14144229Sdavidn		m->pri = pri;
14244229Sdavidn	} else {
14344229Sdavidn		if (m->indirect)
14444229Sdavidn			free(match, M_DEVBUF);
14544229Sdavidn	}
14644229Sdavidn}
14744229Sdavidn
14844229Sdavidn/*
14944229Sdavidn * Iterate over all potential children of some device, calling the given
15044229Sdavidn * function (default being the child's match function) for each one.
15144229Sdavidn * Nonzero returns are matches; the highest value returned is considered
15244229Sdavidn * the best match.  Return the `found child' if we got a match, or NULL
15344229Sdavidn * otherwise.  The `aux' pointer is simply passed on through.
15444229Sdavidn *
15544229Sdavidn * Note that this function is designed so that it can be used to apply
15644229Sdavidn * an arbitrary function to all potential children (its return value
15744229Sdavidn * can be ignored).
15844229Sdavidn */
15944229Sdavidnvoid *
16044229Sdavidnconfig_search(fn, parent, aux)
16144229Sdavidn	cfmatch_t fn;
16244229Sdavidn	register struct device *parent;
16344229Sdavidn	void *aux;
16444229Sdavidn{
16544229Sdavidn	register struct cfdata *cf;
16644229Sdavidn	register short *p;
16744229Sdavidn	struct matchinfo m;
16844229Sdavidn	struct cftable *t;
16944229Sdavidn
17044229Sdavidn	m.fn = fn;
17144229Sdavidn	m.parent = parent;
17244229Sdavidn	m.match = NULL;
17344229Sdavidn	m.aux = aux;
17444229Sdavidn	m.indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect;
17544229Sdavidn	m.pri = 0;
17644229Sdavidn	for(t = allcftables.tqh_first; t; t = t->list.tqe_next){
17744229Sdavidn	  for (cf = t->tab; cf->cf_driver; cf++) {
17844229Sdavidn	    /*
17944229Sdavidn	     * Skip cf if no longer eligible, otherwise scan through
18044229Sdavidn	     * parents for one matching `parent', and try match function.
18144229Sdavidn	     */
18244229Sdavidn	    if (cf->cf_fstate == FSTATE_FOUND)
18344229Sdavidn	      continue;
18444229Sdavidn	    if (cf->cf_fstate == FSTATE_DNOTFOUND ||
18544229Sdavidn		cf->cf_fstate == FSTATE_DSTAR)
18644229Sdavidn	      continue;
18744229Sdavidn	    for (p = cf->cf_parents; *p >= 0; p++)
18844229Sdavidn	      if (parent->dv_cfdata == &(t->tab)[*p])
18944229Sdavidn		mapply(&m, cf);
19044229Sdavidn	  }
19144229Sdavidn	}
19244229Sdavidn	return (m.match);
19344229Sdavidn}
19444229Sdavidn
19544229Sdavidn/*
19644229Sdavidn * Iterate over all potential children of some device, calling the given
19744229Sdavidn * function for each one.
19844229Sdavidn *
19944229Sdavidn * Note that this function is designed so that it can be used to apply
20044229Sdavidn * an arbitrary function to all potential children (its return value
20144229Sdavidn * can be ignored).
20244229Sdavidn */
20344229Sdavidnvoid
20444229Sdavidnconfig_scan(fn, parent)
20544229Sdavidn	cfscan_t fn;
20644229Sdavidn	register struct device *parent;
20744229Sdavidn{
20844229Sdavidn	register struct cfdata *cf;
20944229Sdavidn	register short *p;
21044229Sdavidn	void *match;
21144229Sdavidn	int indirect;
21244229Sdavidn	struct cftable *t;
21344229Sdavidn
21444229Sdavidn	indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect;
21544229Sdavidn	for (t = allcftables.tqh_first; t; t = t->list.tqe_next) {
21644229Sdavidn	  for (cf = t->tab; cf->cf_driver; cf++) {
21744229Sdavidn	    /*
21844229Sdavidn	     * Skip cf if no longer eligible, otherwise scan through
21944229Sdavidn	     * parents for one matching `parent', and try match function.
22044229Sdavidn	     */
22144229Sdavidn	    if (cf->cf_fstate == FSTATE_FOUND)
22244229Sdavidn	      continue;
22344229Sdavidn	    if (cf->cf_fstate == FSTATE_DNOTFOUND ||
22444229Sdavidn		cf->cf_fstate == FSTATE_DSTAR)
22544229Sdavidn	      continue;
22644229Sdavidn	    for (p = cf->cf_parents; *p >= 0; p++)
22744229Sdavidn	      if (parent->dv_cfdata == &(t->tab)[*p]) {
22844229Sdavidn		if (indirect)
22944229Sdavidn		  match = config_make_softc(parent, cf);
23044229Sdavidn		else
23144229Sdavidn		  match = cf;
23244229Sdavidn		(*fn)(parent, match);
23344229Sdavidn	      }
23444229Sdavidn	  }
23544229Sdavidn	}
23644229Sdavidn}
23744229Sdavidn
23844229Sdavidn/*
23944229Sdavidn * Find the given root device.
24044229Sdavidn * This is much like config_search, but there is no parent.
24144229Sdavidn */
24244229Sdavidnvoid *
24344229Sdavidnconfig_rootsearch(fn, rootname, aux)
24444229Sdavidn	register cfmatch_t fn;
24544229Sdavidn	register char *rootname;
24644229Sdavidn	register void *aux;
24744229Sdavidn{
24844229Sdavidn	register struct cfdata *cf;
24944229Sdavidn	register short *p;
25044229Sdavidn	struct matchinfo m;
25144229Sdavidn
25244229Sdavidn	m.fn = fn;
25344229Sdavidn	m.parent = ROOT;
25444229Sdavidn	m.match = NULL;
25544229Sdavidn	m.aux = aux;
25644229Sdavidn	m.indirect = 0;
25744229Sdavidn	m.pri = 0;
25844229Sdavidn	/*
25944229Sdavidn	 * Look at root entries for matching name.  We do not bother
26044229Sdavidn	 * with found-state here since only one root should ever be
26144229Sdavidn	 * searched (and it must be done first).
26244229Sdavidn	 */
26344229Sdavidn	for (p = cfroots; *p >= 0; p++) {
26444229Sdavidn		cf = &cfdata[*p];
26544229Sdavidn		if (strcmp(cf->cf_driver->cd_name, rootname) == 0)
26644229Sdavidn			mapply(&m, cf);
26744229Sdavidn	}
26844229Sdavidn	return (m.match);
26944229Sdavidn}
27044229Sdavidn
27144229Sdavidnchar *msgs[3] = { "", " not configured\n", " unsupported\n" };
27244229Sdavidn
27344229Sdavidn/*
27444229Sdavidn * The given `aux' argument describes a device that has been found
27544229Sdavidn * on the given parent, but not necessarily configured.  Locate the
27644229Sdavidn * configuration data for that device (using the submatch function
27744229Sdavidn * provided, or using candidates' cd_match configuration driver
27844229Sdavidn * functions) and attach it, and return true.  If the device was
27944229Sdavidn * not configured, call the given `print' function and return 0.
28044229Sdavidn */
28144229Sdavidnstruct device *
28244229Sdavidnconfig_found_sm(parent, aux, print, submatch)
28344229Sdavidn	struct device *parent;
28444229Sdavidn	void *aux;
28544229Sdavidn	cfprint_t print;
28644229Sdavidn	cfmatch_t submatch;
28744229Sdavidn{
28844229Sdavidn	void *match;
28944229Sdavidn
29044229Sdavidn	if ((match = config_search(submatch, parent, aux)) != NULL)
29144229Sdavidn		return (config_attach(parent, match, aux, print));
29244229Sdavidn	if (print)
29344229Sdavidn		printf(msgs[(*print)(aux, parent->dv_xname)]);
29444229Sdavidn	return (NULL);
29544229Sdavidn}
29644229Sdavidn
29744229Sdavidn/*
29844229Sdavidn * As above, but for root devices.
29944229Sdavidn */
30044229Sdavidnstruct device *
30144229Sdavidnconfig_rootfound(rootname, aux)
30244229Sdavidn	char *rootname;
30344229Sdavidn	void *aux;
30444229Sdavidn{
30544229Sdavidn	void *match;
30644229Sdavidn
30744229Sdavidn	if ((match = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL)
30844229Sdavidn		return (config_attach(ROOT, match, aux, (cfprint_t)NULL));
30944229Sdavidn	printf("root device %s not configured\n", rootname);
31044229Sdavidn	return (NULL);
31144229Sdavidn}
31244229Sdavidn
31344229Sdavidn/* just like sprintf(buf, "%d") except that it works from the end */
31444229Sdavidnstatic char *
31544229Sdavidnnumber(ep, n)
31644229Sdavidn	register char *ep;
317	register int n;
318{
319
320	*--ep = 0;
321	while (n >= 10) {
322		*--ep = (n % 10) + '0';
323		n /= 10;
324	}
325	*--ep = n + '0';
326	return (ep);
327}
328
329/*
330 * Attach a found device.  Allocates memory for device variables.
331 */
332struct device *
333config_attach(parent, match, aux, print)
334	register struct device *parent;
335	void *match;
336	register void *aux;
337	cfprint_t print;
338{
339	register struct cfdata *cf;
340	register struct device *dev;
341	register struct cfdriver *cd;
342	register struct cfattach *ca;
343	struct cftable *t;
344
345	if (parent && parent->dv_cfdata->cf_driver->cd_indirect) {
346		dev = match;
347		cf = dev->dv_cfdata;
348	} else {
349		cf = match;
350		dev = config_make_softc(parent, cf);
351	}
352
353	cd = cf->cf_driver;
354	ca = cf->cf_attach;
355	cd->cd_devs[cf->cf_unit] = dev;
356
357	if (cf->cf_fstate == FSTATE_STAR)
358		cf->cf_unit++;
359	else
360		cf->cf_fstate = FSTATE_FOUND;
361
362	TAILQ_INSERT_TAIL(&alldevs, dev, dv_list);
363
364	if (parent == ROOT)
365		printf("%s (root)", dev->dv_xname);
366	else {
367		printf("%s at %s", dev->dv_xname, parent->dv_xname);
368		if (print)
369			(void) (*print)(aux, (char *)0);
370	}
371
372	/*
373	 * Before attaching, clobber any unfound devices that are
374	 * otherwise identical, or bump the unit number on all starred
375	 * cfdata for this device.
376	 */
377	for (t = allcftables.tqh_first; t; t = t->list.tqe_next) {
378	  for (cf = t->tab; cf->cf_driver; cf++)
379	    if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit) {
380			if (cf->cf_fstate == FSTATE_NOTFOUND)
381				cf->cf_fstate = FSTATE_FOUND;
382			if (cf->cf_fstate == FSTATE_STAR)
383				cf->cf_unit++;
384	    }
385	}
386#ifdef __alpha__
387	device_register(dev, aux);
388#endif
389	(*ca->ca_attach)(parent, dev, aux);
390	return (dev);
391}
392
393struct device *
394config_make_softc(parent, cf)
395	struct device *parent;
396	struct cfdata *cf;
397{
398	register struct device *dev;
399	register struct cfdriver *cd;
400	register struct cfattach *ca;
401	register size_t lname, lunit;
402	register char *xunit;
403	char num[10];
404
405	cd = cf->cf_driver;
406	ca = cf->cf_attach;
407	if (ca->ca_devsize < sizeof(struct device))
408		panic("config_make_softc");
409
410	/* compute length of name and decimal expansion of unit number */
411	lname = strlen(cd->cd_name);
412	xunit = number(&num[sizeof num], cf->cf_unit);
413	lunit = &num[sizeof num] - xunit;
414	if (lname + lunit >= sizeof(dev->dv_xname))
415		panic("config_attach: device name too long");
416
417	/* get memory for all device vars */
418	dev = (struct device *)malloc(ca->ca_devsize, M_DEVBUF, M_NOWAIT);
419	if (!dev)
420	    panic("config_attach: memory allocation for device softc failed");
421	bzero(dev, ca->ca_devsize);
422	dev->dv_class = cd->cd_class;
423	dev->dv_cfdata = cf;
424	dev->dv_unit = cf->cf_unit;
425	bcopy(cd->cd_name, dev->dv_xname, lname);
426	bcopy(xunit, dev->dv_xname + lname, lunit);
427	dev->dv_parent = parent;
428
429	/* put this device in the devices array */
430	if (dev->dv_unit >= cd->cd_ndevs) {
431		/*
432		 * Need to expand the array.
433		 */
434		int old = cd->cd_ndevs, new;
435		void **nsp;
436
437		if (old == 0)
438			new = MINALLOCSIZE / sizeof(void *);
439		else
440			new = old * 2;
441		while (new <= dev->dv_unit)
442			new *= 2;
443		cd->cd_ndevs = new;
444		nsp = malloc(new * sizeof(void *), M_DEVBUF, M_NOWAIT);
445		if (nsp == 0)
446			panic("config_attach: %sing dev array",
447			    old != 0 ? "expand" : "creat");
448		bzero(nsp + old, (new - old) * sizeof(void *));
449		if (old != 0) {
450			bcopy(cd->cd_devs, nsp, old * sizeof(void *));
451			free(cd->cd_devs, M_DEVBUF);
452		}
453		cd->cd_devs = nsp;
454	}
455	if (cd->cd_devs[dev->dv_unit])
456		panic("config_attach: duplicate %s", dev->dv_xname);
457
458	return (dev);
459}
460
461/*
462 * Attach an event.  These must come from initially-zero space (see
463 * commented-out assignments below), but that occurs naturally for
464 * device instance variables.
465 */
466void
467evcnt_attach(dev, name, ev)
468	struct device *dev;
469	const char *name;
470	struct evcnt *ev;
471{
472
473#ifdef DIAGNOSTIC
474	if (strlen(name) >= sizeof(ev->ev_name))
475		panic("evcnt_attach");
476#endif
477	/* ev->ev_next = NULL; */
478	ev->ev_dev = dev;
479	/* ev->ev_count = 0; */
480	strcpy(ev->ev_name, name);
481	TAILQ_INSERT_TAIL(&allevents, ev, ev_list);
482}
483
484typedef int (*cond_predicate_t) __P((struct device*, void*));
485
486static int haschild __P((struct device *));
487static int detach_devices __P((cond_predicate_t, void *,
488			       config_detach_callback_t, void *));
489
490static int
491haschild(dev)
492	struct device *dev;
493{
494	struct device *d;
495
496	for (d = alldevs.tqh_first;
497	     d != NULL;
498	     d = d->dv_list.tqe_next) {
499		if (d->dv_parent == dev)
500			return(1);
501	}
502	return(0);
503}
504
505static int
506detach_devices(cond, condarg, callback, arg)
507	cond_predicate_t cond;
508	void *condarg;
509	config_detach_callback_t callback;
510	void *arg;
511{
512	struct device *d;
513	int alldone = 1;
514
515	/*
516	 * XXX should use circleq and run around the list backwards
517	 * to allow for predicates to match children.
518	 */
519	d = alldevs.tqh_first;
520	while (d != NULL) {
521		if ((*cond)(d, condarg)) {
522			struct cfdriver *drv = d->dv_cfdata->cf_driver;
523
524			/* device not busy? */
525			/* driver's detach routine decides, upper
526			   layer (eg bus dependent code) is notified
527			   via callback */
528#ifdef DEBUG
529			printf("trying to detach device %s (%p)\n",
530			       d->dv_xname, d);
531#endif
532			if (!haschild(d) &&
533			    d->dv_cfdata->cf_attach->ca_detach &&
534			    ((*(d->dv_cfdata->cf_attach->ca_detach))(d)) == 0) {
535				int needit, i;
536				struct device *help;
537
538				if (callback)
539					(*callback)(d, arg);
540
541				/* remove reference in driver's devicelist */
542				if ((d->dv_unit >= drv->cd_ndevs) ||
543				    (drv->cd_devs[d->dv_unit]!=d))
544					panic("bad unit in detach_devices");
545				drv->cd_devs[d->dv_unit] = NULL;
546
547				/* driver is not needed anymore? */
548				needit = 0;
549				for(i = 0; i<drv->cd_ndevs; i++)
550					if (drv->cd_devs[i])
551						needit = 1;
552
553				if (!needit) {
554					/* free devices array (alloc'd
555                                           in config_make_softc) */
556					free(drv->cd_devs, M_DEVBUF);
557					drv->cd_ndevs = 0;
558				}
559
560				/* remove entry in global device list */
561				help = d->dv_list.tqe_next;
562				TAILQ_REMOVE(&alldevs, d, dv_list);
563#ifdef DEBUG
564				printf("%s removed\n", d->dv_xname);
565#endif
566				d->dv_cfdata->cf_fstate = FSTATE_NOTFOUND;
567				/* free memory for dev data (alloc'd
568                                   in config_make_softc) */
569				free(d, M_DEVBUF);
570				d = help;
571				continue;
572			} else
573				alldone = 0;
574		}
575		d = d->dv_list.tqe_next;
576	}
577	return(!alldone);
578}
579
580int dev_matches_cfdata __P((struct device *dev, void *));
581
582int
583dev_matches_cfdata(dev, arg)
584	struct device *dev;
585	void *arg;
586{
587	struct cfdata *cfdata = arg;
588	return(/* device uses same driver ? */
589		(dev->dv_cfdata->cf_driver == cfdata->cf_driver)
590		/* device instance described by this cfdata? */
591		&& ((cfdata->cf_fstate == FSTATE_STAR)
592		    || ((cfdata->cf_fstate == FSTATE_FOUND)
593		        && (dev->dv_unit == cfdata->cf_unit)))
594		);
595}
596
597int
598config_detach(cf, callback, arg)
599	struct cfdata *cf;
600	config_detach_callback_t callback;
601	void *arg;
602{
603	return(detach_devices(dev_matches_cfdata, cf, callback, arg));
604}
605
606int
607attach_loadable(parentname, parentunit, cftable)
608	char *parentname;
609	int parentunit;
610	struct cftable *cftable;
611{
612	int found = 0;
613	struct device *d;
614
615	TAILQ_INSERT_TAIL(&allcftables, cftable, list);
616
617	for(d = alldevs.tqh_first; d != NULL; d = d->dv_list.tqe_next) {
618		struct cfdriver *drv = d->dv_cfdata->cf_driver;
619
620		if (strcmp(parentname, drv->cd_name) == NULL &&
621		    (parentunit == -1 || parentunit == d->dv_unit)) {
622			int s;
623
624			s = splhigh(); /* ??? */
625			found |= (*d->dv_cfdata->cf_attach->ca_reprobe)(d,
626			    &(cftable->tab[0]));
627			splx(s);
628		}
629	}
630	if (!found)
631		TAILQ_REMOVE(&allcftables, cftable, list);
632	return(found);
633}
634
635static int
636devcf_intable __P((struct device *, void *));
637
638static int
639devcf_intable(dev, arg)
640	struct device *dev;
641	void *arg;
642{
643	struct cftable *tbl = arg;
644	struct cfdata *cf;
645
646	for(cf = tbl->tab; cf->cf_driver; cf++) {
647		if (dev->dv_cfdata == cf)
648			return(1);
649	}
650	return(0);
651}
652
653int
654detach_loadable(cftable)
655	struct cftable *cftable;
656{
657	if (!detach_devices(devcf_intable, cftable, 0, 0))
658		return(0);
659	TAILQ_REMOVE(&allcftables, cftable, list);
660	return(1);
661}
662