subr_autoconf.c revision 1.21
1/*	$OpenBSD: subr_autoconf.c,v 1.21 1998/05/11 09:59:39 niklas Exp $	*/
2/*	$NetBSD: subr_autoconf.c,v 1.21 1996/04/04 06:06:18 cgd Exp $	*/
3
4/*
5 * Copyright (c) 1992, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * This software was developed by the Computer Systems Engineering group
9 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
10 * contributed to Berkeley.
11 *
12 * All advertising materials mentioning features or use of this software
13 * must display the following acknowledgement:
14 *	This product includes software developed by the University of
15 *	California, Lawrence Berkeley Laboratories.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
19 * are met:
20 * 1. Redistributions of source code must retain the above copyright
21 *    notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 *    notice, this list of conditions and the following disclaimer in the
24 *    documentation and/or other materials provided with the distribution.
25 * 3. All advertising materials mentioning features or use of this software
26 *    must display the following acknowledgement:
27 *	This product includes software developed by the University of
28 *	California, Berkeley and its contributors.
29 * 4. Neither the name of the University nor the names of its contributors
30 *    may be used to endorse or promote products derived from this software
31 *    without specific prior written permission.
32 *
33 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
34 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
37 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 * SUCH DAMAGE.
44 *
45 * from: Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp  (LBL)
46 *
47 *	@(#)subr_autoconf.c	8.1 (Berkeley) 6/10/93
48 */
49
50#include <sys/param.h>
51#include <sys/device.h>
52#include <sys/malloc.h>
53#include <sys/systm.h>
54#include <machine/limits.h>
55/* Extra stuff from Matthias Drochner <drochner@zelux6.zel.kfa-juelich.de> */
56#include <sys/queue.h>
57
58/* Bleh!  Need device_register proto */
59#if defined(__alpha__) || defined(hp300)
60#include <machine/autoconf.h>
61#endif /* __alpha__ || hp300 */
62
63/*
64 * Autoconfiguration subroutines.
65 */
66
67typedef int (*cond_predicate_t) __P((struct device *, void *));
68
69/*
70 * ioconf.c exports exactly two names: cfdata and cfroots.  All system
71 * devices and drivers are found via these tables.
72 */
73extern short cfroots[];
74
75#define	ROOT ((struct device *)NULL)
76
77struct matchinfo {
78	cfmatch_t fn;
79	struct	device *parent;
80	void	*match, *aux;
81	int	indirect, pri;
82};
83
84struct cftable_head allcftables;
85
86static struct cftable staticcftable = {
87	cfdata
88};
89
90#ifndef AUTOCONF_VERBOSE
91#define AUTOCONF_VERBOSE 0
92#endif /* AUTOCONF_VERBOSE */
93int autoconf_verbose = AUTOCONF_VERBOSE;	/* trace probe calls */
94
95static char *number __P((char *, int));
96static void mapply __P((struct matchinfo *, struct cfdata *));
97static int haschild __P((struct device *));
98int	detach_devices __P((cond_predicate_t, void *,
99	    config_detach_callback_t, void *));
100int	dev_matches_cfdata __P((struct device *dev, void *));
101int	parentdev_matches_cfdata __P((struct device *dev, void *));
102
103
104struct devicelist alldevs;		/* list of all devices */
105struct evcntlist allevents;		/* list of all event counters */
106
107/*
108 * Initialize autoconfiguration data structures.  This occurs before console
109 * initialization as that might require use of this subsystem.  Furthermore
110 * this means that malloc et al. isn't yet available.
111 */
112void
113config_init()
114{
115
116	TAILQ_INIT(&alldevs);
117	TAILQ_INIT(&allevents);
118	TAILQ_INIT(&allcftables);
119	TAILQ_INSERT_TAIL(&allcftables, &staticcftable, list);
120}
121
122/*
123 * Apply the matching function and choose the best.  This is used
124 * a few times and we want to keep the code small.
125 */
126static void
127mapply(m, cf)
128	register struct matchinfo *m;
129	register struct cfdata *cf;
130{
131	register int pri;
132	void *match;
133
134	if (m->indirect)
135		match = config_make_softc(m->parent, cf);
136	else
137		match = cf;
138
139	if (autoconf_verbose) {
140		printf(">>> probing for %s", cf->cf_driver->cd_name);
141		if (cf->cf_fstate == FSTATE_STAR)
142			printf("*\n");
143		else
144			printf("%d\n", cf->cf_unit);
145	}
146	if (m->fn != NULL)
147		pri = (*m->fn)(m->parent, match, m->aux);
148	else {
149	        if (cf->cf_attach->ca_match == NULL) {
150			panic("mapply: no match function for '%s' device\n",
151			    cf->cf_driver->cd_name);
152		}
153		pri = (*cf->cf_attach->ca_match)(m->parent, match, m->aux);
154	}
155	if (autoconf_verbose)
156		printf(">>> %s probe returned %d\n", cf->cf_driver->cd_name,
157		    pri);
158
159	if (pri > m->pri) {
160		if (m->indirect && m->match)
161			free(m->match, M_DEVBUF);
162		m->match = match;
163		m->pri = pri;
164	} else {
165		if (m->indirect)
166			free(match, M_DEVBUF);
167	}
168}
169
170/*
171 * Iterate over all potential children of some device, calling the given
172 * function (default being the child's match function) for each one.
173 * Nonzero returns are matches; the highest value returned is considered
174 * the best match.  Return the `found child' if we got a match, or NULL
175 * otherwise.  The `aux' pointer is simply passed on through.
176 *
177 * Note that this function is designed so that it can be used to apply
178 * an arbitrary function to all potential children (its return value
179 * can be ignored).
180 */
181void *
182config_search(fn, parent, aux)
183	cfmatch_t fn;
184	register struct device *parent;
185	void *aux;
186{
187	register struct cfdata *cf;
188	register short *p;
189	struct matchinfo m;
190	struct cftable *t;
191
192	m.fn = fn;
193	m.parent = parent;
194	m.match = NULL;
195	m.aux = aux;
196	m.indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect;
197	m.pri = 0;
198	for(t = allcftables.tqh_first; t; t = t->list.tqe_next) {
199		for (cf = t->tab; cf->cf_driver; cf++) {
200			/*
201			 * Skip cf if no longer eligible, otherwise scan
202			 * through parents for one matching `parent',
203			 * and try match function.
204			 */
205			if (cf->cf_fstate == FSTATE_FOUND)
206				continue;
207			if (cf->cf_fstate == FSTATE_DNOTFOUND ||
208			    cf->cf_fstate == FSTATE_DSTAR)
209				continue;
210			for (p = cf->cf_parents; *p >= 0; p++)
211				if (parent->dv_cfdata == &(t->tab)[*p])
212					mapply(&m, cf);
213		}
214	}
215	if (autoconf_verbose) {
216		if (m.match)
217			printf(">>> %s probe won\n",
218			    ((struct cfdata *)m.match)->cf_driver->cd_name);
219		else
220			printf(">>> no winning probe\n");
221	}
222	return (m.match);
223}
224
225/*
226 * Iterate over all potential children of some device, calling the given
227 * function for each one.
228 *
229 * Note that this function is designed so that it can be used to apply
230 * an arbitrary function to all potential children (its return value
231 * can be ignored).
232 */
233void
234config_scan(fn, parent)
235	cfscan_t fn;
236	register struct device *parent;
237{
238	register struct cfdata *cf;
239	register short *p;
240	void *match;
241	int indirect;
242	struct cftable *t;
243
244	indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect;
245	for (t = allcftables.tqh_first; t; t = t->list.tqe_next) {
246		for (cf = t->tab; cf->cf_driver; cf++) {
247			/*
248			 * Skip cf if no longer eligible, otherwise scan
249			 * through parents for one matching `parent',
250			 * and try match function.
251			 */
252			if (cf->cf_fstate == FSTATE_FOUND)
253				continue;
254			if (cf->cf_fstate == FSTATE_DNOTFOUND ||
255			    cf->cf_fstate == FSTATE_DSTAR)
256				continue;
257			for (p = cf->cf_parents; *p >= 0; p++)
258				if (parent->dv_cfdata == &(t->tab)[*p]) {
259					match = indirect?
260					    config_make_softc(parent, cf) :
261					    (void *)cf;
262					(*fn)(parent, match);
263				}
264		}
265	}
266}
267
268/*
269 * Find the given root device.
270 * This is much like config_search, but there is no parent.
271 */
272void *
273config_rootsearch(fn, rootname, aux)
274	register cfmatch_t fn;
275	register char *rootname;
276	register void *aux;
277{
278	register struct cfdata *cf;
279	register short *p;
280	struct matchinfo m;
281
282	m.fn = fn;
283	m.parent = ROOT;
284	m.match = NULL;
285	m.aux = aux;
286	m.indirect = 0;
287	m.pri = 0;
288	/*
289	 * Look at root entries for matching name.  We do not bother
290	 * with found-state here since only one root should ever be
291	 * searched (and it must be done first).
292	 */
293	for (p = cfroots; *p >= 0; p++) {
294		cf = &cfdata[*p];
295		if (strcmp(cf->cf_driver->cd_name, rootname) == 0)
296			mapply(&m, cf);
297	}
298	return (m.match);
299}
300
301char *msgs[3] = { "", " not configured\n", " unsupported\n" };
302
303/*
304 * The given `aux' argument describes a device that has been found
305 * on the given parent, but not necessarily configured.  Locate the
306 * configuration data for that device (using the submatch function
307 * provided, or using candidates' cd_match configuration driver
308 * functions) and attach it, and return true.  If the device was
309 * not configured, call the given `print' function and return 0.
310 */
311struct device *
312config_found_sm(parent, aux, print, submatch)
313	struct device *parent;
314	void *aux;
315	cfprint_t print;
316	cfmatch_t submatch;
317{
318	void *match;
319
320	if ((match = config_search(submatch, parent, aux)) != NULL)
321		return (config_attach(parent, match, aux, print));
322	if (print)
323		printf(msgs[(*print)(aux, parent->dv_xname)]);
324	return (NULL);
325}
326
327/*
328 * As above, but for root devices.
329 */
330struct device *
331config_rootfound(rootname, aux)
332	char *rootname;
333	void *aux;
334{
335	void *match;
336
337	if ((match = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL)
338		return (config_attach(ROOT, match, aux, (cfprint_t)NULL));
339	printf("root device %s not configured\n", rootname);
340	return (NULL);
341}
342
343/* just like sprintf(buf, "%d") except that it works from the end */
344static char *
345number(ep, n)
346	register char *ep;
347	register int n;
348{
349
350	*--ep = 0;
351	while (n >= 10) {
352		*--ep = (n % 10) + '0';
353		n /= 10;
354	}
355	*--ep = n + '0';
356	return (ep);
357}
358
359/*
360 * Attach a found device.  Allocates memory for device variables.
361 */
362struct device *
363config_attach(parent, match, aux, print)
364	register struct device *parent;
365	void *match;
366	register void *aux;
367	cfprint_t print;
368{
369	register struct cfdata *cf;
370	register struct device *dev;
371	register struct cfdriver *cd;
372	register struct cfattach *ca;
373	struct cftable *t;
374
375	if (parent && parent->dv_cfdata->cf_driver->cd_indirect) {
376		dev = match;
377		cf = dev->dv_cfdata;
378	} else {
379		cf = match;
380		dev = config_make_softc(parent, cf);
381	}
382
383	cd = cf->cf_driver;
384	ca = cf->cf_attach;
385
386	cd->cd_devs[dev->dv_unit] = dev;
387
388	/*
389	 * If this is a "STAR" device and we used the last unit, prepare for
390	 * another one.
391	 */
392	if (cf->cf_fstate == FSTATE_STAR) {
393		if (dev->dv_unit == cf->cf_unit)
394			cf->cf_unit++;
395	} else
396		cf->cf_fstate = FSTATE_FOUND;
397
398	TAILQ_INSERT_TAIL(&alldevs, dev, dv_list);
399
400	if (parent == ROOT)
401		printf("%s (root)", dev->dv_xname);
402	else {
403		printf("%s at %s", dev->dv_xname, parent->dv_xname);
404		if (print)
405			(void) (*print)(aux, (char *)0);
406	}
407
408	/*
409	 * Before attaching, clobber any unfound devices that are
410	 * otherwise identical, or bump the unit number on all starred
411	 * cfdata for this device.
412	 */
413	for (t = allcftables.tqh_first; t; t = t->list.tqe_next) {
414		for (cf = t->tab; cf->cf_driver; cf++)
415			if (cf->cf_driver == cd &&
416			    cf->cf_unit == dev->dv_unit) {
417				if (cf->cf_fstate == FSTATE_NOTFOUND)
418					cf->cf_fstate = FSTATE_FOUND;
419				if (cf->cf_fstate == FSTATE_STAR)
420					cf->cf_unit++;
421			}
422	}
423#if defined(__alpha__) || defined(hp300)
424	device_register(dev, aux);
425#endif
426	(*ca->ca_attach)(parent, dev, aux);
427	return (dev);
428}
429
430struct device *
431config_make_softc(parent, cf)
432	struct device *parent;
433	struct cfdata *cf;
434{
435	register struct device *dev;
436	register struct cfdriver *cd;
437	register struct cfattach *ca;
438	register size_t lname, lunit;
439	register char *xunit;
440	char num[10];
441
442	cd = cf->cf_driver;
443	ca = cf->cf_attach;
444	if (ca->ca_devsize < sizeof(struct device))
445		panic("config_make_softc");
446
447	/* get memory for all device vars */
448	dev = (struct device *)malloc(ca->ca_devsize, M_DEVBUF, M_NOWAIT);
449	if (!dev)
450		panic("config_make_softc: allocation for device softc failed");
451	bzero(dev, ca->ca_devsize);
452	dev->dv_class = cd->cd_class;
453	dev->dv_cfdata = cf;
454
455	/* If this is a STAR device, search for a free unit number */
456	if (cf->cf_fstate == FSTATE_STAR) {
457		for (dev->dv_unit = cf->cf_starunit1;
458		    dev->dv_unit < cf->cf_unit; dev->dv_unit++)
459			if (cd->cd_ndevs == 0 ||
460			    cd->cd_devs[dev->dv_unit] == NULL)
461				break;
462	} else
463		dev->dv_unit = cf->cf_unit;
464
465	/* compute length of name and decimal expansion of unit number */
466	lname = strlen(cd->cd_name);
467	xunit = number(&num[sizeof num], dev->dv_unit);
468	lunit = &num[sizeof num] - xunit;
469	if (lname + lunit >= sizeof(dev->dv_xname))
470		panic("config_make_softc: device name too long");
471
472	bcopy(cd->cd_name, dev->dv_xname, lname);
473	bcopy(xunit, dev->dv_xname + lname, lunit);
474	dev->dv_parent = parent;
475
476	/* put this device in the devices array */
477	if (dev->dv_unit >= cd->cd_ndevs) {
478		/*
479		 * Need to expand the array.
480		 */
481		int old = cd->cd_ndevs, new;
482		void **nsp;
483
484		if (old == 0)
485			new = MINALLOCSIZE / sizeof(void *);
486		else
487			new = old * 2;
488		while (new <= dev->dv_unit)
489			new *= 2;
490		cd->cd_ndevs = new;
491		nsp = malloc(new * sizeof(void *), M_DEVBUF, M_NOWAIT);
492		if (nsp == 0)
493			panic("config_make_softc: %sing dev array",
494			    old != 0 ? "expand" : "creat");
495		bzero(nsp + old, (new - old) * sizeof(void *));
496		if (old != 0) {
497			bcopy(cd->cd_devs, nsp, old * sizeof(void *));
498			free(cd->cd_devs, M_DEVBUF);
499		}
500		cd->cd_devs = nsp;
501	}
502	if (cd->cd_devs[dev->dv_unit])
503		panic("config_make_softc: duplicate %s", dev->dv_xname);
504
505	return (dev);
506}
507
508/*
509 * Attach an event.  These must come from initially-zero space (see
510 * commented-out assignments below), but that occurs naturally for
511 * device instance variables.
512 */
513void
514evcnt_attach(dev, name, ev)
515	struct device *dev;
516	const char *name;
517	struct evcnt *ev;
518{
519
520#ifdef DIAGNOSTIC
521	if (strlen(name) >= sizeof(ev->ev_name))
522		panic("evcnt_attach");
523#endif
524	/* ev->ev_next = NULL; */
525	ev->ev_dev = dev;
526	/* ev->ev_count = 0; */
527	strcpy(ev->ev_name, name);
528	TAILQ_INSERT_TAIL(&allevents, ev, ev_list);
529}
530
531static int
532haschild(dev)
533	struct device *dev;
534{
535	struct device *d;
536
537	for (d = alldevs.tqh_first; d != NULL; d = d->dv_list.tqe_next) {
538		if (d->dv_parent == dev)
539			return(1);
540	}
541	return(0);
542}
543
544int
545detach_devices(cond, condarg, callback, arg)
546	cond_predicate_t cond;
547	void *condarg;
548	config_detach_callback_t callback;
549	void *arg;
550{
551	struct device *d;
552	int alldone = 1;
553
554	/*
555	 * XXX should use circleq and run around the list backwards
556	 * to allow for predicates to match children.
557	 */
558	d = alldevs.tqh_first;
559	while (d != NULL) {
560		if ((*cond)(d, condarg)) {
561			struct cfdriver *drv = d->dv_cfdata->cf_driver;
562
563			/* device not busy? */
564			/* driver's detach routine decides, upper
565			   layer (eg bus dependent code) is notified
566			   via callback */
567#ifdef DEBUG
568			printf("trying to detach device %s (%p)\n",
569			       d->dv_xname, d);
570#endif
571			if (!haschild(d) &&
572			    d->dv_cfdata->cf_attach->ca_detach &&
573			    ((*(d->dv_cfdata->cf_attach->ca_detach))(d)) == 0) {
574				int needit, i;
575				struct device *help;
576
577				if (callback)
578					(*callback)(d, arg);
579
580				/* remove reference in driver's devicelist */
581				if ((d->dv_unit >= drv->cd_ndevs) ||
582				    (drv->cd_devs[d->dv_unit]!=d))
583					panic("bad unit in detach_devices");
584				drv->cd_devs[d->dv_unit] = NULL;
585
586				/* driver is not needed anymore? */
587				needit = 0;
588				for(i = 0; i<drv->cd_ndevs; i++)
589					if (drv->cd_devs[i])
590						needit = 1;
591
592				if (!needit) {
593					/* free devices array (alloc'd
594                                           in config_make_softc) */
595					free(drv->cd_devs, M_DEVBUF);
596					drv->cd_ndevs = 0;
597				}
598
599				/* remove entry in global device list */
600				help = d->dv_list.tqe_next;
601				TAILQ_REMOVE(&alldevs, d, dv_list);
602#ifdef DEBUG
603				printf("%s removed\n", d->dv_xname);
604#endif
605				if (d->dv_cfdata->cf_fstate == FSTATE_FOUND)
606					d->dv_cfdata->cf_fstate =
607					    FSTATE_NOTFOUND;
608				/* free memory for dev data (alloc'd
609                                   in config_make_softc) */
610				free(d, M_DEVBUF);
611				d = help;
612				continue;
613			} else
614				alldone = 0;
615		}
616		d = d->dv_list.tqe_next;
617	}
618	return (!alldone);
619}
620
621int
622dev_matches_cfdata(dev, arg)
623	struct device *dev;
624	void *arg;
625{
626	struct cfdata *cfdata = arg;
627	return(/* device uses same driver ? */
628		(dev->dv_cfdata->cf_driver == cfdata->cf_driver)
629		/* device instance described by this cfdata? */
630		&& ((cfdata->cf_fstate == FSTATE_STAR)
631		    || ((cfdata->cf_fstate == FSTATE_FOUND)
632		        && (dev->dv_unit == cfdata->cf_unit)))
633		);
634}
635
636int
637parentdev_matches_cfdata(dev, arg)
638	struct device *dev;
639	void *arg;
640{
641	return (dev->dv_parent ? dev_matches_cfdata(dev->dv_parent, arg) : 0);
642}
643
644int
645config_detach(cf, callback, arg)
646	struct cfdata *cf;
647	config_detach_callback_t callback;
648	void *arg;
649{
650	return (detach_devices(dev_matches_cfdata, cf, callback, arg));
651}
652
653int
654config_detach_children(cf, callback, arg)
655	struct cfdata *cf;
656	config_detach_callback_t callback;
657	void *arg;
658{
659	return (detach_devices(parentdev_matches_cfdata, cf, callback, arg));
660}
661
662int
663attach_loadable(parentname, parentunit, cftable)
664	char *parentname;
665	int parentunit;
666	struct cftable *cftable;
667{
668	int found = 0;
669	struct device *d;
670
671	TAILQ_INSERT_TAIL(&allcftables, cftable, list);
672
673	for(d = alldevs.tqh_first; d != NULL; d = d->dv_list.tqe_next) {
674		struct cfdriver *drv = d->dv_cfdata->cf_driver;
675
676		if (strcmp(parentname, drv->cd_name) == NULL &&
677		    (parentunit == -1 || parentunit == d->dv_unit)) {
678			int s;
679
680			s = splhigh(); /* ??? */
681			found |= (*d->dv_cfdata->cf_attach->ca_reprobe)(d,
682			    &(cftable->tab[0]));
683			splx(s);
684		}
685	}
686	if (!found)
687		TAILQ_REMOVE(&allcftables, cftable, list);
688	return(found);
689}
690
691static int
692devcf_intable __P((struct device *, void *));
693
694static int
695devcf_intable(dev, arg)
696	struct device *dev;
697	void *arg;
698{
699	struct cftable *tbl = arg;
700	struct cfdata *cf;
701
702	for(cf = tbl->tab; cf->cf_driver; cf++) {
703		if (dev->dv_cfdata == cf)
704			return(1);
705	}
706	return(0);
707}
708
709int
710detach_loadable(cftable)
711	struct cftable *cftable;
712{
713	if (!detach_devices(devcf_intable, cftable, 0, 0))
714		return(0);
715	TAILQ_REMOVE(&allcftables, cftable, list);
716	return(1);
717}
718