152592Smckusick /* 252592Smckusick * Copyright (c) 1992 Regents of the University of California. 352592Smckusick * 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 * 9*56896Storek * All advertising materials mentioning features or use of this software 10*56896Storek * must display the following acknowledgement: 11*56896Storek * This product includes software developed by the University of 12*56896Storek * California, Lawrence Berkeley Laboratories. 13*56896Storek * 1452592Smckusick * %sccs.include.redist.c% 1552592Smckusick * 16*56896Storek * @(#)subr_autoconf.c 7.5 (Berkeley) 11/18/92 1752592Smckusick * 18*56896Storek * from: $Header: subr_autoconf.c,v 1.9 92/11/19 01:59:01 torek Exp $ (LBL) 1952592Smckusick */ 2052592Smckusick 2156517Sbostic #include <sys/param.h> 2256517Sbostic #include <sys/device.h> 2356517Sbostic #include <sys/malloc.h> 2452592Smckusick 2552592Smckusick /* 2652592Smckusick * Autoconfiguration subroutines. 2752592Smckusick */ 2852592Smckusick 29*56896Storek /* 30*56896Storek * ioconf.c exports exactly two names: cfdata and cfroots. All system 31*56896Storek * devices and drivers are found via these tables. 32*56896Storek */ 33*56896Storek extern struct cfdata cfdata[]; 34*56896Storek extern short cfroots[]; 3552592Smckusick 3652592Smckusick #define ROOT ((struct device *)NULL) 3752592Smckusick 3854121Smckusick struct matchinfo { 3954121Smckusick cfmatch_t fn; 4054121Smckusick struct device *parent; 4154121Smckusick void *aux; 4254121Smckusick struct cfdata *match; 4354121Smckusick int pri; 4454121Smckusick }; 4554121Smckusick 4652592Smckusick /* 4754121Smckusick * Apply the matching function and choose the best. This is used 4854121Smckusick * a few times and we want to keep the code small. 4954121Smckusick */ 5054121Smckusick static void 5154121Smckusick mapply(m, cf) 5254121Smckusick register struct matchinfo *m; 5354121Smckusick register struct cfdata *cf; 5454121Smckusick { 5554121Smckusick register int pri; 5654121Smckusick 5754121Smckusick if (m->fn != NULL) 5854121Smckusick pri = (*m->fn)(m->parent, cf, m->aux); 5954121Smckusick else 6054121Smckusick pri = (*cf->cf_driver->cd_match)(m->parent, cf, m->aux); 6154121Smckusick if (pri > m->pri) { 6254121Smckusick m->match = cf; 6354121Smckusick m->pri = pri; 6454121Smckusick } 6554121Smckusick } 6654121Smckusick 6754121Smckusick /* 6852592Smckusick * Iterate over all potential children of some device, calling the given 6952592Smckusick * function (default being the child's match function) for each one. 7052592Smckusick * Nonzero returns are matches; the highest value returned is considered 7152592Smckusick * the best match. Return the `found child' if we got a match, or NULL 7252592Smckusick * otherwise. The `aux' pointer is simply passed on through. 7352592Smckusick * 7452592Smckusick * Note that this function is designed so that it can be used to apply 7552592Smckusick * an arbitrary function to all potential children (its return value 7652592Smckusick * can be ignored). 7752592Smckusick */ 7852592Smckusick struct cfdata * 7954121Smckusick config_search(fn, parent, aux) 8054121Smckusick cfmatch_t fn; 8154121Smckusick register struct device *parent; 8254121Smckusick void *aux; 8352592Smckusick { 84*56896Storek register struct cfdata *cf; 8552592Smckusick register short *p; 8654121Smckusick struct matchinfo m; 8752592Smckusick 8854121Smckusick m.fn = fn; 8954121Smckusick m.parent = parent; 9054121Smckusick m.aux = aux; 9154121Smckusick m.match = NULL; 9254121Smckusick m.pri = 0; 9352592Smckusick for (cf = cfdata; cf->cf_driver; cf++) { 9452592Smckusick /* 95*56896Storek * Skip cf if no longer eligible, otherwise scan through 96*56896Storek * parents for one matching `parent', and try match function. 9752592Smckusick */ 98*56896Storek if (cf->cf_fstate == FSTATE_FOUND) 9952592Smckusick continue; 100*56896Storek for (p = cf->cf_parents; *p >= 0; p++) 101*56896Storek if (parent->dv_cfdata == &cfdata[*p]) 10254121Smckusick mapply(&m, cf); 10352592Smckusick } 10454121Smckusick return (m.match); 10552592Smckusick } 10652592Smckusick 10752592Smckusick /* 10852592Smckusick * Find the given root device. 10952592Smckusick * This is much like config_search, but there is no parent. 11052592Smckusick */ 11152592Smckusick struct cfdata * 11252592Smckusick config_rootsearch(fn, rootname, aux) 11352592Smckusick register cfmatch_t fn; 11452592Smckusick register char *rootname; 11552592Smckusick register void *aux; 11652592Smckusick { 11754121Smckusick register struct cfdata *cf; 118*56896Storek register short *p; 11954121Smckusick struct matchinfo m; 12052592Smckusick 12154121Smckusick m.fn = fn; 12254121Smckusick m.parent = ROOT; 12354121Smckusick m.aux = aux; 12454121Smckusick m.match = NULL; 12554121Smckusick m.pri = 0; 12654121Smckusick /* 12754121Smckusick * Look at root entries for matching name. We do not bother 128*56896Storek * with found-state here since only one root should ever be 129*56896Storek * searched (and it must be done first). 13054121Smckusick */ 131*56896Storek for (p = cfroots; *p >= 0; p++) { 132*56896Storek cf = &cfdata[*p]; 133*56896Storek if (strcmp(cf->cf_driver->cd_name, rootname) == 0) 13454121Smckusick mapply(&m, cf); 135*56896Storek } 13654121Smckusick return (m.match); 13752592Smckusick } 13852592Smckusick 13952592Smckusick static char *msgs[3] = { "", " not configured\n", " unsupported\n" }; 14052592Smckusick 14152592Smckusick /* 14252592Smckusick * The given `aux' argument describes a device that has been found 14352592Smckusick * on the given parent, but not necessarily configured. Locate the 14452592Smckusick * configuration data for that device (using the cd_match configuration 14552592Smckusick * driver function) and attach it, and return true. If the device was 14652592Smckusick * not configured, call the given `print' function and return 0. 14752592Smckusick */ 14852592Smckusick int 14952592Smckusick config_found(parent, aux, print) 15052592Smckusick struct device *parent; 15152592Smckusick void *aux; 15252592Smckusick cfprint_t print; 15352592Smckusick { 15452592Smckusick struct cfdata *cf; 15552592Smckusick 15652592Smckusick if ((cf = config_search((cfmatch_t)NULL, parent, aux)) != NULL) { 15752592Smckusick config_attach(parent, cf, aux, print); 15852592Smckusick return (1); 15952592Smckusick } 16052592Smckusick printf(msgs[(*print)(aux, parent->dv_xname)]); 16152592Smckusick return (0); 16252592Smckusick } 16352592Smckusick 16452592Smckusick /* 16552592Smckusick * As above, but for root devices. 16652592Smckusick */ 16752592Smckusick int 16852592Smckusick config_rootfound(rootname, aux) 16952592Smckusick char *rootname; 17052592Smckusick void *aux; 17152592Smckusick { 17252592Smckusick struct cfdata *cf; 17352592Smckusick 17452592Smckusick if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) { 17552592Smckusick config_attach(ROOT, cf, aux, (cfprint_t)NULL); 17652592Smckusick return (1); 17752592Smckusick } 17852592Smckusick printf("root device %s not configured\n", rootname); 17952592Smckusick return (0); 18052592Smckusick } 18152592Smckusick 18252592Smckusick /* just like sprintf(buf, "%d") except that it works from the end */ 18352592Smckusick static char * 18452592Smckusick number(ep, n) 18552592Smckusick register char *ep; 18652592Smckusick register int n; 18752592Smckusick { 18852592Smckusick 18952592Smckusick *--ep = 0; 19052592Smckusick while (n >= 10) { 19152592Smckusick *--ep = (n % 10) + '0'; 19252592Smckusick n /= 10; 19352592Smckusick } 19452592Smckusick *--ep = n + '0'; 19552592Smckusick return (ep); 19652592Smckusick } 19752592Smckusick 19852592Smckusick /* 19952592Smckusick * Attach a found device. Allocates memory for device variables. 20052592Smckusick */ 20152592Smckusick void 20252592Smckusick config_attach(parent, cf, aux, print) 20352592Smckusick register struct device *parent; 20452592Smckusick register struct cfdata *cf; 20552592Smckusick register void *aux; 20652592Smckusick cfprint_t print; 20752592Smckusick { 20852592Smckusick register struct device *dev; 20952592Smckusick register struct cfdriver *cd; 21052592Smckusick register size_t lname, lunit; 21152592Smckusick register char *xunit; 21254121Smckusick int myunit; 21352592Smckusick char num[10]; 21454121Smckusick static struct device **nextp = &alldevs; 21552592Smckusick 21652592Smckusick cd = cf->cf_driver; 21752592Smckusick if (cd->cd_devsize < sizeof(struct device)) 21852592Smckusick panic("config_attach"); 21954121Smckusick myunit = cf->cf_unit; 22052592Smckusick if (cf->cf_fstate == FSTATE_NOTFOUND) 22152592Smckusick cf->cf_fstate = FSTATE_FOUND; 22254121Smckusick else 22354121Smckusick cf->cf_unit++; 22452592Smckusick 22552592Smckusick /* compute length of name and decimal expansion of unit number */ 22652592Smckusick lname = strlen(cd->cd_name); 22754121Smckusick xunit = number(&num[sizeof num], myunit); 22852592Smckusick lunit = &num[sizeof num] - xunit; 229*56896Storek if (lname + lunit >= sizeof(dev->dv_xname)) 230*56896Storek panic("config_attach: device name too long"); 23152592Smckusick 232*56896Storek /* get memory for all device vars */ 233*56896Storek dev = (struct device *)malloc(cd->cd_devsize, M_DEVBUF, M_WAITOK); 234*56896Storek /* XXX cannot wait! */ 23552592Smckusick bzero(dev, cd->cd_devsize); 23654121Smckusick *nextp = dev; /* link up */ 23754121Smckusick nextp = &dev->dv_next; 23854121Smckusick dev->dv_class = cd->cd_class; 23954121Smckusick dev->dv_cfdata = cf; 24054121Smckusick dev->dv_unit = myunit; 241*56896Storek bcopy(cd->cd_name, dev->dv_xname, lname); 24252592Smckusick bcopy(xunit, dev->dv_xname + lname, lunit); 24352592Smckusick dev->dv_parent = parent; 244*56896Storek bcopy(cd->cd_evnam, dev->dv_evnam, sizeof(cd->cd_evnam)); 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) { 26152592Smckusick nsp = malloc(MINALLOCSIZE, M_DEVBUF, M_WAITOK); /*XXX*/ 26252592Smckusick bzero(nsp, MINALLOCSIZE); 26352592Smckusick cd->cd_ndevs = MINALLOCSIZE / sizeof(void *); 26452592Smckusick } else { 26552592Smckusick new = cd->cd_ndevs; 26652592Smckusick do { 26752592Smckusick new *= 2; 26852592Smckusick } while (new <= dev->dv_unit); 26952592Smckusick cd->cd_ndevs = new; 27052592Smckusick oldbytes = old * sizeof(void *); 27152592Smckusick newbytes = new * sizeof(void *); 27252592Smckusick nsp = malloc(newbytes, M_DEVBUF, M_WAITOK); /*XXX*/ 27352592Smckusick bcopy(cd->cd_devs, nsp, oldbytes); 27452592Smckusick bzero(&nsp[old], newbytes - oldbytes); 27552592Smckusick free(cd->cd_devs, M_DEVBUF); 27652592Smckusick } 27752592Smckusick cd->cd_devs = nsp; 27852592Smckusick } 27954121Smckusick if (cd->cd_devs[dev->dv_unit]) 28054121Smckusick panic("config_attach: duplicate %s", dev->dv_xname); 28152592Smckusick cd->cd_devs[dev->dv_unit] = dev; 282*56896Storek 283*56896Storek /* 284*56896Storek * Before attaching, clobber any unfound devices that are 285*56896Storek * otherwise identical. 286*56896Storek */ 287*56896Storek for (cf = cfdata; cf->cf_driver; cf++) 288*56896Storek if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit && 289*56896Storek cf->cf_fstate == FSTATE_NOTFOUND) 290*56896Storek cf->cf_fstate = FSTATE_FOUND; 29152592Smckusick (*cd->cd_attach)(parent, dev, aux); 29252592Smckusick } 293