152592Smckusick /*
263176Sbostic * Copyright (c) 1992, 1993
363176Sbostic * The Regents of the University of California. All rights reserved.
452592Smckusick *
554121Smckusick * This software was developed by the Computer Systems Engineering group
654121Smckusick * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
754121Smckusick * contributed to Berkeley.
852592Smckusick *
956896Storek * All advertising materials mentioning features or use of this software
1056896Storek * must display the following acknowledgement:
1156896Storek * This product includes software developed by the University of
1256896Storek * California, Lawrence Berkeley Laboratories.
1356896Storek *
1452592Smckusick * %sccs.include.redist.c%
1552592Smckusick *
16*67162Smckusick * @(#)subr_autoconf.c 8.3 (Berkeley) 05/17/94
1752592Smckusick *
1857761Storek * from: $Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp $ (LBL)
1952592Smckusick */
2052592Smckusick
2156517Sbostic #include <sys/param.h>
2256517Sbostic #include <sys/device.h>
2356517Sbostic #include <sys/malloc.h>
24*67162Smckusick #include <libkern/libkern.h>
2552592Smckusick
2652592Smckusick /*
2752592Smckusick * Autoconfiguration subroutines.
2852592Smckusick */
2952592Smckusick
3056896Storek /*
3156896Storek * ioconf.c exports exactly two names: cfdata and cfroots. All system
3256896Storek * devices and drivers are found via these tables.
3356896Storek */
3456896Storek extern struct cfdata cfdata[];
3556896Storek extern short cfroots[];
3652592Smckusick
3752592Smckusick #define ROOT ((struct device *)NULL)
3852592Smckusick
3954121Smckusick struct matchinfo {
4054121Smckusick cfmatch_t fn;
4154121Smckusick struct device *parent;
4254121Smckusick void *aux;
4354121Smckusick struct cfdata *match;
4454121Smckusick int pri;
4554121Smckusick };
4654121Smckusick
4752592Smckusick /*
4854121Smckusick * Apply the matching function and choose the best. This is used
4954121Smckusick * a few times and we want to keep the code small.
5054121Smckusick */
5154121Smckusick static void
mapply(m,cf)5254121Smckusick mapply(m, cf)
5354121Smckusick register struct matchinfo *m;
5454121Smckusick register struct cfdata *cf;
5554121Smckusick {
5654121Smckusick register int pri;
5754121Smckusick
5854121Smckusick if (m->fn != NULL)
5954121Smckusick pri = (*m->fn)(m->parent, cf, m->aux);
6054121Smckusick else
6154121Smckusick pri = (*cf->cf_driver->cd_match)(m->parent, cf, m->aux);
6254121Smckusick if (pri > m->pri) {
6354121Smckusick m->match = cf;
6454121Smckusick m->pri = pri;
6554121Smckusick }
6654121Smckusick }
6754121Smckusick
6854121Smckusick /*
6952592Smckusick * Iterate over all potential children of some device, calling the given
7052592Smckusick * function (default being the child's match function) for each one.
7152592Smckusick * Nonzero returns are matches; the highest value returned is considered
7252592Smckusick * the best match. Return the `found child' if we got a match, or NULL
7352592Smckusick * otherwise. The `aux' pointer is simply passed on through.
7452592Smckusick *
7552592Smckusick * Note that this function is designed so that it can be used to apply
7652592Smckusick * an arbitrary function to all potential children (its return value
7752592Smckusick * can be ignored).
7852592Smckusick */
7952592Smckusick struct cfdata *
config_search(fn,parent,aux)8054121Smckusick config_search(fn, parent, aux)
8154121Smckusick cfmatch_t fn;
8254121Smckusick register struct device *parent;
8354121Smckusick void *aux;
8452592Smckusick {
8556896Storek register struct cfdata *cf;
8652592Smckusick register short *p;
8754121Smckusick struct matchinfo m;
8852592Smckusick
8954121Smckusick m.fn = fn;
9054121Smckusick m.parent = parent;
9154121Smckusick m.aux = aux;
9254121Smckusick m.match = NULL;
9354121Smckusick m.pri = 0;
9452592Smckusick for (cf = cfdata; cf->cf_driver; cf++) {
9552592Smckusick /*
9656896Storek * Skip cf if no longer eligible, otherwise scan through
9756896Storek * parents for one matching `parent', and try match function.
9852592Smckusick */
9956896Storek if (cf->cf_fstate == FSTATE_FOUND)
10052592Smckusick continue;
10156896Storek for (p = cf->cf_parents; *p >= 0; p++)
10256896Storek if (parent->dv_cfdata == &cfdata[*p])
10354121Smckusick mapply(&m, cf);
10452592Smckusick }
10554121Smckusick return (m.match);
10652592Smckusick }
10752592Smckusick
10852592Smckusick /*
10952592Smckusick * Find the given root device.
11052592Smckusick * This is much like config_search, but there is no parent.
11152592Smckusick */
11252592Smckusick struct cfdata *
config_rootsearch(fn,rootname,aux)11352592Smckusick config_rootsearch(fn, rootname, aux)
11452592Smckusick register cfmatch_t fn;
11552592Smckusick register char *rootname;
11652592Smckusick register void *aux;
11752592Smckusick {
11854121Smckusick register struct cfdata *cf;
11956896Storek register short *p;
12054121Smckusick struct matchinfo m;
12152592Smckusick
12254121Smckusick m.fn = fn;
12354121Smckusick m.parent = ROOT;
12454121Smckusick m.aux = aux;
12554121Smckusick m.match = NULL;
12654121Smckusick m.pri = 0;
12754121Smckusick /*
12854121Smckusick * Look at root entries for matching name. We do not bother
12956896Storek * with found-state here since only one root should ever be
13056896Storek * searched (and it must be done first).
13154121Smckusick */
13256896Storek for (p = cfroots; *p >= 0; p++) {
13356896Storek cf = &cfdata[*p];
13456896Storek if (strcmp(cf->cf_driver->cd_name, rootname) == 0)
13554121Smckusick mapply(&m, cf);
13656896Storek }
13754121Smckusick return (m.match);
13852592Smckusick }
13952592Smckusick
14052592Smckusick static char *msgs[3] = { "", " not configured\n", " unsupported\n" };
14152592Smckusick
14252592Smckusick /*
14352592Smckusick * The given `aux' argument describes a device that has been found
14452592Smckusick * on the given parent, but not necessarily configured. Locate the
14552592Smckusick * configuration data for that device (using the cd_match configuration
14652592Smckusick * driver function) and attach it, and return true. If the device was
14752592Smckusick * not configured, call the given `print' function and return 0.
14852592Smckusick */
14952592Smckusick int
config_found(parent,aux,print)15052592Smckusick config_found(parent, aux, print)
15152592Smckusick struct device *parent;
15252592Smckusick void *aux;
15352592Smckusick cfprint_t print;
15452592Smckusick {
15552592Smckusick struct cfdata *cf;
15652592Smckusick
15752592Smckusick if ((cf = config_search((cfmatch_t)NULL, parent, aux)) != NULL) {
15852592Smckusick config_attach(parent, cf, aux, print);
15952592Smckusick return (1);
16052592Smckusick }
16152592Smckusick printf(msgs[(*print)(aux, parent->dv_xname)]);
16252592Smckusick return (0);
16352592Smckusick }
16452592Smckusick
16552592Smckusick /*
16652592Smckusick * As above, but for root devices.
16752592Smckusick */
16852592Smckusick int
config_rootfound(rootname,aux)16952592Smckusick config_rootfound(rootname, aux)
17052592Smckusick char *rootname;
17152592Smckusick void *aux;
17252592Smckusick {
17352592Smckusick struct cfdata *cf;
17452592Smckusick
17552592Smckusick if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) {
17652592Smckusick config_attach(ROOT, cf, aux, (cfprint_t)NULL);
17752592Smckusick return (1);
17852592Smckusick }
17952592Smckusick printf("root device %s not configured\n", rootname);
18052592Smckusick return (0);
18152592Smckusick }
18252592Smckusick
18352592Smckusick /* just like sprintf(buf, "%d") except that it works from the end */
18452592Smckusick static char *
number(ep,n)18552592Smckusick number(ep, n)
18652592Smckusick register char *ep;
18752592Smckusick register int n;
18852592Smckusick {
18952592Smckusick
19052592Smckusick *--ep = 0;
19152592Smckusick while (n >= 10) {
19252592Smckusick *--ep = (n % 10) + '0';
19352592Smckusick n /= 10;
19452592Smckusick }
19552592Smckusick *--ep = n + '0';
19652592Smckusick return (ep);
19752592Smckusick }
19852592Smckusick
19952592Smckusick /*
20052592Smckusick * Attach a found device. Allocates memory for device variables.
20152592Smckusick */
20252592Smckusick void
config_attach(parent,cf,aux,print)20352592Smckusick config_attach(parent, cf, aux, print)
20452592Smckusick register struct device *parent;
20552592Smckusick register struct cfdata *cf;
20652592Smckusick register void *aux;
20752592Smckusick cfprint_t print;
20852592Smckusick {
20952592Smckusick register struct device *dev;
21052592Smckusick register struct cfdriver *cd;
21152592Smckusick register size_t lname, lunit;
21252592Smckusick register char *xunit;
21354121Smckusick int myunit;
21452592Smckusick char num[10];
21554121Smckusick static struct device **nextp = &alldevs;
21652592Smckusick
21752592Smckusick cd = cf->cf_driver;
21852592Smckusick if (cd->cd_devsize < sizeof(struct device))
21952592Smckusick panic("config_attach");
22054121Smckusick myunit = cf->cf_unit;
22152592Smckusick if (cf->cf_fstate == FSTATE_NOTFOUND)
22252592Smckusick cf->cf_fstate = FSTATE_FOUND;
22354121Smckusick else
22454121Smckusick cf->cf_unit++;
22552592Smckusick
22652592Smckusick /* compute length of name and decimal expansion of unit number */
22752592Smckusick lname = strlen(cd->cd_name);
22854121Smckusick xunit = number(&num[sizeof num], myunit);
22952592Smckusick lunit = &num[sizeof num] - xunit;
23056896Storek if (lname + lunit >= sizeof(dev->dv_xname))
23156896Storek panic("config_attach: device name too long");
23252592Smckusick
23356896Storek /* get memory for all device vars */
23456896Storek dev = (struct device *)malloc(cd->cd_devsize, M_DEVBUF, M_WAITOK);
23556896Storek /* XXX cannot wait! */
23652592Smckusick bzero(dev, cd->cd_devsize);
23754121Smckusick *nextp = dev; /* link up */
23854121Smckusick nextp = &dev->dv_next;
23954121Smckusick dev->dv_class = cd->cd_class;
24054121Smckusick dev->dv_cfdata = cf;
24154121Smckusick dev->dv_unit = myunit;
24256896Storek bcopy(cd->cd_name, dev->dv_xname, lname);
24352592Smckusick bcopy(xunit, dev->dv_xname + lname, lunit);
24452592Smckusick dev->dv_parent = parent;
24552592Smckusick if (parent == ROOT)
24652592Smckusick printf("%s (root)", dev->dv_xname);
24752592Smckusick else {
24852592Smckusick printf("%s at %s", dev->dv_xname, parent->dv_xname);
24952592Smckusick (void) (*print)(aux, (char *)0);
25052592Smckusick }
25152592Smckusick
25252592Smckusick /* put this device in the devices array */
25352592Smckusick if (dev->dv_unit >= cd->cd_ndevs) {
25452592Smckusick /*
25552592Smckusick * Need to expand the array.
25652592Smckusick */
25752592Smckusick int old = cd->cd_ndevs, oldbytes, new, newbytes;
25852592Smckusick void **nsp;
25952592Smckusick
26052592Smckusick if (old == 0) {
26167130Smckusick new = max(MINALLOCSIZE / sizeof(void *),
26267130Smckusick dev->dv_unit + 1);
26367130Smckusick newbytes = new * sizeof(void *);
26467130Smckusick nsp = malloc(newbytes, M_DEVBUF, M_WAITOK); /*XXX*/
26567130Smckusick bzero(nsp, newbytes);
26652592Smckusick } else {
26752592Smckusick new = cd->cd_ndevs;
26852592Smckusick do {
26952592Smckusick new *= 2;
27052592Smckusick } while (new <= dev->dv_unit);
27152592Smckusick oldbytes = old * sizeof(void *);
27252592Smckusick newbytes = new * sizeof(void *);
27352592Smckusick nsp = malloc(newbytes, M_DEVBUF, M_WAITOK); /*XXX*/
27452592Smckusick bcopy(cd->cd_devs, nsp, oldbytes);
27552592Smckusick bzero(&nsp[old], newbytes - oldbytes);
27652592Smckusick free(cd->cd_devs, M_DEVBUF);
27752592Smckusick }
27867130Smckusick cd->cd_ndevs = new;
27952592Smckusick cd->cd_devs = nsp;
28052592Smckusick }
28154121Smckusick if (cd->cd_devs[dev->dv_unit])
28254121Smckusick panic("config_attach: duplicate %s", dev->dv_xname);
28352592Smckusick cd->cd_devs[dev->dv_unit] = dev;
28456896Storek
28556896Storek /*
28656896Storek * Before attaching, clobber any unfound devices that are
28756896Storek * otherwise identical.
28856896Storek */
28956896Storek for (cf = cfdata; cf->cf_driver; cf++)
29056896Storek if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit &&
29156896Storek cf->cf_fstate == FSTATE_NOTFOUND)
29256896Storek cf->cf_fstate = FSTATE_FOUND;
29352592Smckusick (*cd->cd_attach)(parent, dev, aux);
29452592Smckusick }
29557761Storek
29657761Storek /*
29757761Storek * Attach an event. These must come from initially-zero space (see
29857761Storek * commented-out assignments below), but that occurs naturally for
29957761Storek * device instance variables.
30057761Storek */
30157761Storek void
evcnt_attach(dev,name,ev)30257761Storek evcnt_attach(dev, name, ev)
30357761Storek struct device *dev;
30457761Storek const char *name;
30557761Storek struct evcnt *ev;
30657761Storek {
30757761Storek static struct evcnt **nextp = &allevents;
30857761Storek
30957761Storek #ifdef DIAGNOSTIC
31057761Storek if (strlen(name) >= sizeof(ev->ev_name))
31157761Storek panic("evcnt_attach");
31257761Storek #endif
31357761Storek /* ev->ev_next = NULL; */
31457761Storek ev->ev_dev = dev;
31557761Storek /* ev->ev_count = 0; */
31657761Storek strcpy(ev->ev_name, name);
31757761Storek *nextp = ev;
31857761Storek nextp = &ev->ev_next;
31957761Storek }
320