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