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 * 952592Smckusick * %sccs.include.redist.c% 1052592Smckusick * 11*56517Sbostic * @(#)subr_autoconf.c 7.4 (Berkeley) 10/11/92 1252592Smckusick * 1354121Smckusick * from: $Header: subr_autoconf.c,v 1.6 92/06/11 17:56:19 torek Exp $ (LBL) 1452592Smckusick */ 1552592Smckusick 16*56517Sbostic #include <sys/param.h> 17*56517Sbostic #include <sys/device.h> 18*56517Sbostic #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 2854121Smckusick struct matchinfo { 2954121Smckusick cfmatch_t fn; 3054121Smckusick struct device *parent; 3154121Smckusick void *aux; 3254121Smckusick struct cfdata *match; 3354121Smckusick int pri; 3454121Smckusick }; 3554121Smckusick 3652592Smckusick /* 3754121Smckusick * Apply the matching function and choose the best. This is used 3854121Smckusick * a few times and we want to keep the code small. 3954121Smckusick */ 4054121Smckusick static void 4154121Smckusick mapply(m, cf) 4254121Smckusick register struct matchinfo *m; 4354121Smckusick register struct cfdata *cf; 4454121Smckusick { 4554121Smckusick register int pri; 4654121Smckusick 4754121Smckusick if (m->fn != NULL) 4854121Smckusick pri = (*m->fn)(m->parent, cf, m->aux); 4954121Smckusick else 5054121Smckusick pri = (*cf->cf_driver->cd_match)(m->parent, cf, m->aux); 5154121Smckusick if (pri > m->pri) { 5254121Smckusick m->match = cf; 5354121Smckusick m->pri = pri; 5454121Smckusick } 5554121Smckusick } 5654121Smckusick 5754121Smckusick /* 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 * 6954121Smckusick config_search(fn, parent, aux) 7054121Smckusick cfmatch_t fn; 7154121Smckusick register struct device *parent; 7254121Smckusick void *aux; 7352592Smckusick { 7454121Smckusick register struct cfdata *cf, *pcf; 7552592Smckusick register short *p; 7654121Smckusick struct matchinfo m; 7752592Smckusick 7854121Smckusick m.fn = fn; 7954121Smckusick m.parent = parent; 8054121Smckusick m.aux = aux; 8154121Smckusick m.match = NULL; 8254121Smckusick m.pri = 0; 8352592Smckusick for (cf = cfdata; cf->cf_driver; cf++) { 8452592Smckusick /* 8552592Smckusick * Skip cf if no longer eligible, or if a root entry. 8654121Smckusick * 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++]; 9454121Smckusick if (parent->dv_cfdata == pcf) 9554121Smckusick mapply(&m, cf); 9652592Smckusick } 9752592Smckusick } 9854121Smckusick 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 { 11154121Smckusick register struct cfdata *cf; 11254121Smckusick struct matchinfo m; 11352592Smckusick 11454121Smckusick m.fn = fn; 11554121Smckusick m.parent = ROOT; 11654121Smckusick m.aux = aux; 11754121Smckusick m.match = NULL; 11854121Smckusick m.pri = 0; 11954121Smckusick /* 12054121Smckusick * Look at root entries for matching name. We do not bother 12154121Smckusick * with found-state here since only one root should ever be searched. 12254121Smckusick */ 12354121Smckusick for (cf = cfdata; cf->cf_driver; cf++) 12454121Smckusick if (cf->cf_parents == NULL && cf->cf_unit == 0 && 12554121Smckusick strcmp(cf->cf_driver->cd_name, rootname) == 0) 12654121Smckusick mapply(&m, cf); 12754121Smckusick 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; 20354121Smckusick int myunit; 20452592Smckusick char num[10]; 20554121Smckusick static struct device **nextp = &alldevs; 20652592Smckusick 20752592Smckusick cd = cf->cf_driver; 20852592Smckusick if (cd->cd_devsize < sizeof(struct device)) 20952592Smckusick panic("config_attach"); 21054121Smckusick myunit = cf->cf_unit; 21152592Smckusick if (cf->cf_fstate == FSTATE_NOTFOUND) 21252592Smckusick cf->cf_fstate = FSTATE_FOUND; 21354121Smckusick else 21454121Smckusick cf->cf_unit++; 21552592Smckusick 21652592Smckusick /* compute length of name and decimal expansion of unit number */ 21752592Smckusick lname = strlen(cd->cd_name); 21854121Smckusick 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); 22554121Smckusick *nextp = dev; /* link up */ 22654121Smckusick nextp = &dev->dv_next; 22754121Smckusick dev->dv_class = cd->cd_class; 22854121Smckusick dev->dv_cfdata = cf; 22952592Smckusick dev->dv_name = cd->cd_name; 23054121Smckusick 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 } 26954121Smckusick if (cd->cd_devs[dev->dv_unit]) 27054121Smckusick 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