1*52592Smckusick /* 2*52592Smckusick * Copyright (c) 1992 Regents of the University of California. 3*52592Smckusick * All rights reserved. 4*52592Smckusick * 5*52592Smckusick * This code is derived from software contributed to Berkeley by 6*52592Smckusick * the Computer Systems Engineering group at Lawrence Berkeley 7*52592Smckusick * Laboratory under DARPA contract BG 91-66. 8*52592Smckusick * 9*52592Smckusick * %sccs.include.redist.c% 10*52592Smckusick * 11*52592Smckusick * @(#)subr_autoconf.c 7.1 (Berkeley) 02/19/92 12*52592Smckusick * 13*52592Smckusick * from: $Header: subr_autoconf.c,v 1.3 91/11/23 00:53:49 torek Exp $ (LBL) 14*52592Smckusick */ 15*52592Smckusick 16*52592Smckusick #include "sys/param.h" 17*52592Smckusick #include "sys/device.h" 18*52592Smckusick #include "sys/malloc.h" 19*52592Smckusick 20*52592Smckusick /* 21*52592Smckusick * Autoconfiguration subroutines. 22*52592Smckusick */ 23*52592Smckusick 24*52592Smckusick extern struct cfdata cfdata[]; /* from ioconf.c */ 25*52592Smckusick 26*52592Smckusick #define ROOT ((struct device *)NULL) 27*52592Smckusick 28*52592Smckusick /* 29*52592Smckusick * Iterate over all potential children of some device, calling the given 30*52592Smckusick * function (default being the child's match function) for each one. 31*52592Smckusick * Nonzero returns are matches; the highest value returned is considered 32*52592Smckusick * the best match. Return the `found child' if we got a match, or NULL 33*52592Smckusick * otherwise. The `aux' pointer is simply passed on through. 34*52592Smckusick * 35*52592Smckusick * Note that this function is designed so that it can be used to apply 36*52592Smckusick * an arbitrary function to all potential children (its return value 37*52592Smckusick * can be ignored). 38*52592Smckusick */ 39*52592Smckusick struct cfdata * 40*52592Smckusick config_search(fn, dev, aux) 41*52592Smckusick register cfmatch_t fn; 42*52592Smckusick register struct device *dev; 43*52592Smckusick register void *aux; 44*52592Smckusick { 45*52592Smckusick register struct cfdata *cf, *pcf, *match = NULL; 46*52592Smckusick register short *p; 47*52592Smckusick register int pri, bestpri = 0; 48*52592Smckusick 49*52592Smckusick for (cf = cfdata; cf->cf_driver; cf++) { 50*52592Smckusick /* 51*52592Smckusick * Skip cf if no longer eligible, or if a root entry. 52*52592Smckusick * Otherwise scan through parents for one matching `dev' 53*52592Smckusick * (and alive), and try match function. 54*52592Smckusick */ 55*52592Smckusick if (cf->cf_fstate == FSTATE_FOUND || 56*52592Smckusick (p = cf->cf_parents) == NULL) 57*52592Smckusick continue; 58*52592Smckusick while (*p >= 0) { 59*52592Smckusick pcf = &cfdata[*p++]; 60*52592Smckusick if (pcf->cf_fstate != FSTATE_FOUND || 61*52592Smckusick pcf->cf_driver->cd_name != dev->dv_name || 62*52592Smckusick pcf->cf_unit != dev->dv_unit) 63*52592Smckusick continue; 64*52592Smckusick if (fn != NULL) 65*52592Smckusick pri = (*fn)(dev, cf, aux); 66*52592Smckusick else 67*52592Smckusick pri = (*cf->cf_driver->cd_match)(dev, cf, aux); 68*52592Smckusick if (pri > bestpri) { 69*52592Smckusick match = cf; 70*52592Smckusick bestpri = pri; 71*52592Smckusick } 72*52592Smckusick } 73*52592Smckusick } 74*52592Smckusick return (match); 75*52592Smckusick } 76*52592Smckusick 77*52592Smckusick /* 78*52592Smckusick * Find the given root device. 79*52592Smckusick * This is much like config_search, but there is no parent. 80*52592Smckusick */ 81*52592Smckusick struct cfdata * 82*52592Smckusick config_rootsearch(fn, rootname, aux) 83*52592Smckusick register cfmatch_t fn; 84*52592Smckusick register char *rootname; 85*52592Smckusick register void *aux; 86*52592Smckusick { 87*52592Smckusick register struct cfdata *cf, *match = NULL; 88*52592Smckusick register int pri, bestpri = 0; 89*52592Smckusick 90*52592Smckusick for (cf = cfdata; cf->cf_driver; cf++) { 91*52592Smckusick /* 92*52592Smckusick * Look at root entries for matching name. 93*52592Smckusick * We do not bother with found-state here 94*52592Smckusick * since only one root should ever be searched. 95*52592Smckusick */ 96*52592Smckusick if (cf->cf_parents != NULL || cf->cf_unit != 0 || 97*52592Smckusick strcmp(cf->cf_driver->cd_name, rootname) != 0) 98*52592Smckusick continue; 99*52592Smckusick if (fn != NULL) 100*52592Smckusick pri = (*fn)(ROOT, cf, aux); 101*52592Smckusick else 102*52592Smckusick pri = (*cf->cf_driver->cd_match)(ROOT, cf, aux); 103*52592Smckusick if (pri > bestpri) { 104*52592Smckusick match = cf; 105*52592Smckusick bestpri = pri; 106*52592Smckusick } 107*52592Smckusick } 108*52592Smckusick return (match); 109*52592Smckusick } 110*52592Smckusick 111*52592Smckusick static char *msgs[3] = { "", " not configured\n", " unsupported\n" }; 112*52592Smckusick 113*52592Smckusick /* 114*52592Smckusick * The given `aux' argument describes a device that has been found 115*52592Smckusick * on the given parent, but not necessarily configured. Locate the 116*52592Smckusick * configuration data for that device (using the cd_match configuration 117*52592Smckusick * driver function) and attach it, and return true. If the device was 118*52592Smckusick * not configured, call the given `print' function and return 0. 119*52592Smckusick */ 120*52592Smckusick int 121*52592Smckusick config_found(parent, aux, print) 122*52592Smckusick struct device *parent; 123*52592Smckusick void *aux; 124*52592Smckusick cfprint_t print; 125*52592Smckusick { 126*52592Smckusick struct cfdata *cf; 127*52592Smckusick 128*52592Smckusick if ((cf = config_search((cfmatch_t)NULL, parent, aux)) != NULL) { 129*52592Smckusick config_attach(parent, cf, aux, print); 130*52592Smckusick return (1); 131*52592Smckusick } 132*52592Smckusick printf(msgs[(*print)(aux, parent->dv_xname)]); 133*52592Smckusick return (0); 134*52592Smckusick } 135*52592Smckusick 136*52592Smckusick /* 137*52592Smckusick * As above, but for root devices. 138*52592Smckusick */ 139*52592Smckusick int 140*52592Smckusick config_rootfound(rootname, aux) 141*52592Smckusick char *rootname; 142*52592Smckusick void *aux; 143*52592Smckusick { 144*52592Smckusick struct cfdata *cf; 145*52592Smckusick 146*52592Smckusick if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) { 147*52592Smckusick config_attach(ROOT, cf, aux, (cfprint_t)NULL); 148*52592Smckusick return (1); 149*52592Smckusick } 150*52592Smckusick printf("root device %s not configured\n", rootname); 151*52592Smckusick return (0); 152*52592Smckusick } 153*52592Smckusick 154*52592Smckusick /* just like sprintf(buf, "%d") except that it works from the end */ 155*52592Smckusick static char * 156*52592Smckusick number(ep, n) 157*52592Smckusick register char *ep; 158*52592Smckusick register int n; 159*52592Smckusick { 160*52592Smckusick 161*52592Smckusick *--ep = 0; 162*52592Smckusick while (n >= 10) { 163*52592Smckusick *--ep = (n % 10) + '0'; 164*52592Smckusick n /= 10; 165*52592Smckusick } 166*52592Smckusick *--ep = n + '0'; 167*52592Smckusick return (ep); 168*52592Smckusick } 169*52592Smckusick 170*52592Smckusick /* 171*52592Smckusick * Attach a found device. Allocates memory for device variables. 172*52592Smckusick */ 173*52592Smckusick void 174*52592Smckusick config_attach(parent, cf, aux, print) 175*52592Smckusick register struct device *parent; 176*52592Smckusick register struct cfdata *cf; 177*52592Smckusick register void *aux; 178*52592Smckusick cfprint_t print; 179*52592Smckusick { 180*52592Smckusick register struct device *dev; 181*52592Smckusick register struct cfdriver *cd; 182*52592Smckusick register size_t lname, lunit; 183*52592Smckusick register char *xunit; 184*52592Smckusick char num[10]; 185*52592Smckusick 186*52592Smckusick cd = cf->cf_driver; 187*52592Smckusick if (cd->cd_devsize < sizeof(struct device)) 188*52592Smckusick panic("config_attach"); 189*52592Smckusick if (cf->cf_fstate == FSTATE_NOTFOUND) 190*52592Smckusick cf->cf_fstate = FSTATE_FOUND; 191*52592Smckusick 192*52592Smckusick /* compute length of name and decimal expansion of unit number */ 193*52592Smckusick lname = strlen(cd->cd_name); 194*52592Smckusick xunit = number(&num[sizeof num], cf->cf_unit); 195*52592Smckusick lunit = &num[sizeof num] - xunit; 196*52592Smckusick 197*52592Smckusick /* get memory for all device vars, plus expanded name */ 198*52592Smckusick dev = (struct device *)malloc(cd->cd_devsize + lname + lunit, 199*52592Smckusick M_DEVBUF, M_WAITOK); /* XXX cannot wait! */ 200*52592Smckusick bzero(dev, cd->cd_devsize); 201*52592Smckusick dev->dv_name = cd->cd_name; 202*52592Smckusick dev->dv_unit = cf->cf_unit; 203*52592Smckusick dev->dv_flags = cf->cf_flags; 204*52592Smckusick dev->dv_xname = (char *)dev + cd->cd_devsize; 205*52592Smckusick bcopy(dev->dv_name, dev->dv_xname, lname); 206*52592Smckusick bcopy(xunit, dev->dv_xname + lname, lunit); 207*52592Smckusick dev->dv_parent = parent; 208*52592Smckusick if (parent == ROOT) 209*52592Smckusick printf("%s (root)", dev->dv_xname); 210*52592Smckusick else { 211*52592Smckusick printf("%s at %s", dev->dv_xname, parent->dv_xname); 212*52592Smckusick (void) (*print)(aux, (char *)0); 213*52592Smckusick } 214*52592Smckusick 215*52592Smckusick /* put this device in the devices array */ 216*52592Smckusick if (dev->dv_unit >= cd->cd_ndevs) { 217*52592Smckusick /* 218*52592Smckusick * Need to expand the array. 219*52592Smckusick */ 220*52592Smckusick int old = cd->cd_ndevs, oldbytes, new, newbytes; 221*52592Smckusick void **nsp; 222*52592Smckusick 223*52592Smckusick if (old == 0) { 224*52592Smckusick nsp = malloc(MINALLOCSIZE, M_DEVBUF, M_WAITOK); /*XXX*/ 225*52592Smckusick bzero(nsp, MINALLOCSIZE); 226*52592Smckusick cd->cd_ndevs = MINALLOCSIZE / sizeof(void *); 227*52592Smckusick } else { 228*52592Smckusick new = cd->cd_ndevs; 229*52592Smckusick do { 230*52592Smckusick new *= 2; 231*52592Smckusick } while (new <= dev->dv_unit); 232*52592Smckusick cd->cd_ndevs = new; 233*52592Smckusick oldbytes = old * sizeof(void *); 234*52592Smckusick newbytes = new * sizeof(void *); 235*52592Smckusick nsp = malloc(newbytes, M_DEVBUF, M_WAITOK); /*XXX*/ 236*52592Smckusick bcopy(cd->cd_devs, nsp, oldbytes); 237*52592Smckusick bzero(&nsp[old], newbytes - oldbytes); 238*52592Smckusick free(cd->cd_devs, M_DEVBUF); 239*52592Smckusick } 240*52592Smckusick cd->cd_devs = nsp; 241*52592Smckusick } 242*52592Smckusick cd->cd_devs[dev->dv_unit] = dev; 243*52592Smckusick (*cd->cd_attach)(parent, dev, aux); 244*52592Smckusick } 245