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