1 /* 2 * Copyright (c) 1992, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This software was developed by the Computer Systems Engineering group 6 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 7 * contributed to Berkeley. 8 * 9 * All advertising materials mentioning features or use of this software 10 * must display the following acknowledgement: 11 * This product includes software developed by the University of 12 * California, Lawrence Berkeley Laboratories. 13 * 14 * %sccs.include.redist.c% 15 * 16 * @(#)subr_autoconf.c 8.1 (Berkeley) 06/10/93 17 * 18 * from: $Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp $ (LBL) 19 */ 20 21 #include <sys/param.h> 22 #include <sys/device.h> 23 #include <sys/malloc.h> 24 25 /* 26 * Autoconfiguration subroutines. 27 */ 28 29 /* 30 * ioconf.c exports exactly two names: cfdata and cfroots. All system 31 * devices and drivers are found via these tables. 32 */ 33 extern struct cfdata cfdata[]; 34 extern short cfroots[]; 35 36 #define ROOT ((struct device *)NULL) 37 38 struct matchinfo { 39 cfmatch_t fn; 40 struct device *parent; 41 void *aux; 42 struct cfdata *match; 43 int pri; 44 }; 45 46 /* 47 * Apply the matching function and choose the best. This is used 48 * a few times and we want to keep the code small. 49 */ 50 static void 51 mapply(m, cf) 52 register struct matchinfo *m; 53 register struct cfdata *cf; 54 { 55 register int pri; 56 57 if (m->fn != NULL) 58 pri = (*m->fn)(m->parent, cf, m->aux); 59 else 60 pri = (*cf->cf_driver->cd_match)(m->parent, cf, m->aux); 61 if (pri > m->pri) { 62 m->match = cf; 63 m->pri = pri; 64 } 65 } 66 67 /* 68 * Iterate over all potential children of some device, calling the given 69 * function (default being the child's match function) for each one. 70 * Nonzero returns are matches; the highest value returned is considered 71 * the best match. Return the `found child' if we got a match, or NULL 72 * otherwise. The `aux' pointer is simply passed on through. 73 * 74 * Note that this function is designed so that it can be used to apply 75 * an arbitrary function to all potential children (its return value 76 * can be ignored). 77 */ 78 struct cfdata * 79 config_search(fn, parent, aux) 80 cfmatch_t fn; 81 register struct device *parent; 82 void *aux; 83 { 84 register struct cfdata *cf; 85 register short *p; 86 struct matchinfo m; 87 88 m.fn = fn; 89 m.parent = parent; 90 m.aux = aux; 91 m.match = NULL; 92 m.pri = 0; 93 for (cf = cfdata; cf->cf_driver; cf++) { 94 /* 95 * Skip cf if no longer eligible, otherwise scan through 96 * parents for one matching `parent', and try match function. 97 */ 98 if (cf->cf_fstate == FSTATE_FOUND) 99 continue; 100 for (p = cf->cf_parents; *p >= 0; p++) 101 if (parent->dv_cfdata == &cfdata[*p]) 102 mapply(&m, cf); 103 } 104 return (m.match); 105 } 106 107 /* 108 * Find the given root device. 109 * This is much like config_search, but there is no parent. 110 */ 111 struct cfdata * 112 config_rootsearch(fn, rootname, aux) 113 register cfmatch_t fn; 114 register char *rootname; 115 register void *aux; 116 { 117 register struct cfdata *cf; 118 register short *p; 119 struct matchinfo m; 120 121 m.fn = fn; 122 m.parent = ROOT; 123 m.aux = aux; 124 m.match = NULL; 125 m.pri = 0; 126 /* 127 * Look at root entries for matching name. We do not bother 128 * with found-state here since only one root should ever be 129 * searched (and it must be done first). 130 */ 131 for (p = cfroots; *p >= 0; p++) { 132 cf = &cfdata[*p]; 133 if (strcmp(cf->cf_driver->cd_name, rootname) == 0) 134 mapply(&m, cf); 135 } 136 return (m.match); 137 } 138 139 static char *msgs[3] = { "", " not configured\n", " unsupported\n" }; 140 141 /* 142 * The given `aux' argument describes a device that has been found 143 * on the given parent, but not necessarily configured. Locate the 144 * configuration data for that device (using the cd_match configuration 145 * driver function) and attach it, and return true. If the device was 146 * not configured, call the given `print' function and return 0. 147 */ 148 int 149 config_found(parent, aux, print) 150 struct device *parent; 151 void *aux; 152 cfprint_t print; 153 { 154 struct cfdata *cf; 155 156 if ((cf = config_search((cfmatch_t)NULL, parent, aux)) != NULL) { 157 config_attach(parent, cf, aux, print); 158 return (1); 159 } 160 printf(msgs[(*print)(aux, parent->dv_xname)]); 161 return (0); 162 } 163 164 /* 165 * As above, but for root devices. 166 */ 167 int 168 config_rootfound(rootname, aux) 169 char *rootname; 170 void *aux; 171 { 172 struct cfdata *cf; 173 174 if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) { 175 config_attach(ROOT, cf, aux, (cfprint_t)NULL); 176 return (1); 177 } 178 printf("root device %s not configured\n", rootname); 179 return (0); 180 } 181 182 /* just like sprintf(buf, "%d") except that it works from the end */ 183 static char * 184 number(ep, n) 185 register char *ep; 186 register int n; 187 { 188 189 *--ep = 0; 190 while (n >= 10) { 191 *--ep = (n % 10) + '0'; 192 n /= 10; 193 } 194 *--ep = n + '0'; 195 return (ep); 196 } 197 198 /* 199 * Attach a found device. Allocates memory for device variables. 200 */ 201 void 202 config_attach(parent, cf, aux, print) 203 register struct device *parent; 204 register struct cfdata *cf; 205 register void *aux; 206 cfprint_t print; 207 { 208 register struct device *dev; 209 register struct cfdriver *cd; 210 register size_t lname, lunit; 211 register char *xunit; 212 int myunit; 213 char num[10]; 214 static struct device **nextp = &alldevs; 215 216 cd = cf->cf_driver; 217 if (cd->cd_devsize < sizeof(struct device)) 218 panic("config_attach"); 219 myunit = cf->cf_unit; 220 if (cf->cf_fstate == FSTATE_NOTFOUND) 221 cf->cf_fstate = FSTATE_FOUND; 222 else 223 cf->cf_unit++; 224 225 /* compute length of name and decimal expansion of unit number */ 226 lname = strlen(cd->cd_name); 227 xunit = number(&num[sizeof num], myunit); 228 lunit = &num[sizeof num] - xunit; 229 if (lname + lunit >= sizeof(dev->dv_xname)) 230 panic("config_attach: device name too long"); 231 232 /* get memory for all device vars */ 233 dev = (struct device *)malloc(cd->cd_devsize, M_DEVBUF, M_WAITOK); 234 /* XXX cannot wait! */ 235 bzero(dev, cd->cd_devsize); 236 *nextp = dev; /* link up */ 237 nextp = &dev->dv_next; 238 dev->dv_class = cd->cd_class; 239 dev->dv_cfdata = cf; 240 dev->dv_unit = myunit; 241 bcopy(cd->cd_name, dev->dv_xname, lname); 242 bcopy(xunit, dev->dv_xname + lname, lunit); 243 dev->dv_parent = parent; 244 if (parent == ROOT) 245 printf("%s (root)", dev->dv_xname); 246 else { 247 printf("%s at %s", dev->dv_xname, parent->dv_xname); 248 (void) (*print)(aux, (char *)0); 249 } 250 251 /* put this device in the devices array */ 252 if (dev->dv_unit >= cd->cd_ndevs) { 253 /* 254 * Need to expand the array. 255 */ 256 int old = cd->cd_ndevs, oldbytes, new, newbytes; 257 void **nsp; 258 259 if (old == 0) { 260 nsp = malloc(MINALLOCSIZE, M_DEVBUF, M_WAITOK); /*XXX*/ 261 bzero(nsp, MINALLOCSIZE); 262 cd->cd_ndevs = MINALLOCSIZE / sizeof(void *); 263 } else { 264 new = cd->cd_ndevs; 265 do { 266 new *= 2; 267 } while (new <= dev->dv_unit); 268 cd->cd_ndevs = new; 269 oldbytes = old * sizeof(void *); 270 newbytes = new * sizeof(void *); 271 nsp = malloc(newbytes, M_DEVBUF, M_WAITOK); /*XXX*/ 272 bcopy(cd->cd_devs, nsp, oldbytes); 273 bzero(&nsp[old], newbytes - oldbytes); 274 free(cd->cd_devs, M_DEVBUF); 275 } 276 cd->cd_devs = nsp; 277 } 278 if (cd->cd_devs[dev->dv_unit]) 279 panic("config_attach: duplicate %s", dev->dv_xname); 280 cd->cd_devs[dev->dv_unit] = dev; 281 282 /* 283 * Before attaching, clobber any unfound devices that are 284 * otherwise identical. 285 */ 286 for (cf = cfdata; cf->cf_driver; cf++) 287 if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit && 288 cf->cf_fstate == FSTATE_NOTFOUND) 289 cf->cf_fstate = FSTATE_FOUND; 290 (*cd->cd_attach)(parent, dev, aux); 291 } 292 293 /* 294 * Attach an event. These must come from initially-zero space (see 295 * commented-out assignments below), but that occurs naturally for 296 * device instance variables. 297 */ 298 void 299 evcnt_attach(dev, name, ev) 300 struct device *dev; 301 const char *name; 302 struct evcnt *ev; 303 { 304 static struct evcnt **nextp = &allevents; 305 306 #ifdef DIAGNOSTIC 307 if (strlen(name) >= sizeof(ev->ev_name)) 308 panic("evcnt_attach"); 309 #endif 310 /* ev->ev_next = NULL; */ 311 ev->ev_dev = dev; 312 /* ev->ev_count = 0; */ 313 strcpy(ev->ev_name, name); 314 *nextp = ev; 315 nextp = &ev->ev_next; 316 } 317