]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/isa/pnpparse.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / isa / pnpparse.c
1 /*-
2  * Copyright (c) 1999 Doug Rabson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/malloc.h>
33 #include <sys/module.h>
34 #include <sys/bus.h>
35
36 #include <machine/stdarg.h>
37
38 #include <isa/isavar.h>
39 #include <isa/pnpreg.h>
40 #include <isa/pnpvar.h>
41
42 #define MAXDEP  8
43
44 #define I16(p)  ((p)[0] + ((p)[1] << 8))
45 #define I32(p)  (I16(p) + (I16((p)+2) << 16))
46
47 void
48 pnp_printf(u_int32_t id, char *fmt, ...)
49 {
50         va_list ap;
51
52         va_start(ap, fmt);
53         printf("%s: ", pnp_eisaformat(id));
54         vprintf(fmt, ap);
55         va_end(ap);
56 }
57
58 /* parse a single descriptor */
59
60 static int
61 pnp_parse_desc(device_t dev, u_char tag, u_char *res, int len,
62                struct isa_config *config, int ldn)
63 {
64         char buf[100];
65         u_int32_t id;
66         u_int32_t compat_id;
67         int temp;
68
69         id = isa_get_logicalid(dev);
70
71         if (PNP_RES_TYPE(tag) == 0) {
72
73                 /* Small resource */
74                 switch (PNP_SRES_NUM(tag)) {
75
76                 case PNP_TAG_VERSION:
77                 case PNP_TAG_VENDOR:
78                         /* these descriptors are quietly ignored */
79                         break;
80
81                 case PNP_TAG_LOGICAL_DEVICE:
82                 case PNP_TAG_START_DEPENDANT:
83                 case PNP_TAG_END_DEPENDANT:
84                         if (bootverbose)
85                                 pnp_printf(id, "unexpected small tag %d\n",
86                                            PNP_SRES_NUM(tag));
87                         /* shouldn't happen; quit now */
88                         return (1);
89
90                 case PNP_TAG_COMPAT_DEVICE:
91                         /*
92                          * Got a compatible device id resource.
93                          * Should keep a list of compat ids in the device.
94                          */
95                         bcopy(res, &compat_id, 4);
96                         if (isa_get_compatid(dev) == 0)
97                                 isa_set_compatid(dev, compat_id);
98                         break;
99             
100                 case PNP_TAG_IRQ_FORMAT:
101                         if (config->ic_nirq == ISA_NIRQ) {
102                                 pnp_printf(id, "too many irqs\n");
103                                 return (1);
104                         }
105                         if (I16(res) == 0) {
106                                 /* a null descriptor */
107                                 config->ic_irqmask[config->ic_nirq] = 0;
108                                 config->ic_nirq++;
109                                 break;
110                         }
111                         if (bootverbose)
112                                 pnp_printf(id, "adding irq mask %#02x\n",
113                                            I16(res));
114                         config->ic_irqmask[config->ic_nirq] = I16(res);
115                         config->ic_nirq++;
116                         break;
117
118                 case PNP_TAG_DMA_FORMAT:
119                         if (config->ic_ndrq == ISA_NDRQ) {
120                                 pnp_printf(id, "too many drqs\n");
121                                 return (1);
122                         }
123                         if (res[0] == 0) {
124                                 /* a null descriptor */
125                                 config->ic_drqmask[config->ic_ndrq] = 0;
126                                 config->ic_ndrq++;
127                                 break;
128                         }
129                         if (bootverbose)
130                                 pnp_printf(id, "adding dma mask %#02x\n",
131                                            res[0]);
132                         config->ic_drqmask[config->ic_ndrq] = res[0];
133                         config->ic_ndrq++;
134                         break;
135
136                 case PNP_TAG_IO_RANGE:
137                         if (config->ic_nport == ISA_NPORT) {
138                                 pnp_printf(id, "too many ports\n");
139                                 return (1);
140                         }
141                         if (res[6] == 0) {
142                                 /* a null descriptor */
143                                 config->ic_port[config->ic_nport].ir_start = 0;
144                                 config->ic_port[config->ic_nport].ir_end = 0;
145                                 config->ic_port[config->ic_nport].ir_size = 0;
146                                 config->ic_port[config->ic_nport].ir_align = 0;
147                                 config->ic_nport++;
148                                 break;
149                         }
150                         if (bootverbose) {
151                                 pnp_printf(id, "adding io range "
152                                            "%#x-%#x, size=%#x, "
153                                            "align=%#x\n",
154                                            I16(res + 1),
155                                            I16(res + 3) + res[6]-1,
156                                            res[6], res[5]);
157                         }
158                         config->ic_port[config->ic_nport].ir_start =
159                             I16(res + 1);
160                         config->ic_port[config->ic_nport].ir_end =
161                             I16(res + 3) + res[6] - 1;
162                         config->ic_port[config->ic_nport].ir_size = res[6];
163                         if (res[5] == 0) {
164                             /* Make sure align is at least one */
165                             res[5] = 1;
166                         }
167                         config->ic_port[config->ic_nport].ir_align = res[5];
168                         config->ic_nport++;
169                         pnp_check_quirks(isa_get_vendorid(dev),
170                                          isa_get_logicalid(dev), ldn, config);
171                         break;
172
173                 case PNP_TAG_IO_FIXED:
174                         if (config->ic_nport == ISA_NPORT) {
175                                 pnp_printf(id, "too many ports\n");
176                                 return (1);
177                         }
178                         if (res[2] == 0) {
179                                 /* a null descriptor */
180                                 config->ic_port[config->ic_nport].ir_start = 0;
181                                 config->ic_port[config->ic_nport].ir_end = 0;
182                                 config->ic_port[config->ic_nport].ir_size = 0;
183                                 config->ic_port[config->ic_nport].ir_align = 0;
184                                 config->ic_nport++;
185                                 break;
186                         }
187                         if (bootverbose) {
188                                 pnp_printf(id, "adding fixed io range "
189                                            "%#x-%#x, size=%#x, "
190                                            "align=%#x\n",
191                                            I16(res),
192                                            I16(res) + res[2] - 1,
193                                            res[2], 1);
194                         }
195                         config->ic_port[config->ic_nport].ir_start = I16(res);
196                         config->ic_port[config->ic_nport].ir_end =
197                             I16(res) + res[2] - 1;
198                         config->ic_port[config->ic_nport].ir_size = res[2];
199                         config->ic_port[config->ic_nport].ir_align = 1;
200                         config->ic_nport++;
201                         break;
202
203                 case PNP_TAG_END:
204                         if (bootverbose)
205                                 pnp_printf(id, "end config\n");
206                         return (1);
207
208                 default:
209                         /* Skip this resource */
210                         pnp_printf(id, "unexpected small tag %d\n",
211                                       PNP_SRES_NUM(tag));
212                         break;
213                 }
214         } else {
215                 /* Large resource */
216                 switch (PNP_LRES_NUM(tag)) {
217
218                 case PNP_TAG_ID_UNICODE:
219                 case PNP_TAG_LARGE_VENDOR:
220                         /* these descriptors are quietly ignored */
221                         break;
222
223                 case PNP_TAG_ID_ANSI:
224                         if (len > sizeof(buf) - 1)
225                                 len = sizeof(buf) - 1;
226                         bcopy(res, buf, len);
227
228                         /*
229                          * Trim trailing spaces and garbage.
230                          */
231                         while (len > 0 && buf[len - 1] <= ' ')
232                                 len--;
233                         buf[len] = '\0';
234                         device_set_desc_copy(dev, buf);
235                         break;
236                         
237                 case PNP_TAG_MEMORY_RANGE:
238                         if (config->ic_nmem == ISA_NMEM) {
239                                 pnp_printf(id, "too many memory ranges\n");
240                                 return (1);
241                         }
242                         if (I16(res + 7) == 0) {
243                                 /* a null descriptor */
244                                 config->ic_mem[config->ic_nmem].ir_start = 0;
245                                 config->ic_mem[config->ic_nmem].ir_end = 0;
246                                 config->ic_mem[config->ic_nmem].ir_size = 0;
247                                 config->ic_mem[config->ic_nmem].ir_align = 0;
248                                 config->ic_nmem++;
249                                 break;
250                         }
251                         if (bootverbose) {
252                                 temp = I16(res + 7) << 8;
253                                 pnp_printf(id, "adding memory range "
254                                            "%#x-%#x, size=%#x, "
255                                            "align=%#x\n",
256                                            I16(res + 1) << 8,
257                                            (I16(res + 3) << 8) + temp - 1,
258                                            temp, I16(res + 5));
259                         }
260                         config->ic_mem[config->ic_nmem].ir_start =
261                             I16(res + 1) << 8;
262                         config->ic_mem[config->ic_nmem].ir_end =
263                             (I16(res + 3) << 8) + (I16(res + 7) << 8) - 1;
264                         config->ic_mem[config->ic_nmem].ir_size =
265                             I16(res + 7) << 8;
266                         config->ic_mem[config->ic_nmem].ir_align = I16(res + 5);
267                         if (!config->ic_mem[config->ic_nmem].ir_align)
268                                 config->ic_mem[config->ic_nmem].ir_align =
269                                     0x10000;
270                         config->ic_nmem++;
271                         break;
272
273                 case PNP_TAG_MEMORY32_RANGE:
274                         if (config->ic_nmem == ISA_NMEM) {
275                                 pnp_printf(id, "too many memory ranges\n");
276                                 return (1);
277                         }
278                         if (I32(res + 13) == 0) {
279                                 /* a null descriptor */
280                                 config->ic_mem[config->ic_nmem].ir_start = 0;
281                                 config->ic_mem[config->ic_nmem].ir_end = 0;
282                                 config->ic_mem[config->ic_nmem].ir_size = 0;
283                                 config->ic_mem[config->ic_nmem].ir_align = 0;
284                                 config->ic_nmem++;
285                                 break;
286                         }
287                         if (bootverbose) {
288                                 pnp_printf(id, "adding memory32 range "
289                                            "%#x-%#x, size=%#x, "
290                                            "align=%#x\n",
291                                            I32(res + 1),
292                                            I32(res + 5) + I32(res + 13) - 1,
293                                            I32(res + 13), I32(res + 9));
294                         }
295                         config->ic_mem[config->ic_nmem].ir_start = I32(res + 1);
296                         config->ic_mem[config->ic_nmem].ir_end =
297                             I32(res + 5) + I32(res + 13) - 1;
298                         config->ic_mem[config->ic_nmem].ir_size = I32(res + 13);
299                         config->ic_mem[config->ic_nmem].ir_align = I32(res + 9);
300                         config->ic_nmem++;
301                         break;
302
303                 case PNP_TAG_MEMORY32_FIXED:
304                         if (config->ic_nmem == ISA_NMEM) {
305                                 pnp_printf(id, "too many memory ranges\n");
306                                 return (1);
307                         }
308                         if (I32(res + 5) == 0) {
309                                 /* a null descriptor */
310                                 config->ic_mem[config->ic_nmem].ir_start = 0;
311                                 config->ic_mem[config->ic_nmem].ir_end = 0;
312                                 config->ic_mem[config->ic_nmem].ir_size = 0;
313                                 config->ic_mem[config->ic_nmem].ir_align = 0;
314                                 break;
315                         }
316                         if (bootverbose) {
317                                 pnp_printf(id, "adding fixed memory32 range "
318                                            "%#x-%#x, size=%#x\n",
319                                            I32(res + 1),
320                                            I32(res + 1) + I32(res + 5) - 1,
321                                            I32(res + 5));
322                         }
323                         config->ic_mem[config->ic_nmem].ir_start = I32(res + 1);
324                         config->ic_mem[config->ic_nmem].ir_end =
325                             I32(res + 1) + I32(res + 5) - 1;
326                         config->ic_mem[config->ic_nmem].ir_size = I32(res + 5);
327                         config->ic_mem[config->ic_nmem].ir_align = 1;
328                         config->ic_nmem++;
329                         break;
330
331                 default:
332                         /* Skip this resource */
333                         pnp_printf(id, "unexpected large tag %d\n",
334                                    PNP_SRES_NUM(tag));
335                         break;
336                 }
337         }
338
339         return (0);
340 }
341
342 /*
343  * Parse a single "dependent" resource combination.
344  */
345
346 u_char
347 *pnp_parse_dependant(device_t dev, u_char *resources, int len,
348                      struct isa_config *config, int ldn)
349 {
350
351         return pnp_scan_resources(dev, resources, len, config, ldn,
352                                   pnp_parse_desc);
353 }
354
355 static void
356 pnp_merge_resources(device_t dev, struct isa_config *from,
357                     struct isa_config *to)
358 {
359         device_t parent;
360         int i;
361
362         parent = device_get_parent(dev);
363         for (i = 0; i < from->ic_nmem; i++) {
364                 if (to->ic_nmem == ISA_NMEM) {
365                         device_printf(parent, "too many memory ranges\n");
366                         return;
367                 }
368                 to->ic_mem[to->ic_nmem] = from->ic_mem[i];
369                 to->ic_nmem++;
370         }
371         for (i = 0; i < from->ic_nport; i++) {
372                 if (to->ic_nport == ISA_NPORT) {
373                         device_printf(parent, "too many port ranges\n");
374                         return;
375                 }
376                 to->ic_port[to->ic_nport] = from->ic_port[i];
377                 to->ic_nport++;
378         }
379         for (i = 0; i < from->ic_nirq; i++) {
380                 if (to->ic_nirq == ISA_NIRQ) {
381                         device_printf(parent, "too many irq ranges\n");
382                         return;
383                 }
384                 to->ic_irqmask[to->ic_nirq] = from->ic_irqmask[i];
385                 to->ic_nirq++;
386         }
387         for (i = 0; i < from->ic_ndrq; i++) {
388                 if (to->ic_ndrq == ISA_NDRQ) {
389                         device_printf(parent, "too many drq ranges\n");
390                         return;
391                 }
392                 to->ic_drqmask[to->ic_ndrq] = from->ic_drqmask[i];
393                 to->ic_ndrq++;
394         }
395 }
396
397 /*
398  * Parse resource data for Logical Devices, make a list of available
399  * resource configurations, and add them to the device.
400  *
401  * This function exits as soon as it gets an error reading *ANY*
402  * Resource Data or it reaches the end of Resource Data.
403  */
404
405 void
406 pnp_parse_resources(device_t dev, u_char *resources, int len, int ldn)
407 {
408         struct isa_config *configs;
409         struct isa_config *config;
410         device_t parent;
411         int priorities[1 + MAXDEP];
412         u_char *start;
413         u_char *p;
414         u_char tag;
415         u_int32_t id;
416         int ncfgs;
417         int l;
418         int i;
419
420         parent = device_get_parent(dev);
421         id = isa_get_logicalid(dev);
422
423         configs = (struct isa_config *)malloc(sizeof(*configs)*(1 + MAXDEP),
424                                               M_DEVBUF, M_NOWAIT | M_ZERO);
425         if (configs == NULL) {
426                 device_printf(parent, "No memory to parse PNP data\n");
427                 return;
428         }
429         config = &configs[0];
430         priorities[0] = 0;
431         ncfgs = 1;
432
433         p = resources;
434         start = NULL;
435         while (len > 0) {
436                 tag = *p++;
437                 len--;
438                 if (PNP_RES_TYPE(tag) == 0) {
439                         /* Small resource */
440                         l = PNP_SRES_LEN(tag);
441                         if (len < l) {
442                                 len = 0;
443                                 continue;
444                         }
445                         len -= l;
446
447                         switch (PNP_SRES_NUM(tag)) {
448
449                         case PNP_TAG_START_DEPENDANT:
450                                 if (start != NULL) {
451                                         /*
452                                          * Copy the common resources first,
453                                          * then parse the "dependent" resources.
454                                          */
455                                         pnp_merge_resources(dev, &configs[0],
456                                                             config);
457                                         pnp_parse_dependant(dev, start,
458                                                             p - start - 1,
459                                                             config, ldn);
460                                 }
461                                 start = p + l;
462                                 if (ncfgs > MAXDEP) {
463                                         device_printf(parent, "too many dependent configs (%d)\n", MAXDEP);
464                                         len = 0;
465                                         break;
466                                 }
467                                 config = &configs[ncfgs];
468                                 /*
469                                  * If the priority is not specified,
470                                  * then use the default of 'acceptable'
471                                  */
472                                 if (l > 0)
473                                         priorities[ncfgs] = p[0];
474                                 else
475                                         priorities[ncfgs] = 1;
476                                 if (bootverbose)
477                                         pnp_printf(id, "start dependent (%d)\n",
478                                                    priorities[ncfgs]);
479                                 ncfgs++;
480                                 break;
481
482                         case PNP_TAG_END_DEPENDANT:
483                                 if (start == NULL) {
484                                         device_printf(parent,
485                                                       "malformed resources\n");
486                                         len = 0;
487                                         break;
488                                 }
489                                 /*
490                                  * Copy the common resources first,
491                                  * then parse the "dependent" resources.
492                                  */
493                                 pnp_merge_resources(dev, &configs[0], config);
494                                 pnp_parse_dependant(dev, start, p - start - 1,
495                                                     config, ldn);
496                                 start = NULL;
497                                 if (bootverbose)
498                                         pnp_printf(id, "end dependent\n");
499                                 /*
500                                  * Back to the common part; clear it
501                                  * as its contents has already been copied
502                                  * to each dependant.
503                                  */
504                                 config = &configs[0];
505                                 bzero(config, sizeof(*config));
506                                 break;
507
508                         case PNP_TAG_END:
509                                 if (start != NULL) {
510                                         device_printf(parent,
511                                                       "malformed resources\n");
512                                 }
513                                 len = 0;
514                                 break;
515
516                         default:
517                                 if (start != NULL)
518                                         /* defer parsing a dependent section */
519                                         break;
520                                 if (pnp_parse_desc(dev, tag, p, l, config, ldn))
521                                         len = 0;
522                                 break;
523                         }
524                         p += l;
525                 } else {
526                         /* Large resource */
527                         if (len < 2) {
528                                 len = 0;
529                                 break;
530                         }
531                         l = I16(p);
532                         p += 2;
533                         len -= 2;
534                         if (len < l) {
535                                 len = 0;
536                                 break;
537                         }
538                         len -= l;
539                         if (start == NULL &&
540                             pnp_parse_desc(dev, tag, p, l, config, ldn)) {
541                                 len = 0;
542                                 break;
543                         }
544                         p += l;
545                 }
546         }
547
548         if (ncfgs == 1) {
549                 /* Single config without dependants */
550                 ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]);
551                 free(configs, M_DEVBUF);
552                 return;
553         }
554
555         for (i = 1; i < ncfgs; i++) {
556                 /*
557                  * Merge the remaining part of the common resources,
558                  * if any. Strictly speaking, there shouldn't be common/main
559                  * resources after the END_DEPENDENT tag.
560                  */
561                 pnp_merge_resources(dev, &configs[0], &configs[i]);
562                 ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]);
563         }
564
565         free(configs, M_DEVBUF);
566 }
567
568 u_char
569 *pnp_scan_resources(device_t dev, u_char *resources, int len,
570                     struct isa_config *config, int ldn, pnp_scan_cb *cb)
571 {
572         u_char *p;
573         u_char tag;
574         int l;
575
576         p = resources;
577         while (len > 0) {
578                 tag = *p++;
579                 len--;
580                 if (PNP_RES_TYPE(tag) == 0) {
581                         /* small resource */
582                         l = PNP_SRES_LEN(tag);
583                         if (len < l)
584                                 break;
585                         if ((*cb)(dev, tag, p, l, config, ldn))
586                                 return (p + l);
587                         if (PNP_SRES_NUM(tag) == PNP_TAG_END)
588                                 return (p + l);
589                 } else {
590                         /* large resource */
591                         if (len < 2)
592                                 break;
593                         l = I16(p);
594                         p += 2;
595                         len -= 2;
596                         if (len < l)
597                                 break;
598                         if ((*cb)(dev, tag, p, l, config, ldn))
599                                 return (p + l);
600                 }
601                 p += l;
602                 len -= l;
603         }
604         return NULL;
605 }