diff options
Diffstat (limited to 'sys/boot/common/pnp.c')
-rw-r--r-- | sys/boot/common/pnp.c | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/sys/boot/common/pnp.c b/sys/boot/common/pnp.c new file mode 100644 index 0000000..05cce99 --- /dev/null +++ b/sys/boot/common/pnp.c @@ -0,0 +1,382 @@ +/* + * mjs copyright + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * "Plug and Play" functionality. + * + * We use the PnP enumerators to obtain identifiers for installed hardware, + * and the contents of a database to determine modules to be loaded to support + * such hardware. + */ + +#include <stand.h> +#include <string.h> +#include <bootstrap.h> + +struct pnpinfo_stql pnp_devices; +static int pnp_devices_initted = 0; + +static void pnp_discard(void); + +/* + * Perform complete enumeration sweep + */ + +COMMAND_SET(pnpscan, "pnpscan", "scan for PnP devices", pnp_scan); + +static int +pnp_scan(int argc, char *argv[]) +{ + struct pnpinfo *pi; + int hdlr; + int verbose; + int ch; + + if (pnp_devices_initted == 0) { + STAILQ_INIT(&pnp_devices); + pnp_devices_initted = 1; + } + + verbose = 0; + optind = 1; + optreset = 1; + while ((ch = getopt(argc, argv, "v")) != -1) { + switch(ch) { + case 'v': + verbose = 1; + break; + case '?': + default: + /* getopt has already reported an error */ + return(CMD_OK); + } + } + + /* forget anything we think we knew */ + pnp_discard(); + + /* iterate over all of the handlers */ + for (hdlr = 0; pnphandlers[hdlr] != NULL; hdlr++) { + if (verbose) + printf("Probing %s...\n", pnphandlers[hdlr]->pp_name); + pnphandlers[hdlr]->pp_enumerate(); + } + if (verbose) { + pager_open(); + pager_output("PNP scan summary:\n"); + STAILQ_FOREACH(pi, &pnp_devices, pi_link) { + pager_output(STAILQ_FIRST(&pi->pi_ident)->id_ident); /* first ident should be canonical */ + if (pi->pi_desc != NULL) { + pager_output(" : "); + pager_output(pi->pi_desc); + } + pager_output("\n"); + } + pager_close(); + } + return(CMD_OK); +} + +#if 0 +/* + * Try to load outstanding modules (eg. after disk change) + */ +COMMAND_SET(pnpload, "pnpload", "load modules for PnP devices", pnp_load); + +static int +pnp_load(int argc, char *argv[]) +{ + struct pnpinfo *pi; + char *modfname; + + /* find anything? */ + if (STAILQ_FIRST(&pnp_devices) != NULL) { + + /* check for kernel, assign modules handled by static drivers there */ + if (pnp_scankernel()) { + command_errmsg = "cannot load drivers until kernel loaded"; + return(CMD_ERROR); + } + if (fname == NULL) { + /* default paths */ + pnp_readconf("/boot/pnpdata.local"); + pnp_readconf("/boot/pnpdata"); + } else { + if (pnp_readconf(fname)) { + sprintf(command_errbuf, "can't read PnP information from '%s'", fname); + return(CMD_ERROR); + } + } + + /* try to load any modules that have been nominated */ + STAILQ_FOREACH(pi, &pnp_devices, pi_link) { + /* Already loaded? */ + if ((pi->pi_module != NULL) && (file_findfile(pi->pi_module, NULL) == NULL)) { + modfname = malloc(strlen(pi->pi_module) + 4); + sprintf(modfname, "%s.ko", pi->pi_module); /* XXX implicit knowledge of KLD module filenames */ + if (mod_load(pi->pi_module, pi->pi_argc, pi->pi_argv)) + printf("Could not load module '%s' for device '%s'\n", modfname, STAILQ_FIRST(&pi->pi_ident)->id_ident); + free(modfname); + } + } + } + return(CMD_OK); +} +#endif +/* + * Throw away anything we think we know about PnP devices. + */ +static void +pnp_discard(void) +{ + struct pnpinfo *pi; + + while (STAILQ_FIRST(&pnp_devices) != NULL) { + pi = STAILQ_FIRST(&pnp_devices); + STAILQ_REMOVE_HEAD(&pnp_devices, pi_link); + pnp_freeinfo(pi); + } +} +#if 0 +/* + * The PnP configuration database consists of a flat text file with + * entries one per line. Valid lines are: + * + * # <text> + * + * This line is a comment, and ignored. + * + * [<name>] + * + * Entries following this line are for devices connected to the + * bus <name>, At least one such entry must be encountered + * before identifiers are recognised. + * + * ident=<identifier> rev=<revision> module=<module> args=<arguments> + * + * This line describes an identifier:module mapping. The 'ident' + * and 'module' fields are required; the 'rev' field is currently + * ignored (but should be used), and the 'args' field must come + * last. + * + * Comments may be appended to lines; any character including or following + * '#' on a line is ignored. + */ +static int +pnp_readconf(char *path) +{ + struct pnpinfo *pi; + struct pnpident *id; + int fd, line; + char lbuf[128], *currbus, *ident, *revision, *module, *args; + char *cp, *ep, *tp, c; + + /* try to open the file */ + if ((fd = open(path, O_RDONLY)) >= 0) { + line = 0; + currbus = NULL; + + while (fgetstr(lbuf, sizeof(lbuf), fd) > 0) { + line++; + /* Find the first non-space character on the line */ + for (cp = lbuf; (*cp != 0) && !isspace(*cp); cp++) + ; + + /* keep/discard? */ + if ((*cp == 0) || (*cp == '#')) + continue; + + /* cut trailing comment? */ + if ((ep = strchr(cp, '#')) != NULL) + *ep = 0; + + /* bus declaration? */ + if (*cp == '[') { + if (((ep = strchr(cp, ']')) == NULL) || ((ep - cp) < 2)) { + printf("%s line %d: bad bus specification\n", path, line); + } else { + if (currbus != NULL) + free(currbus); + *ep = 0; + currbus = strdup(cp + 1); + } + continue; + } + + /* XXX should we complain? */ + if (currbus == NULL) + continue; + + /* mapping */ + for (ident = module = args = revision = NULL; *cp != 0;) { + + /* discard leading whitespace */ + if (isspace(*cp)) { + cp++; + continue; + } + + /* scan for terminator, separator */ + for (ep = cp; (*ep != 0) && (*ep != '=') && !isspace(*ep); ep++) + ; + + if (*ep == '=') { + *ep = 0; + for (tp = ep + 1; (*tp != 0) && !isspace(*tp); tp++) + ; + c = *tp; + *tp = 0; + if ((ident == NULL) && !strcmp(cp, "ident")) { + ident = ep + 1; + } else if ((revision == NULL) && !strcmp(cp, "revision")) { + revision = ep + 1; + } else if ((args == NULL) && !strcmp(cp, "args")) { + *tp = c; + while (*tp != 0) /* skip to end of string */ + tp++; + args = ep + 1; + } else { + /* XXX complain? */ + } + cp = tp; + continue; + } + + /* it's garbage or a keyword - ignore it for now */ + cp = ep; + } + + /* we must have at least ident and module set to be interesting */ + if ((ident == NULL) || (module == NULL)) + continue; + + /* + * Loop looking for module/bus that might match this, but aren't already + * assigned. + * XXX no revision parse/test here yet. + */ + STAILQ_FOREACH(pi, &pnp_devices, pi_link) { + + /* no driver assigned, bus matches OK */ + if ((pi->pi_module == NULL) && + !strcmp(pi->pi_handler->pp_name, currbus)) { + + /* scan idents, take first match */ + STAILQ_FOREACH(id, &pi->pi_ident, id_link) + if (!strcmp(id->id_ident, ident)) + break; + + /* find a match? */ + if (id != NULL) { + if (args != NULL) + if (parse(&pi->pi_argc, &pi->pi_argv, args)) { + printf("%s line %d: bad arguments\n", path, line); + continue; + } + pi->pi_module = strdup(module); + printf("use module '%s' for %s:%s\n", module, pi->pi_handler->pp_name, id->id_ident); + } + } + } + } + close(fd); + } + return(CMD_OK); +} + +static int +pnp_scankernel(void) +{ + return(CMD_OK); +} +#endif +/* + * Add a unique identifier to (pi) + */ +void +pnp_addident(struct pnpinfo *pi, char *ident) +{ + struct pnpident *id; + + STAILQ_FOREACH(id, &pi->pi_ident, id_link) + if (!strcmp(id->id_ident, ident)) + return; /* already have this one */ + + id = malloc(sizeof(struct pnpident)); + id->id_ident = strdup(ident); + STAILQ_INSERT_TAIL(&pi->pi_ident, id, id_link); +} + +/* + * Allocate a new pnpinfo struct + */ +struct pnpinfo * +pnp_allocinfo(void) +{ + struct pnpinfo *pi; + + pi = malloc(sizeof(struct pnpinfo)); + bzero(pi, sizeof(struct pnpinfo)); + STAILQ_INIT(&pi->pi_ident); + return(pi); +} + +/* + * Release storage held by a pnpinfo struct + */ +void +pnp_freeinfo(struct pnpinfo *pi) +{ + struct pnpident *id; + + while (!STAILQ_EMPTY(&pi->pi_ident)) { + id = STAILQ_FIRST(&pi->pi_ident); + STAILQ_REMOVE_HEAD(&pi->pi_ident, id_link); + free(id->id_ident); + free(id); + } + if (pi->pi_desc) + free(pi->pi_desc); + if (pi->pi_module) + free(pi->pi_module); + if (pi->pi_argv) + free(pi->pi_argv); + free(pi); +} + +/* + * Add a new pnpinfo struct to the list. + */ +void +pnp_addinfo(struct pnpinfo *pi) +{ + STAILQ_INSERT_TAIL(&pnp_devices, pi, pi_link); +} + + +/* + * Format an EISA id as a string in standard ISA PnP format, AAAIIRR + * where 'AAA' is the EISA vendor ID, II is the product ID and RR the revision ID. + */ +char * +pnp_eisaformat(u_int8_t *data) +{ + static char idbuf[8]; + const char hextoascii[] = "0123456789abcdef"; + + idbuf[0] = '@' + ((data[0] & 0x7c) >> 2); + idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5)); + idbuf[2] = '@' + (data[1] & 0x1f); + idbuf[3] = hextoascii[(data[2] >> 4)]; + idbuf[4] = hextoascii[(data[2] & 0xf)]; + idbuf[5] = hextoascii[(data[3] >> 4)]; + idbuf[6] = hextoascii[(data[3] & 0xf)]; + idbuf[7] = 0; + return(idbuf); +} + |