152592Smckusick /* 252592Smckusick * Copyright (c) 1992 Regents of the University of California. 352592Smckusick * All rights reserved. 452592Smckusick * 5*54121Smckusick * This software was developed by the Computer Systems Engineering group 6*54121Smckusick * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 7*54121Smckusick * contributed to Berkeley. 852592Smckusick * 952592Smckusick * %sccs.include.redist.c% 1052592Smckusick * 11*54121Smckusick * @(#)subr_autoconf.c 7.3 (Berkeley) 06/20/92 1252592Smckusick * 13*54121Smckusick * from: $Header: subr_autoconf.c,v 1.6 92/06/11 17:56:19 torek Exp $ (LBL) 1452592Smckusick */ 1552592Smckusick 1652592Smckusick #include "sys/param.h" 1752592Smckusick #include "sys/device.h" 1852592Smckusick #include "sys/malloc.h" 1952592Smckusick 2052592Smckusick /* 2152592Smckusick * Autoconfiguration subroutines. 2252592Smckusick */ 2352592Smckusick 2452592Smckusick extern struct cfdata cfdata[]; /* from ioconf.c */ 2552592Smckusick 2652592Smckusick #define ROOT ((struct device *)NULL) 2752592Smckusick 28*54121Smckusick struct matchinfo { 29*54121Smckusick cfmatch_t fn; 30*54121Smckusick struct device *parent; 31*54121Smckusick void *aux; 32*54121Smckusick struct cfdata *match; 33*54121Smckusick int pri; 34*54121Smckusick }; 35*54121Smckusick 3652592Smckusick /* 37*54121Smckusick * Apply the matching function and choose the best. This is used 38*54121Smckusick * a few times and we want to keep the code small. 39*54121Smckusick */ 40*54121Smckusick static void 41*54121Smckusick mapply(m, cf) 42*54121Smckusick register struct matchinfo *m; 43*54121Smckusick register struct cfdata *cf; 44*54121Smckusick { 45*54121Smckusick register int pri; 46*54121Smckusick 47*54121Smckusick if (m->fn != NULL) 48*54121Smckusick pri = (*m->fn)(m->parent, cf, m->aux); 49*54121Smckusick else 50*54121Smckusick pri = (*cf->cf_driver->cd_match)(m->parent, cf, m->aux); 51*54121Smckusick if (pri > m->pri) { 52*54121Smckusick m->match = cf; 53*54121Smckusick m->pri = pri; 54*54121Smckusick } 55*54121Smckusick } 56*54121Smckusick 57*54121Smckusick /* 5852592Smckusick * Iterate over all potential children of some device, calling the given 5952592Smckusick * function (default being the child's match function) for each one. 6052592Smckusick * Nonzero returns are matches; the highest value returned is considered 6152592Smckusick * the best match. Return the `found child' if we got a match, or NULL 6252592Smckusick * otherwise. The `aux' pointer is simply passed on through. 6352592Smckusick * 6452592Smckusick * Note that this function is designed so that it can be used to apply 6552592Smckusick * an arbitrary function to all potential children (its return value 6652592Smckusick * can be ignored). 6752592Smckusick */ 6852592Smckusick struct cfdata * 69*54121Smckusick config_search(fn, parent, aux) 70*54121Smckusick cfmatch_t fn; 71*54121Smckusick register struct device *parent; 72*54121Smckusick void *aux; 7352592Smckusick { 74*54121Smckusick register struct cfdata *cf, *pcf; 7552592Smckusick register short *p; 76*54121Smckusick struct matchinfo m; 7752592Smckusick 78*54121Smckusick m.fn = fn; 79*54121Smckusick m.parent = parent; 80*54121Smckusick m.aux = aux; 81*54121Smckusick m.match = NULL; 82*54121Smckusick m.pri = 0; 8352592Smckusick for (cf = cfdata; cf->cf_driver; cf++) { 8452592Smckusick /* 8552592Smckusick * Skip cf if no longer eligible, or if a root entry. 86*54121Smckusick * Otherwise scan through parents for one matching `parent' 8752592Smckusick * (and alive), and try match function. 8852592Smckusick */ 8952592Smckusick if (cf->cf_fstate == FSTATE_FOUND || 9052592Smckusick (p = cf->cf_parents) == NULL) 9152592Smckusick continue; 9252592Smckusick while (*p >= 0) { 9352592Smckusick pcf = &cfdata[*p++]; 94*54121Smckusick if (parent->dv_cfdata == pcf) 95*54121Smckusick mapply(&m, cf); 9652592Smckusick } 9752592Smckusick } 98*54121Smckusick return (m.match); 9952592Smckusick } 10052592Smckusick 10152592Smckusick /* 10252592Smckusick * Find the given root device. 10352592Smckusick * This is much like config_search, but there is no parent. 10452592Smckusick */ 10552592Smckusick struct cfdata * 10652592Smckusick config_rootsearch(fn, rootname, aux) 10752592Smckusick register cfmatch_t fn; 10852592Smckusick register char *rootname; 10952592Smckusick register void *aux; 11052592Smckusick { 111*54121Smckusick register struct cfdata *cf; 112*54121Smckusick struct matchinfo m; 11352592Smckusick 114*54121Smckusick m.fn = fn; 115*54121Smckusick m.parent = ROOT; 116*54121Smckusick m.aux = aux; 117*54121Smckusick m.match = NULL; 118*54121Smckusick m.pri = 0; 119*54121Smckusick /* 120*54121Smckusick * Look at root entries for matching name. We do not bother 121*54121Smckusick * with found-state here since only one root should ever be searched. 122*54121Smckusick */ 123*54121Smckusick for (cf = cfdata; cf->cf_driver; cf++) 124*54121Smckusick if (cf->cf_parents == NULL && cf->cf_unit == 0 && 125*54121Smckusick strcmp(cf->cf_driver->cd_name, rootname) == 0) 126*54121Smckusick mapply(&m, cf); 127*54121Smckusick return (m.match); 12852592Smckusick } 12952592Smckusick 13052592Smckusick static char *msgs[3] = { "", " not configured\n", " unsupported\n" }; 13152592Smckusick 13252592Smckusick /* 13352592Smckusick * The given `aux' argument describes a device that has been found 13452592Smckusick * on the given parent, but not necessarily configured. Locate the 13552592Smckusick * configuration data for that device (using the cd_match configuration 13652592Smckusick * driver function) and attach it, and return true. If the device was 13752592Smckusick * not configured, call the given `print' function and return 0. 13852592Smckusick */ 13952592Smckusick int 14052592Smckusick config_found(parent, aux, print) 14152592Smckusick struct device *parent; 14252592Smckusick void *aux; 14352592Smckusick cfprint_t print; 14452592Smckusick { 14552592Smckusick struct cfdata *cf; 14652592Smckusick 14752592Smckusick if ((cf = config_search((cfmatch_t)NULL, parent, aux)) != NULL) { 14852592Smckusick config_attach(parent, cf, aux, print); 14952592Smckusick return (1); 15052592Smckusick } 15152592Smckusick printf(msgs[(*print)(aux, parent->dv_xname)]); 15252592Smckusick return (0); 15352592Smckusick } 15452592Smckusick 15552592Smckusick /* 15652592Smckusick * As above, but for root devices. 15752592Smckusick */ 15852592Smckusick int 15952592Smckusick config_rootfound(rootname, aux) 16052592Smckusick char *rootname; 16152592Smckusick void *aux; 16252592Smckusick { 16352592Smckusick struct cfdata *cf; 16452592Smckusick 16552592Smckusick if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) { 16652592Smckusick config_attach(ROOT, cf, aux, (cfprint_t)NULL); 16752592Smckusick return (1); 16852592Smckusick } 16952592Smckusick printf("root device %s not configured\n", rootname); 17052592Smckusick return (0); 17152592Smckusick } 17252592Smckusick 17352592Smckusick /* just like sprintf(buf, "%d") except that it works from the end */ 17452592Smckusick static char * 17552592Smckusick number(ep, n) 17652592Smckusick register char *ep; 17752592Smckusick register int n; 17852592Smckusick { 17952592Smckusick 18052592Smckusick *--ep = 0; 18152592Smckusick while (n >= 10) { 18252592Smckusick *--ep = (n % 10) + '0'; 18352592Smckusick n /= 10; 18452592Smckusick } 18552592Smckusick *--ep = n + '0'; 18652592Smckusick return (ep); 18752592Smckusick } 18852592Smckusick 18952592Smckusick /* 19052592Smckusick * Attach a found device. Allocates memory for device variables. 19152592Smckusick */ 19252592Smckusick void 19352592Smckusick config_attach(parent, cf, aux, print) 19452592Smckusick register struct device *parent; 19552592Smckusick register struct cfdata *cf; 19652592Smckusick register void *aux; 19752592Smckusick cfprint_t print; 19852592Smckusick { 19952592Smckusick register struct device *dev; 20052592Smckusick register struct cfdriver *cd; 20152592Smckusick register size_t lname, lunit; 20252592Smckusick register char *xunit; 203*54121Smckusick int myunit; 20452592Smckusick char num[10]; 205*54121Smckusick static struct device **nextp = &alldevs; 20652592Smckusick 20752592Smckusick cd = cf->cf_driver; 20852592Smckusick if (cd->cd_devsize < sizeof(struct device)) 20952592Smckusick panic("config_attach"); 210*54121Smckusick myunit = cf->cf_unit; 21152592Smckusick if (cf->cf_fstate == FSTATE_NOTFOUND) 21252592Smckusick cf->cf_fstate = FSTATE_FOUND; 213*54121Smckusick else 214*54121Smckusick cf->cf_unit++; 21552592Smckusick 21652592Smckusick /* compute length of name and decimal expansion of unit number */ 21752592Smckusick lname = strlen(cd->cd_name); 218*54121Smckusick xunit = number(&num[sizeof num], myunit); 21952592Smckusick lunit = &num[sizeof num] - xunit; 22052592Smckusick 22152592Smckusick /* get memory for all device vars, plus expanded name */ 22252592Smckusick dev = (struct device *)malloc(cd->cd_devsize + lname + lunit, 22352592Smckusick M_DEVBUF, M_WAITOK); /* XXX cannot wait! */ 22452592Smckusick bzero(dev, cd->cd_devsize); 225*54121Smckusick *nextp = dev; /* link up */ 226*54121Smckusick nextp = &dev->dv_next; 227*54121Smckusick dev->dv_class = cd->cd_class; 228*54121Smckusick dev->dv_cfdata = cf; 22952592Smckusick dev->dv_name = cd->cd_name; 230*54121Smckusick dev->dv_unit = myunit; 23152592Smckusick dev->dv_xname = (char *)dev + cd->cd_devsize; 23252592Smckusick bcopy(dev->dv_name, dev->dv_xname, lname); 23352592Smckusick bcopy(xunit, dev->dv_xname + lname, lunit); 23452592Smckusick dev->dv_parent = parent; 23552592Smckusick if (parent == ROOT) 23652592Smckusick printf("%s (root)", dev->dv_xname); 23752592Smckusick else { 23852592Smckusick printf("%s at %s", dev->dv_xname, parent->dv_xname); 23952592Smckusick (void) (*print)(aux, (char *)0); 24052592Smckusick } 24152592Smckusick 24252592Smckusick /* put this device in the devices array */ 24352592Smckusick if (dev->dv_unit >= cd->cd_ndevs) { 24452592Smckusick /* 24552592Smckusick * Need to expand the array. 24652592Smckusick */ 24752592Smckusick int old = cd->cd_ndevs, oldbytes, new, newbytes; 24852592Smckusick void **nsp; 24952592Smckusick 25052592Smckusick if (old == 0) { 25152592Smckusick nsp = malloc(MINALLOCSIZE, M_DEVBUF, M_WAITOK); /*XXX*/ 25252592Smckusick bzero(nsp, MINALLOCSIZE); 25352592Smckusick cd->cd_ndevs = MINALLOCSIZE / sizeof(void *); 25452592Smckusick } else { 25552592Smckusick new = cd->cd_ndevs; 25652592Smckusick do { 25752592Smckusick new *= 2; 25852592Smckusick } while (new <= dev->dv_unit); 25952592Smckusick cd->cd_ndevs = new; 26052592Smckusick oldbytes = old * sizeof(void *); 26152592Smckusick newbytes = new * sizeof(void *); 26252592Smckusick nsp = malloc(newbytes, M_DEVBUF, M_WAITOK); /*XXX*/ 26352592Smckusick bcopy(cd->cd_devs, nsp, oldbytes); 26452592Smckusick bzero(&nsp[old], newbytes - oldbytes); 26552592Smckusick free(cd->cd_devs, M_DEVBUF); 26652592Smckusick } 26752592Smckusick cd->cd_devs = nsp; 26852592Smckusick } 269*54121Smckusick if (cd->cd_devs[dev->dv_unit]) 270*54121Smckusick panic("config_attach: duplicate %s", dev->dv_xname); 27152592Smckusick cd->cd_devs[dev->dv_unit] = dev; 27252592Smckusick (*cd->cd_attach)(parent, dev, aux); 27352592Smckusick } 274