subr_autoconf.c revision 22975
154359Sroberto/*
254359Sroberto * Copyright (c) 1992, 1993
354359Sroberto *	The Regents of the University of California.  All rights reserved.
454359Sroberto *
554359Sroberto * This software was developed by the Computer Systems Engineering group
654359Sroberto * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
754359Sroberto * contributed to Berkeley.
854359Sroberto *
954359Sroberto * All advertising materials mentioning features or use of this software
1054359Sroberto * must display the following acknowledgement:
1154359Sroberto *	This product includes software developed by the University of
1254359Sroberto *	California, Lawrence Berkeley Laboratories.
1354359Sroberto *
1454359Sroberto * Redistribution and use in source and binary forms, with or without
1554359Sroberto * modification, are permitted provided that the following conditions
1654359Sroberto * are met:
1754359Sroberto * 1. Redistributions of source code must retain the above copyright
1854359Sroberto *    notice, this list of conditions and the following disclaimer.
1954359Sroberto * 2. Redistributions in binary form must reproduce the above copyright
2054359Sroberto *    notice, this list of conditions and the following disclaimer in the
2154359Sroberto *    documentation and/or other materials provided with the distribution.
2254359Sroberto * 3. All advertising materials mentioning features or use of this software
2354359Sroberto *    must display the following acknowledgement:
2454359Sroberto *	This product includes software developed by the University of
2554359Sroberto *	California, Berkeley and its contributors.
2654359Sroberto * 4. Neither the name of the University nor the names of its contributors
2754359Sroberto *    may be used to endorse or promote products derived from this software
2854359Sroberto *    without specific prior written permission.
2954359Sroberto *
3054359Sroberto * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
3154359Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3254359Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3354359Sroberto * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3454359Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3554359Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3654359Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3754359Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3854359Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3954359Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
4054359Sroberto * SUCH DAMAGE.
4154359Sroberto *
4254359Sroberto *	@(#)subr_autoconf.c	8.1 (Berkeley) 6/10/93
4354359Sroberto *
4454359Sroberto * $Id$
4554359Sroberto */
4654359Sroberto
4754359Sroberto#include <sys/param.h>
4854359Sroberto#include <sys/device.h>
4954359Sroberto#include <sys/malloc.h>
5054359Sroberto
5154359Sroberto/*
5254359Sroberto * Autoconfiguration subroutines.
5354359Sroberto */
5454359Sroberto
5554359Sroberto/*
5654359Sroberto * ioconf.c exports exactly two names: cfdata and cfroots.  All system
5754359Sroberto * devices and drivers are found via these tables.
5854359Sroberto */
5954359Srobertoextern struct cfdata cfdata[];
6054359Srobertoextern short cfroots[];
6154359Sroberto
6282498Sroberto#define	ROOT ((struct device *)NULL)
6354359Sroberto
64182007Srobertostruct matchinfo {
65106163Sroberto	cfmatch_t fn;
6654359Sroberto	struct	device *parent;
6754359Sroberto	void	*aux;
6854359Sroberto	struct	cfdata *match;
6954359Sroberto	int	pri;
7054359Sroberto};
7154359Sroberto
7254359Sroberto/*
7354359Sroberto * Apply the matching function and choose the best.  This is used
7454359Sroberto * a few times and we want to keep the code small.
7554359Sroberto */
7654359Srobertostatic void
7754359Srobertomapply(m, cf)
7854359Sroberto	register struct matchinfo *m;
7954359Sroberto	register struct cfdata *cf;
8054359Sroberto{
8154359Sroberto	register int pri;
8254359Sroberto
8354359Sroberto	if (m->fn != NULL)
8454359Sroberto		pri = (*m->fn)(m->parent, cf, m->aux);
8554359Sroberto	else
8654359Sroberto		pri = (*cf->cf_driver->cd_match)(m->parent, cf, m->aux);
8754359Sroberto	if (pri > m->pri) {
8854359Sroberto		m->match = cf;
8954359Sroberto		m->pri = pri;
9054359Sroberto	}
9154359Sroberto}
9254359Sroberto
9354359Sroberto/*
9454359Sroberto * Iterate over all potential children of some device, calling the given
9554359Sroberto * function (default being the child's match function) for each one.
9654359Sroberto * Nonzero returns are matches; the highest value returned is considered
9754359Sroberto * the best match.  Return the `found child' if we got a match, or NULL
9854359Sroberto * otherwise.  The `aux' pointer is simply passed on through.
9954359Sroberto *
10054359Sroberto * Note that this function is designed so that it can be used to apply
10154359Sroberto * an arbitrary function to all potential children (its return value
10254359Sroberto * can be ignored).
10354359Sroberto */
10454359Srobertostruct cfdata *
10554359Srobertoconfig_search(fn, parent, aux)
10654359Sroberto	cfmatch_t fn;
10754359Sroberto	register struct device *parent;
10854359Sroberto	void *aux;
10954359Sroberto{
11054359Sroberto	register struct cfdata *cf;
11154359Sroberto	register short *p;
11254359Sroberto	struct matchinfo m;
11354359Sroberto
11454359Sroberto	m.fn = fn;
11554359Sroberto	m.parent = parent;
11654359Sroberto	m.aux = aux;
11754359Sroberto	m.match = NULL;
11854359Sroberto	m.pri = 0;
11954359Sroberto	for (cf = cfdata; cf->cf_driver; cf++) {
12054359Sroberto		/*
12154359Sroberto		 * Skip cf if no longer eligible, otherwise scan through
12254359Sroberto		 * parents for one matching `parent', and try match function.
12354359Sroberto		 */
12454359Sroberto		if (cf->cf_fstate == FSTATE_FOUND)
12554359Sroberto			continue;
12654359Sroberto		for (p = cf->cf_parents; *p >= 0; p++)
12754359Sroberto			if (parent->dv_cfdata == &cfdata[*p])
12854359Sroberto				mapply(&m, cf);
12954359Sroberto	}
13054359Sroberto	return (m.match);
13154359Sroberto}
13254359Sroberto
13354359Sroberto/*
13454359Sroberto * Find the given root device.
13554359Sroberto * This is much like config_search, but there is no parent.
13654359Sroberto */
13754359Srobertostruct cfdata *
13854359Srobertoconfig_rootsearch(fn, rootname, aux)
13954359Sroberto	register cfmatch_t fn;
14054359Sroberto	register char *rootname;
14154359Sroberto	register void *aux;
14254359Sroberto{
14354359Sroberto	register struct cfdata *cf;
14454359Sroberto	register short *p;
14554359Sroberto	struct matchinfo m;
14654359Sroberto
14754359Sroberto	m.fn = fn;
14854359Sroberto	m.parent = ROOT;
14954359Sroberto	m.aux = aux;
15054359Sroberto	m.match = NULL;
15154359Sroberto	m.pri = 0;
15254359Sroberto	/*
15354359Sroberto	 * Look at root entries for matching name.  We do not bother
15454359Sroberto	 * with found-state here since only one root should ever be
15554359Sroberto	 * searched (and it must be done first).
15654359Sroberto	 */
15754359Sroberto	for (p = cfroots; *p >= 0; p++) {
15854359Sroberto		cf = &cfdata[*p];
15954359Sroberto		if (strcmp(cf->cf_driver->cd_name, rootname) == 0)
16054359Sroberto			mapply(&m, cf);
16154359Sroberto	}
16254359Sroberto	return (m.match);
16354359Sroberto}
16454359Sroberto
16554359Srobertostatic char *msgs[3] = { "", " not configured\n", " unsupported\n" };
16654359Sroberto
16754359Sroberto/*
16854359Sroberto * The given `aux' argument describes a device that has been found
16954359Sroberto * on the given parent, but not necessarily configured.  Locate the
17054359Sroberto * configuration data for that device (using the cd_match configuration
17154359Sroberto * driver function) and attach it, and return true.  If the device was
17254359Sroberto * not configured, call the given `print' function and return 0.
17354359Sroberto */
17454359Srobertoint
17554359Srobertoconfig_found(parent, aux, print)
17654359Sroberto	struct device *parent;
17754359Sroberto	void *aux;
17854359Sroberto	cfprint_t print;
179132451Sroberto{
180132451Sroberto	struct cfdata *cf;
18154359Sroberto
18254359Sroberto	if ((cf = config_search((cfmatch_t)NULL, parent, aux)) != NULL) {
18354359Sroberto		config_attach(parent, cf, aux, print);
18454359Sroberto		return (1);
18554359Sroberto	}
18654359Sroberto	printf(msgs[(*print)(aux, parent->dv_xname)]);
18754359Sroberto	return (0);
18854359Sroberto}
18954359Sroberto
19054359Sroberto/*
19154359Sroberto * As above, but for root devices.
19254359Sroberto */
19354359Srobertoint
19454359Srobertoconfig_rootfound(rootname, aux)
19554359Sroberto	char *rootname;
19654359Sroberto	void *aux;
19754359Sroberto{
19854359Sroberto	struct cfdata *cf;
19954359Sroberto
20054359Sroberto	if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) {
20154359Sroberto		config_attach(ROOT, cf, aux, (cfprint_t)NULL);
20254359Sroberto		return (1);
20354359Sroberto	}
20454359Sroberto	printf("root device %s not configured\n", rootname);
20554359Sroberto	return (0);
20654359Sroberto}
20754359Sroberto
20854359Sroberto/* just like sprintf(buf, "%d") except that it works from the end */
20954359Srobertostatic char *
21054359Srobertonumber(ep, n)
21154359Sroberto	register char *ep;
21254359Sroberto	register int n;
21354359Sroberto{
21454359Sroberto
21554359Sroberto	*--ep = 0;
21654359Sroberto	while (n >= 10) {
21754359Sroberto		*--ep = (n % 10) + '0';
21854359Sroberto		n /= 10;
21954359Sroberto	}
22054359Sroberto	*--ep = n + '0';
22154359Sroberto	return (ep);
22254359Sroberto}
22354359Sroberto
22454359Sroberto/*
22554359Sroberto * Attach a found device.  Allocates memory for device variables.
22654359Sroberto */
22754359Srobertovoid
228132451Srobertoconfig_attach(parent, cf, aux, print)
229132451Sroberto	register struct device *parent;
230132451Sroberto	register struct cfdata *cf;
231132451Sroberto	register void *aux;
23254359Sroberto	cfprint_t print;
23354359Sroberto{
23454359Sroberto	register struct device *dev;
23554359Sroberto	register struct cfdriver *cd;
23654359Sroberto	register size_t lname, lunit;
23754359Sroberto	register char *xunit;
23854359Sroberto	int myunit;
23954359Sroberto	char num[10];
24054359Sroberto	static struct device **nextp = &alldevs;
24154359Sroberto
24254359Sroberto	cd = cf->cf_driver;
24354359Sroberto	if (cd->cd_devsize < sizeof(struct device))
24454359Sroberto		panic("config_attach");
24554359Sroberto	myunit = cf->cf_unit;
24654359Sroberto	if (cf->cf_fstate == FSTATE_NOTFOUND)
24754359Sroberto		cf->cf_fstate = FSTATE_FOUND;
24854359Sroberto	else
249132451Sroberto		cf->cf_unit++;
250132451Sroberto
251132451Sroberto	/* compute length of name and decimal expansion of unit number */
252132451Sroberto	lname = strlen(cd->cd_name);
25354359Sroberto	xunit = number(&num[sizeof num], myunit);
25454359Sroberto	lunit = &num[sizeof num] - xunit;
25554359Sroberto	if (lname + lunit >= sizeof(dev->dv_xname))
256132451Sroberto		panic("config_attach: device name too long");
257132451Sroberto
258132451Sroberto	/* get memory for all device vars */
25954359Sroberto	dev = (struct device *)malloc(cd->cd_devsize, M_DEVBUF, M_WAITOK);
260182007Sroberto					/* XXX cannot wait! */
26154359Sroberto	bzero(dev, cd->cd_devsize);
26254359Sroberto	*nextp = dev;			/* link up */
26354359Sroberto	nextp = &dev->dv_next;
26454359Sroberto	dev->dv_class = cd->cd_class;
26554359Sroberto	dev->dv_cfdata = cf;
26654359Sroberto	dev->dv_unit = myunit;
26754359Sroberto	bcopy(cd->cd_name, dev->dv_xname, lname);
26854359Sroberto	bcopy(xunit, dev->dv_xname + lname, lunit);
26954359Sroberto	dev->dv_parent = parent;
27054359Sroberto	if (parent == ROOT)
271132451Sroberto		printf("%s (root)", dev->dv_xname);
27254359Sroberto	else {
27354359Sroberto		printf("%s at %s", dev->dv_xname, parent->dv_xname);
27454359Sroberto		(void) (*print)(aux, (char *)0);
27554359Sroberto	}
27654359Sroberto
27754359Sroberto	/* put this device in the devices array */
27854359Sroberto	if (dev->dv_unit >= cd->cd_ndevs) {
27954359Sroberto		/*
28054359Sroberto		 * Need to expand the array.
28154359Sroberto		 */
28254359Sroberto		int old = cd->cd_ndevs, oldbytes, new, newbytes;
28354359Sroberto		void **nsp;
28454359Sroberto
285132451Sroberto		if (old == 0) {
286132451Sroberto			nsp = malloc(MINALLOCSIZE, M_DEVBUF, M_WAITOK);	/*XXX*/
287182007Sroberto			bzero(nsp, MINALLOCSIZE);
288132451Sroberto			cd->cd_ndevs = MINALLOCSIZE / sizeof(void *);
289132451Sroberto		} else {
290132451Sroberto			new = cd->cd_ndevs;
291132451Sroberto			do {
292132451Sroberto				new *= 2;
293132451Sroberto			} while (new <= dev->dv_unit);
294132451Sroberto			cd->cd_ndevs = new;
295132451Sroberto			oldbytes = old * sizeof(void *);
296132451Sroberto			newbytes = new * sizeof(void *);
297132451Sroberto			nsp = malloc(newbytes, M_DEVBUF, M_WAITOK);	/*XXX*/
298132451Sroberto			bcopy(cd->cd_devs, nsp, oldbytes);
299132451Sroberto			bzero(&nsp[old], newbytes - oldbytes);
300132451Sroberto			free(cd->cd_devs, M_DEVBUF);
301132451Sroberto		}
302132451Sroberto		cd->cd_devs = nsp;
303132451Sroberto	}
304	if (cd->cd_devs[dev->dv_unit])
305		panic("config_attach: duplicate %s", dev->dv_xname);
306	cd->cd_devs[dev->dv_unit] = dev;
307
308	/*
309	 * Before attaching, clobber any unfound devices that are
310	 * otherwise identical.
311	 */
312	for (cf = cfdata; cf->cf_driver; cf++)
313		if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit &&
314		    cf->cf_fstate == FSTATE_NOTFOUND)
315			cf->cf_fstate = FSTATE_FOUND;
316	(*cd->cd_attach)(parent, dev, aux);
317}
318
319/*
320 * Attach an event.  These must come from initially-zero space (see
321 * commented-out assignments below), but that occurs naturally for
322 * device instance variables.
323 */
324void
325evcnt_attach(dev, name, ev)
326	struct device *dev;
327	const char *name;
328	struct evcnt *ev;
329{
330	static struct evcnt **nextp = &allevents;
331
332#ifdef DIAGNOSTIC
333	if (strlen(name) >= sizeof(ev->ev_name))
334		panic("evcnt_attach");
335#endif
336	/* ev->ev_next = NULL; */
337	ev->ev_dev = dev;
338	/* ev->ev_count = 0; */
339	strcpy(ev->ev_name, name);
340	*nextp = ev;
341	nextp = &ev->ev_next;
342}
343