4 * $FreeBSD: src/sys/boot/common/pnp.c,v 1.9.2.2 2000/12/28 13:12:36 ps Exp $
7 * "Plug and Play" functionality.
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
16 #include <bootstrap.h>
18 STAILQ_HEAD(,pnpinfo) pnp_devices;
19 static int pnp_devices_initted = 0;
21 static void pnp_discard(void);
24 * Perform complete enumeration sweep
27 COMMAND_SET(pnpscan, "pnpscan", "scan for PnP devices", pnp_scan);
30 pnp_scan(int argc, char *argv[])
37 if (pnp_devices_initted == 0) {
38 STAILQ_INIT(&pnp_devices);
39 pnp_devices_initted = 1;
45 while ((ch = getopt(argc, argv, "v")) != -1) {
52 /* getopt has already reported an error */
57 /* forget anything we think we knew */
60 /* iterate over all of the handlers */
61 for (hdlr = 0; pnphandlers[hdlr] != NULL; hdlr++) {
63 printf("Probing %s...\n", pnphandlers[hdlr]->pp_name);
64 pnphandlers[hdlr]->pp_enumerate();
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) {
73 pager_output(pi->pi_desc);
84 * Try to load outstanding modules (eg. after disk change)
86 COMMAND_SET(pnpload, "pnpload", "load modules for PnP devices", pnp_load);
89 pnp_load(int argc, char *argv[])
95 if (pnp_devices.stqh_first != NULL) {
97 /* check for kernel, assign modules handled by static drivers there */
98 if (pnp_scankernel()) {
99 command_errmsg = "cannot load drivers until kernel loaded";
104 pnp_readconf("/boot/pnpdata.local");
105 pnp_readconf("/boot/pnpdata");
107 if (pnp_readconf(fname)) {
108 sprintf(command_errbuf, "can't read PnP information from '%s'", fname);
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) && (mod_findmodule(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);
129 * Throw away anything we think we know about PnP devices.
136 while (pnp_devices.stqh_first != NULL) {
137 pi = pnp_devices.stqh_first;
138 STAILQ_REMOVE_HEAD(&pnp_devices, pi_link);
144 * The PnP configuration database consists of a flat text file with
145 * entries one per line. Valid lines are:
149 * This line is a comment, and ignored.
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.
157 * ident=<identifier> rev=<revision> module=<module> args=<arguments>
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
164 * Comments may be appended to lines; any character including or following
165 * '#' on a line is ignored.
168 pnp_readconf(char *path)
173 char lbuf[128], *currbus, *ident, *revision, *module, *args;
174 char *cp, *ep, *tp, c;
176 /* try to open the file */
177 if ((fd = open(path, O_RDONLY)) >= 0) {
181 while (fgetstr(lbuf, sizeof(lbuf), fd) > 0) {
183 /* Find the first non-space character on the line */
184 for (cp = lbuf; (*cp != 0) && !isspace(*cp); cp++)
188 if ((*cp == 0) || (*cp == '#'))
191 /* cut trailing comment? */
192 if ((ep = strchr(cp, '#')) != NULL)
195 /* bus declaration? */
197 if (((ep = strchr(cp, ']')) == NULL) || ((ep - cp) < 2)) {
198 printf("%s line %d: bad bus specification\n", path, line);
203 currbus = strdup(cp + 1);
208 /* XXX should we complain? */
213 for (ident = module = args = revision = NULL; *cp != 0;) {
215 /* discard leading whitespace */
221 /* scan for terminator, separator */
222 for (ep = cp; (*ep != 0) && (*ep != '=') && !isspace(*ep); ep++)
227 for (tp = ep + 1; (*tp != 0) && !isspace(*tp); tp++)
231 if ((ident == NULL) && !strcmp(cp, "ident")) {
233 } else if ((revision == NULL) && !strcmp(cp, "revision")) {
235 } else if ((args == NULL) && !strcmp(cp, "args")) {
237 while (*tp != 0) /* skip to end of string */
247 /* it's garbage or a keyword - ignore it for now */
251 /* we must have at least ident and module set to be interesting */
252 if ((ident == NULL) || (module == NULL))
256 * Loop looking for module/bus that might match this, but aren't already
258 * XXX no revision parse/test here yet.
260 for (pi = pnp_devices.stqh_first; pi != NULL; pi = pi->pi_link.stqe_next) {
262 /* no driver assigned, bus matches OK */
263 if ((pi->pi_module == NULL) &&
264 !strcmp(pi->pi_handler->pp_name, currbus)) {
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))
274 if (parse(&pi->pi_argc, &pi->pi_argv, args)) {
275 printf("%s line %d: bad arguments\n", path, line);
278 pi->pi_module = strdup(module);
279 printf("use module '%s' for %s:%s\n", module, pi->pi_handler->pp_name, id->id_ident);
296 * Add a unique identifier to (pi)
299 pnp_addident(struct pnpinfo *pi, char *ident)
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 */
307 id = malloc(sizeof(struct pnpident));
308 id->id_ident = strdup(ident);
309 STAILQ_INSERT_TAIL(&pi->pi_ident, id, id_link);
313 * Allocate a new pnpinfo struct
320 pi = malloc(sizeof(struct pnpinfo));
321 bzero(pi, sizeof(struct pnpinfo));
322 STAILQ_INIT(&pi->pi_ident);
327 * Release storage held by a pnpinfo struct
330 pnp_freeinfo(struct pnpinfo *pi)
334 while (pi->pi_ident.stqh_first != NULL) {
335 id = pi->pi_ident.stqh_first;
336 STAILQ_REMOVE_HEAD(&pi->pi_ident, id_link);
350 * Add a new pnpinfo struct to the list.
353 pnp_addinfo(struct pnpinfo *pi)
355 STAILQ_INSERT_TAIL(&pnp_devices, pi, pi_link);
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.
364 pnp_eisaformat(u_int8_t *data)
366 static char idbuf[8];
367 const char hextoascii[] = "0123456789abcdef";
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)];