4 * $FreeBSD: src/sys/boot/common/pnp.c,v 1.16 2003/08/25 23:30:41 obrien Exp $
5 * $DragonFly: src/sys/boot/common/pnp.c,v 1.4 2008/09/02 17:21:12 dillon Exp $
9 * "Plug and Play" functionality.
11 * We use the PnP enumerators to obtain identifiers for installed hardware,
12 * and the contents of a database to determine modules to be loaded to support
18 #include <bootstrap.h>
20 struct pnpinfo_stql pnp_devices
;
21 static int pnp_devices_initted
= 0;
23 static void pnp_discard(void);
26 * Perform complete enumeration sweep
29 COMMAND_SET(pnpscan
, "pnpscan", "scan for PnP devices", pnp_scan
);
32 pnp_scan(int argc
, char *argv
[])
39 if (pnp_devices_initted
== 0) {
40 STAILQ_INIT(&pnp_devices
);
41 pnp_devices_initted
= 1;
47 while ((ch
= getopt(argc
, argv
, "v")) != -1) {
54 /* getopt has already reported an error */
59 /* forget anything we think we knew */
62 /* iterate over all of the handlers */
63 for (hdlr
= 0; pnphandlers
[hdlr
] != NULL
; hdlr
++) {
65 printf("Probing %s...\n", pnphandlers
[hdlr
]->pp_name
);
66 pnphandlers
[hdlr
]->pp_enumerate();
70 pager_output("PNP scan summary:\n");
71 STAILQ_FOREACH(pi
, &pnp_devices
, pi_link
) {
72 pager_output(STAILQ_FIRST(&pi
->pi_ident
)->id_ident
); /* first ident should be canonical */
73 if (pi
->pi_desc
!= NULL
) {
75 pager_output(pi
->pi_desc
);
86 * Try to load outstanding modules (eg. after disk change)
88 COMMAND_SET(pnpload
, "pnpload", "load modules for PnP devices", pnp_load
);
91 pnp_load(int argc
, char *argv
[])
97 if (STAILQ_FIRST(&pnp_devices
) != NULL
) {
99 /* check for kernel, assign modules handled by static drivers there */
100 if (pnp_scankernel()) {
101 command_errmsg
= "cannot load drivers until kernel loaded";
106 pnp_readconf("/boot/pnpdata.local");
107 pnp_readconf("/boot/pnpdata");
109 if (pnp_readconf(fname
)) {
110 sprintf(command_errbuf
, "can't read PnP information from '%s'", fname
);
115 /* try to load any modules that have been nominated */
116 STAILQ_FOREACH(pi
, &pnp_devices
, pi_link
) {
117 /* Already loaded? */
118 if ((pi
->pi_module
!= NULL
) && (file_findfile(pi
->pi_module
, NULL
) == NULL
)) {
119 modfname
= malloc(strlen(pi
->pi_module
) + 4);
120 sprintf(modfname
, "%s.ko", pi
->pi_module
); /* XXX implicit knowledge of KLD module filenames */
121 if (mod_load(pi
->pi_module
, pi
->pi_argc
, pi
->pi_argv
))
122 printf("Could not load module '%s' for device '%s'\n", modfname
, STAILQ_FIRST(&pi
->pi_ident
)->id_ident
);
131 * Throw away anything we think we know about PnP devices.
138 while (STAILQ_FIRST(&pnp_devices
) != NULL
) {
139 pi
= STAILQ_FIRST(&pnp_devices
);
140 STAILQ_REMOVE_HEAD(&pnp_devices
, pi_link
);
146 * The PnP configuration database consists of a flat text file with
147 * entries one per line. Valid lines are:
151 * This line is a comment, and ignored.
155 * Entries following this line are for devices connected to the
156 * bus <name>, At least one such entry must be encountered
157 * before identifiers are recognised.
159 * ident=<identifier> rev=<revision> module=<module> args=<arguments>
161 * This line describes an identifier:module mapping. The 'ident'
162 * and 'module' fields are required; the 'rev' field is currently
163 * ignored (but should be used), and the 'args' field must come
166 * Comments may be appended to lines; any character including or following
167 * '#' on a line is ignored.
170 pnp_readconf(char *path
)
175 char lbuf
[128], *currbus
, *ident
, *revision
, *module
, *args
;
176 char *cp
, *ep
, *tp
, c
;
178 /* try to open the file */
179 if ((fd
= rel_open(path
, O_RDONLY
)) >= 0) {
183 while (fgetstr(lbuf
, sizeof(lbuf
), fd
) > 0) {
185 /* Find the first non-space character on the line */
186 for (cp
= lbuf
; (*cp
!= 0) && !isspace(*cp
); cp
++)
190 if ((*cp
== 0) || (*cp
== '#'))
193 /* cut trailing comment? */
194 if ((ep
= strchr(cp
, '#')) != NULL
)
197 /* bus declaration? */
199 if (((ep
= strchr(cp
, ']')) == NULL
) || ((ep
- cp
) < 2)) {
200 printf("%s line %d: bad bus specification\n", path
, line
);
205 currbus
= strdup(cp
+ 1);
210 /* XXX should we complain? */
215 for (ident
= module
= args
= revision
= NULL
; *cp
!= 0;) {
217 /* discard leading whitespace */
223 /* scan for terminator, separator */
224 for (ep
= cp
; (*ep
!= 0) && (*ep
!= '=') && !isspace(*ep
); ep
++)
229 for (tp
= ep
+ 1; (*tp
!= 0) && !isspace(*tp
); tp
++)
233 if ((ident
== NULL
) && !strcmp(cp
, "ident")) {
235 } else if ((revision
== NULL
) && !strcmp(cp
, "revision")) {
237 } else if ((args
== NULL
) && !strcmp(cp
, "args")) {
239 while (*tp
!= 0) /* skip to end of string */
249 /* it's garbage or a keyword - ignore it for now */
253 /* we must have at least ident and module set to be interesting */
254 if ((ident
== NULL
) || (module
== NULL
))
258 * Loop looking for module/bus that might match this, but aren't already
260 * XXX no revision parse/test here yet.
262 STAILQ_FOREACH(pi
, &pnp_devices
, pi_link
) {
264 /* no driver assigned, bus matches OK */
265 if ((pi
->pi_module
== NULL
) &&
266 !strcmp(pi
->pi_handler
->pp_name
, currbus
)) {
268 /* scan idents, take first match */
269 STAILQ_FOREACH(id
, &pi
->pi_ident
, id_link
)
270 if (!strcmp(id
->id_ident
, ident
))
276 if (parse(&pi
->pi_argc
, &pi
->pi_argv
, args
)) {
277 printf("%s line %d: bad arguments\n", path
, line
);
280 pi
->pi_module
= strdup(module
);
281 printf("use module '%s' for %s:%s\n", module
, pi
->pi_handler
->pp_name
, id
->id_ident
);
298 * Add a unique identifier to (pi)
301 pnp_addident(struct pnpinfo
*pi
, char *ident
)
305 STAILQ_FOREACH(id
, &pi
->pi_ident
, id_link
)
306 if (!strcmp(id
->id_ident
, ident
))
307 return; /* already have this one */
309 id
= malloc(sizeof(struct pnpident
));
310 id
->id_ident
= strdup(ident
);
311 STAILQ_INSERT_TAIL(&pi
->pi_ident
, id
, id_link
);
315 * Allocate a new pnpinfo struct
322 pi
= malloc(sizeof(struct pnpinfo
));
323 bzero(pi
, sizeof(struct pnpinfo
));
324 STAILQ_INIT(&pi
->pi_ident
);
329 * Release storage held by a pnpinfo struct
332 pnp_freeinfo(struct pnpinfo
*pi
)
336 while (!STAILQ_EMPTY(&pi
->pi_ident
)) {
337 id
= STAILQ_FIRST(&pi
->pi_ident
);
338 STAILQ_REMOVE_HEAD(&pi
->pi_ident
, id_link
);
352 * Add a new pnpinfo struct to the list.
355 pnp_addinfo(struct pnpinfo
*pi
)
357 STAILQ_INSERT_TAIL(&pnp_devices
, pi
, pi_link
);
362 * Format an EISA id as a string in standard ISA PnP format, AAAIIRR
363 * where 'AAA' is the EISA vendor ID, II is the product ID and RR the revision ID.
366 pnp_eisaformat(u_int8_t
*data
)
368 static char idbuf
[8];
369 const char hextoascii
[] = "0123456789abcdef";
371 idbuf
[0] = '@' + ((data
[0] & 0x7c) >> 2);
372 idbuf
[1] = '@' + (((data
[0] & 0x3) << 3) + ((data
[1] & 0xe0) >> 5));
373 idbuf
[2] = '@' + (data
[1] & 0x1f);
374 idbuf
[3] = hextoascii
[(data
[2] >> 4)];
375 idbuf
[4] = hextoascii
[(data
[2] & 0xf)];
376 idbuf
[5] = hextoascii
[(data
[3] >> 4)];
377 idbuf
[6] = hextoascii
[(data
[3] & 0xf)];