Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / boot / common / pnp.c
1 /*
2  * mjs copyright
3  *
4  * $FreeBSD: src/sys/boot/common/pnp.c,v 1.9.2.2 2000/12/28 13:12:36 ps Exp $
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
18 STAILQ_HEAD(,pnpinfo)   pnp_devices;
19 static int              pnp_devices_initted = 0;
20
21 static void             pnp_discard(void);
22
23 /*
24  * Perform complete enumeration sweep
25  */
26
27 COMMAND_SET(pnpscan, "pnpscan", "scan for PnP devices", pnp_scan);
28
29 static int
30 pnp_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  */
86 COMMAND_SET(pnpload, "pnpload", "load modules for PnP devices", pnp_load);
87
88 static int
89 pnp_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) && (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);
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  */
131 static void
132 pnp_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  */
167 static int
168 pnp_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
289 static int
290 pnp_scankernel(void)
291 {
292     return(CMD_OK);
293 }
294 #endif
295 /*
296  * Add a unique identifier to (pi)
297  */
298 void
299 pnp_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  */
315 struct pnpinfo *
316 pnp_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  */
329 void
330 pnp_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  */
352 void
353 pnp_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  */
363 char *
364 pnp_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