c680aa640b1cee75ae382865b8d891ecbf50297e
[dragonfly.git] / sys / bus / 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  * $FreeBSD: src/sys/isa/pnpparse.c,v 1.14 2003/06/11 00:32:45 obrien Exp $
27  * $DragonFly: src/sys/bus/isa/pnpparse.c,v 1.10 2006/12/22 23:12:16 swildner Exp $
28  */
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 "isavar.h"
39 #include "pnpreg.h"
40 #include "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         kprintf("%s: ", pnp_eisaformat(id));
54         kvprintf(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         u_char *res;
351
352         res = pnp_scan_resources(dev, resources, len, config,
353                                 ldn, pnp_parse_desc);
354         return(res);
355 }
356
357 static void
358 pnp_merge_resources(device_t dev, struct isa_config *from,
359                     struct isa_config *to)
360 {
361         device_t parent;
362         int i;
363
364         parent = device_get_parent(dev);
365         for (i = 0; i < from->ic_nmem; i++) {
366                 if (to->ic_nmem == ISA_NMEM) {
367                         device_printf(parent, "too many memory ranges\n");
368                         return;
369                 }
370                 to->ic_mem[to->ic_nmem] = from->ic_mem[i];
371                 to->ic_nmem++;
372         }
373         for (i = 0; i < from->ic_nport; i++) {
374                 if (to->ic_nport == ISA_NPORT) {
375                         device_printf(parent, "too many port ranges\n");
376                         return;
377                 }
378                 to->ic_port[to->ic_nport] = from->ic_port[i];
379                 to->ic_nport++;
380         }
381         for (i = 0; i < from->ic_nirq; i++) {
382                 if (to->ic_nirq == ISA_NIRQ) {
383                         device_printf(parent, "too many irq ranges\n");
384                         return;
385                 }
386                 to->ic_irqmask[to->ic_nirq] = from->ic_irqmask[i];
387                 to->ic_nirq++;
388         }
389         for (i = 0; i < from->ic_ndrq; i++) {
390                 if (to->ic_ndrq == ISA_NDRQ) {
391                         device_printf(parent, "too many drq ranges\n");
392                         return;
393                 }
394                 to->ic_drqmask[to->ic_ndrq] = from->ic_drqmask[i];
395                 to->ic_ndrq++;
396         }
397 }
398
399 /*
400  * Parse resource data for Logical Devices, make a list of available
401  * resource configurations, and add them to the device.
402  *
403  * This function exits as soon as it gets an error reading *ANY*
404  * Resource Data or it reaches the end of Resource Data.
405  */
406
407 void
408 pnp_parse_resources(device_t dev, u_char *resources, int len, int ldn)
409 {
410         struct isa_config *configs;
411         struct isa_config *config;
412         device_t parent;
413         int priorities[1 + MAXDEP];
414         u_char *start;
415         u_char *p;
416         u_char tag;
417         u_int32_t id;
418         int ncfgs;
419         int l;
420         int i;
421
422         parent = device_get_parent(dev);
423         id = isa_get_logicalid(dev);
424
425         configs = kmalloc(sizeof(*configs)*(1 + MAXDEP),
426                           M_DEVBUF, M_WAITOK | M_ZERO);
427         config = &configs[0];
428         priorities[0] = 0;
429         ncfgs = 1;
430
431         p = resources;
432         start = NULL;
433         while (len > 0) {
434                 tag = *p++;
435                 len--;
436                 if (PNP_RES_TYPE(tag) == 0) {
437                         /* Small resource */
438                         l = PNP_SRES_LEN(tag);
439                         if (len < l) {
440                                 len = 0;
441                                 continue;
442                         }
443                         len -= l;
444
445                         switch (PNP_SRES_NUM(tag)) {
446
447                         case PNP_TAG_START_DEPENDANT:
448                                 if (start != NULL) {
449                                         /*
450                                          * Copy the common resources first,
451                                          * then parse the "dependent" resources.
452                                          */
453                                         pnp_merge_resources(dev, &configs[0],
454                                                             config);
455                                         pnp_parse_dependant(dev, start,
456                                                             p - start - 1,
457                                                             config, ldn);
458                                 }
459                                 start = p + l;
460                                 if (ncfgs > MAXDEP) {
461                                         device_printf(parent, "too many dependant configs (%d)\n", MAXDEP);
462                                         len = 0;
463                                         break;
464                                 }
465                                 config = &configs[ncfgs];
466                                 /*
467                                  * If the priority is not specified,
468                                  * then use the default of 'acceptable'
469                                  */
470                                 if (l > 0)
471                                         priorities[ncfgs] = p[0];
472                                 else
473                                         priorities[ncfgs] = 1;
474                                 if (bootverbose)
475                                         pnp_printf(id, "start dependent (%d)\n",
476                                                    priorities[ncfgs]);
477                                 ncfgs++;
478                                 break;
479
480                         case PNP_TAG_END_DEPENDANT:
481                                 if (start == NULL) {
482                                         device_printf(parent,
483                                                       "malformed resources\n");
484                                         len = 0;
485                                         break;
486                                 }
487                                 /*
488                                  * Copy the common resources first,
489                                  * then parse the "dependent" resources.
490                                  */
491                                 pnp_merge_resources(dev, &configs[0], config);
492                                 pnp_parse_dependant(dev, start, p - start - 1,
493                                                     config, ldn);
494                                 start = NULL;
495                                 if (bootverbose)
496                                         pnp_printf(id, "end dependent\n");
497                                 /*
498                                  * Back to the common part; clear it
499                                  * as its contents has already been copied
500                                  * to each dependant.
501                                  */
502                                 config = &configs[0];
503                                 bzero(config, sizeof(*config));
504                                 break;
505
506                         case PNP_TAG_END:
507                                 if (start != NULL) {
508                                         device_printf(parent,
509                                                       "malformed resources\n");
510                                 }
511                                 len = 0;
512                                 break;
513
514                         default:
515                                 if (start != NULL)
516                                         /* defer parsing a dependent section */
517                                         break;
518                                 if (pnp_parse_desc(dev, tag, p, l, config, ldn))
519                                         len = 0;
520                                 break;
521                         }
522                         p += l;
523                 } else {
524                         /* Large resource */
525                         if (len < 2) {
526                                 len = 0;
527                                 break;
528                         }
529                         l = I16(p);
530                         p += 2;
531                         len -= 2;
532                         if (len < l) {
533                                 len = 0;
534                                 break;
535                         }
536                         len -= l;
537                         if (start == NULL &&
538                             pnp_parse_desc(dev, tag, p, l, config, ldn)) {
539                                 len = 0;
540                                 break;
541                         }
542                         p += l;
543                 }
544         }
545
546         if (ncfgs == 1) {
547                 /* Single config without dependants */
548                 ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]);
549                 kfree(configs, M_DEVBUF);
550                 return;
551         }
552
553         for (i = 1; i < ncfgs; i++) {
554                 /*
555                  * Merge the remaining part of the common resources,
556                  * if any. Strictly speaking, there shouldn't be common/main
557                  * resources after the END_DEPENDENT tag.
558                  */
559                 pnp_merge_resources(dev, &configs[0], &configs[i]);
560                 ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]);
561         }
562
563         kfree(configs, M_DEVBUF);
564 }
565
566 u_char *
567 pnp_scan_resources(device_t dev, u_char *resources, int len,
568                     struct isa_config *config, int ldn, pnp_scan_cb *cb)
569 {
570         u_char *p;
571         u_char tag;
572         int l;
573
574         p = resources;
575         while (len > 0) {
576                 tag = *p++;
577                 len--;
578                 if (PNP_RES_TYPE(tag) == 0) {
579                         /* small resource */
580                         l = PNP_SRES_LEN(tag);
581                         if (len < l)
582                                 break;
583                         if ((*cb)(dev, tag, p, l, config, ldn))
584                                 return (p + l);
585                         if (PNP_SRES_NUM(tag) == PNP_TAG_END)
586                                 return (p + l);
587                 } else {
588                         /* large resource */
589                         if (len < 2)
590                                 break;
591                         l = I16(p);
592                         p += 2;
593                         len -= 2;
594                         if (len < l)
595                                 break;
596                         if ((*cb)(dev, tag, p, l, config, ldn))
597                                 return (p + l);
598                 }
599                 p += l;
600                 len -= l;
601         }
602         return NULL;
603 }