pnp.c revision 64187
1/* 2 * mjs copyright 3 * 4 * $FreeBSD: head/sys/boot/common/pnp.c 64187 2000-08-03 09:14:02Z jhb $ 5 */ 6/* 7 * "Plug and Play" functionality. 8 * 9 * We use the PnP enumerators to obtain identifiers for installed hardware, 10 * and the contents of a database to determine modules to be loaded to support 11 * such hardware. 12 */ 13 14#include <stand.h> 15#include <string.h> 16#include <bootstrap.h> 17 18STAILQ_HEAD(,pnpinfo) pnp_devices; 19static int pnp_devices_initted = 0; 20 21static void pnp_discard(void); 22 23/* 24 * Perform complete enumeration sweep 25 */ 26 27COMMAND_SET(pnpscan, "pnpscan", "scan for PnP devices", pnp_scan); 28 29static int 30pnp_scan(int argc, char *argv[]) 31{ 32 struct pnpinfo *pi; 33 int hdlr; 34 int verbose; 35 int ch; 36 37 if (pnp_devices_initted == 0) { 38 STAILQ_INIT(&pnp_devices); 39 pnp_devices_initted = 1; 40 } 41 42 verbose = 0; 43 optind = 1; 44 optreset = 1; 45 while ((ch = getopt(argc, argv, "v")) != -1) { 46 switch(ch) { 47 case 'v': 48 verbose = 1; 49 break; 50 case '?': 51 default: 52 /* getopt has already reported an error */ 53 return(CMD_OK); 54 } 55 } 56 57 /* forget anything we think we knew */ 58 pnp_discard(); 59 60 /* iterate over all of the handlers */ 61 for (hdlr = 0; pnphandlers[hdlr] != NULL; hdlr++) { 62 if (verbose) 63 printf("Probing %s...\n", pnphandlers[hdlr]->pp_name); 64 pnphandlers[hdlr]->pp_enumerate(); 65 } 66 if (verbose) { 67 pager_open(); 68 pager_output("PNP scan summary:\n"); 69 for (pi = pnp_devices.stqh_first; pi != NULL; pi = pi->pi_link.stqe_next) { 70 pager_output(pi->pi_ident.stqh_first->id_ident); /* first ident should be canonical */ 71 if (pi->pi_desc != NULL) { 72 pager_output(" : "); 73 pager_output(pi->pi_desc); 74 } 75 pager_output("\n"); 76 } 77 pager_close(); 78 } 79 return(CMD_OK); 80} 81 82#if 0 83/* 84 * Try to load outstanding modules (eg. after disk change) 85 */ 86COMMAND_SET(pnpload, "pnpload", "load modules for PnP devices", pnp_load); 87 88static int 89pnp_load(int argc, char *argv[]) 90{ 91 struct pnpinfo *pi; 92 char *modfname; 93 94 /* find anything? */ 95 if (pnp_devices.stqh_first != NULL) { 96 97 /* check for kernel, assign modules handled by static drivers there */ 98 if (pnp_scankernel()) { 99 command_errmsg = "cannot load drivers until kernel loaded"; 100 return(CMD_ERROR); 101 } 102 if (fname == NULL) { 103 /* default paths */ 104 pnp_readconf("/boot/pnpdata.local"); 105 pnp_readconf("/boot/pnpdata"); 106 } else { 107 if (pnp_readconf(fname)) { 108 sprintf(command_errbuf, "can't read PnP information from '%s'", fname); 109 return(CMD_ERROR); 110 } 111 } 112 113 /* try to load any modules that have been nominated */ 114 for (pi = pnp_devices.stqh_first; pi != NULL; pi = pi->pi_link.stqe_next) { 115 /* Already loaded? */ 116 if ((pi->pi_module != NULL) && (file_findfile(pi->pi_module, NULL) == NULL)) { 117 modfname = malloc(strlen(pi->pi_module) + 4); 118 sprintf(modfname, "%s.ko", pi->pi_module); /* XXX implicit knowledge of KLD module filenames */ 119 if (mod_load(pi->pi_module, pi->pi_argc, pi->pi_argv)) 120 printf("Could not load module '%s' for device '%s'\n", modfname, pi->pi_ident.stqh_first->id_ident); 121 free(modfname); 122 } 123 } 124 } 125 return(CMD_OK); 126} 127#endif 128/* 129 * Throw away anything we think we know about PnP devices. 130 */ 131static void 132pnp_discard(void) 133{ 134 struct pnpinfo *pi; 135 136 while (pnp_devices.stqh_first != NULL) { 137 pi = pnp_devices.stqh_first; 138 STAILQ_REMOVE_HEAD(&pnp_devices, pi_link); 139 pnp_freeinfo(pi); 140 } 141} 142#if 0 143/* 144 * The PnP configuration database consists of a flat text file with 145 * entries one per line. Valid lines are: 146 * 147 * # <text> 148 * 149 * This line is a comment, and ignored. 150 * 151 * [<name>] 152 * 153 * Entries following this line are for devices connected to the 154 * bus <name>, At least one such entry must be encountered 155 * before identifiers are recognised. 156 * 157 * ident=<identifier> rev=<revision> module=<module> args=<arguments> 158 * 159 * This line describes an identifier:module mapping. The 'ident' 160 * and 'module' fields are required; the 'rev' field is currently 161 * ignored (but should be used), and the 'args' field must come 162 * last. 163 * 164 * Comments may be appended to lines; any character including or following 165 * '#' on a line is ignored. 166 */ 167static int 168pnp_readconf(char *path) 169{ 170 struct pnpinfo *pi; 171 struct pnpident *id; 172 int fd, line; 173 char lbuf[128], *currbus, *ident, *revision, *module, *args; 174 char *cp, *ep, *tp, c; 175 176 /* try to open the file */ 177 if ((fd = open(path, O_RDONLY)) >= 0) { 178 line = 0; 179 currbus = NULL; 180 181 while (fgetstr(lbuf, sizeof(lbuf), fd) > 0) { 182 line++; 183 /* Find the first non-space character on the line */ 184 for (cp = lbuf; (*cp != 0) && !isspace(*cp); cp++) 185 ; 186 187 /* keep/discard? */ 188 if ((*cp == 0) || (*cp == '#')) 189 continue; 190 191 /* cut trailing comment? */ 192 if ((ep = strchr(cp, '#')) != NULL) 193 *ep = 0; 194 195 /* bus declaration? */ 196 if (*cp == '[') { 197 if (((ep = strchr(cp, ']')) == NULL) || ((ep - cp) < 2)) { 198 printf("%s line %d: bad bus specification\n", path, line); 199 } else { 200 if (currbus != NULL) 201 free(currbus); 202 *ep = 0; 203 currbus = strdup(cp + 1); 204 } 205 continue; 206 } 207 208 /* XXX should we complain? */ 209 if (currbus == NULL) 210 continue; 211 212 /* mapping */ 213 for (ident = module = args = revision = NULL; *cp != 0;) { 214 215 /* discard leading whitespace */ 216 if (isspace(*cp)) { 217 cp++; 218 continue; 219 } 220 221 /* scan for terminator, separator */ 222 for (ep = cp; (*ep != 0) && (*ep != '=') && !isspace(*ep); ep++) 223 ; 224 225 if (*ep == '=') { 226 *ep = 0; 227 for (tp = ep + 1; (*tp != 0) && !isspace(*tp); tp++) 228 ; 229 c = *tp; 230 *tp = 0; 231 if ((ident == NULL) && !strcmp(cp, "ident")) { 232 ident = ep + 1; 233 } else if ((revision == NULL) && !strcmp(cp, "revision")) { 234 revision = ep + 1; 235 } else if ((args == NULL) && !strcmp(cp, "args")) { 236 *tp = c; 237 while (*tp != 0) /* skip to end of string */ 238 tp++; 239 args = ep + 1; 240 } else { 241 /* XXX complain? */ 242 } 243 cp = tp; 244 continue; 245 } 246 247 /* it's garbage or a keyword - ignore it for now */ 248 cp = ep; 249 } 250 251 /* we must have at least ident and module set to be interesting */ 252 if ((ident == NULL) || (module == NULL)) 253 continue; 254 255 /* 256 * Loop looking for module/bus that might match this, but aren't already 257 * assigned. 258 * XXX no revision parse/test here yet. 259 */ 260 for (pi = pnp_devices.stqh_first; pi != NULL; pi = pi->pi_link.stqe_next) { 261 262 /* no driver assigned, bus matches OK */ 263 if ((pi->pi_module == NULL) && 264 !strcmp(pi->pi_handler->pp_name, currbus)) { 265 266 /* scan idents, take first match */ 267 for (id = pi->pi_ident.stqh_first; id != NULL; id = id->id_link.stqe_next) 268 if (!strcmp(id->id_ident, ident)) 269 break; 270 271 /* find a match? */ 272 if (id != NULL) { 273 if (args != NULL) 274 if (parse(&pi->pi_argc, &pi->pi_argv, args)) { 275 printf("%s line %d: bad arguments\n", path, line); 276 continue; 277 } 278 pi->pi_module = strdup(module); 279 printf("use module '%s' for %s:%s\n", module, pi->pi_handler->pp_name, id->id_ident); 280 } 281 } 282 } 283 } 284 close(fd); 285 } 286 return(CMD_OK); 287} 288 289static int 290pnp_scankernel(void) 291{ 292 return(CMD_OK); 293} 294#endif 295/* 296 * Add a unique identifier to (pi) 297 */ 298void 299pnp_addident(struct pnpinfo *pi, char *ident) 300{ 301 struct pnpident *id; 302 303 for (id = pi->pi_ident.stqh_first; id != NULL; id = id->id_link.stqe_next) 304 if (!strcmp(id->id_ident, ident)) 305 return; /* already have this one */ 306 307 id = malloc(sizeof(struct pnpident)); 308 id->id_ident = strdup(ident); 309 STAILQ_INSERT_TAIL(&pi->pi_ident, id, id_link); 310} 311 312/* 313 * Allocate a new pnpinfo struct 314 */ 315struct pnpinfo * 316pnp_allocinfo(void) 317{ 318 struct pnpinfo *pi; 319 320 pi = malloc(sizeof(struct pnpinfo)); 321 bzero(pi, sizeof(struct pnpinfo)); 322 STAILQ_INIT(&pi->pi_ident); 323 return(pi); 324} 325 326/* 327 * Release storage held by a pnpinfo struct 328 */ 329void 330pnp_freeinfo(struct pnpinfo *pi) 331{ 332 struct pnpident *id; 333 334 while (pi->pi_ident.stqh_first != NULL) { 335 id = pi->pi_ident.stqh_first; 336 STAILQ_REMOVE_HEAD(&pi->pi_ident, id_link); 337 free(id->id_ident); 338 free(id); 339 } 340 if (pi->pi_desc) 341 free(pi->pi_desc); 342 if (pi->pi_module) 343 free(pi->pi_module); 344 if (pi->pi_argv) 345 free(pi->pi_argv); 346 free(pi); 347} 348 349/* 350 * Add a new pnpinfo struct to the list. 351 */ 352void 353pnp_addinfo(struct pnpinfo *pi) 354{ 355 STAILQ_INSERT_TAIL(&pnp_devices, pi, pi_link); 356} 357 358 359/* 360 * Format an EISA id as a string in standard ISA PnP format, AAAIIRR 361 * where 'AAA' is the EISA vendor ID, II is the product ID and RR the revision ID. 362 */ 363char * 364pnp_eisaformat(u_int8_t *data) 365{ 366 static char idbuf[8]; 367 const char hextoascii[] = "0123456789abcdef"; 368 369 idbuf[0] = '@' + ((data[0] & 0x7c) >> 2); 370 idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5)); 371 idbuf[2] = '@' + (data[1] & 0x1f); 372 idbuf[3] = hextoascii[(data[2] >> 4)]; 373 idbuf[4] = hextoascii[(data[2] & 0xf)]; 374 idbuf[5] = hextoascii[(data[3] >> 4)]; 375 idbuf[6] = hextoascii[(data[3] & 0xf)]; 376 idbuf[7] = 0; 377 return(idbuf); 378} 379 380