3ed607158f47220be8da30f5cbb3aafa86eb075b
[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.5 2003/11/09 02:22:33 dillon 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         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         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 = (struct isa_config *)malloc(sizeof(*configs)*(1 + MAXDEP),
426                                               M_DEVBUF, M_NOWAIT | M_ZERO);
427         if (configs == NULL) {
428                 device_printf(parent, "No memory to parse PNP data\n");
429                 return;
430         }
431         config = &configs[0];
432         priorities[0] = 0;
433         ncfgs = 1;
434
435         p = resources;
436         start = NULL;
437         while (len > 0) {
438                 tag = *p++;
439                 len--;
440                 if (PNP_RES_TYPE(tag) == 0) {
441                         /* Small resource */
442                         l = PNP_SRES_LEN(tag);
443                         if (len < l) {
444                                 len = 0;
445                                 continue;
446                         }
447                         len -= l;
448
449                         switch (PNP_SRES_NUM(tag)) {
450
451                         case PNP_TAG_START_DEPENDANT:
452                                 if (start != NULL) {
453                                         /*
454                                          * Copy the common resources first,
455                                          * then parse the "dependent" resources.
456                                          */
457                                         pnp_merge_resources(dev, &configs[0],
458                                                             config);
459                                         pnp_parse_dependant(dev, start,
460                                                             p - start - 1,
461                                                             config, ldn);
462                                 }
463                                 start = p + l;
464                                 if (ncfgs > MAXDEP) {
465                                         device_printf(parent, "too many dependant configs (%d)\n", MAXDEP);
466                                         len = 0;
467                                         break;
468                                 }
469                                 config = &configs[ncfgs];
470                                 /*
471                                  * If the priority is not specified,
472                                  * then use the default of 'acceptable'
473                                  */
474                                 if (l > 0)
475                                         priorities[ncfgs] = p[0];
476                                 else
477                                         priorities[ncfgs] = 1;
478                                 if (bootverbose)
479                                         pnp_printf(id, "start dependent (%d)\n",
480                                                    priorities[ncfgs]);
481                                 ncfgs++;
482                                 break;
483
484                         case PNP_TAG_END_DEPENDANT:
485                                 if (start == NULL) {
486                                         device_printf(parent,
487                                                       "malformed resources\n");
488                                         len = 0;
489                                         break;
490                                 }
491                                 /*
492                                  * Copy the common resources first,
493                                  * then parse the "dependent" resources.
494                                  */
495                                 pnp_merge_resources(dev, &configs[0], config);
496                                 pnp_parse_dependant(dev, start, p - start - 1,
497                                                     config, ldn);
498                                 start = NULL;
499                                 if (bootverbose)
500                                         pnp_printf(id, "end dependent\n");
501                                 /*
502                                  * Back to the common part; clear it
503                                  * as its contents has already been copied
504                                  * to each dependant.
505                                  */
506                                 config = &configs[0];
507                                 bzero(config, sizeof(*config));
508                                 break;
509
510                         case PNP_TAG_END:
511                                 if (start != NULL) {
512                                         device_printf(parent,
513                                                       "malformed resources\n");
514                                 }
515                                 len = 0;
516                                 break;
517
518                         default:
519                                 if (start != NULL)
520                                         /* defer parsing a dependent section */
521                                         break;
522                                 if (pnp_parse_desc(dev, tag, p, l, config, ldn))
523                                         len = 0;
524                                 break;
525                         }
526                         p += l;
527                 } else {
528                         /* Large resource */
529                         if (len < 2) {
530                                 len = 0;
531                                 break;
532                         }
533                         l = I16(p);
534                         p += 2;
535                         len -= 2;
536                         if (len < l) {
537                                 len = 0;
538                                 break;
539                         }
540                         len -= l;
541                         if (start == NULL &&
542                             pnp_parse_desc(dev, tag, p, l, config, ldn)) {
543                                 len = 0;
544                                 break;
545                         }
546                         p += l;
547                 }
548         }
549
550         if (ncfgs == 1) {
551                 /* Single config without dependants */
552                 ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]);
553                 free(configs, M_DEVBUF);
554                 return;
555         }
556
557         for (i = 1; i < ncfgs; i++) {
558                 /*
559                  * Merge the remaining part of the common resources,
560                  * if any. Strictly speaking, there shouldn't be common/main
561                  * resources after the END_DEPENDENT tag.
562                  */
563                 pnp_merge_resources(dev, &configs[0], &configs[i]);
564                 ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]);
565         }
566
567         free(configs, M_DEVBUF);
568 }
569
570 u_char *
571 pnp_scan_resources(device_t dev, u_char *resources, int len,
572                     struct isa_config *config, int ldn, pnp_scan_cb *cb)
573 {
574         u_char *p;
575         u_char tag;
576         int l;
577
578         p = resources;
579         while (len > 0) {
580                 tag = *p++;
581                 len--;
582                 if (PNP_RES_TYPE(tag) == 0) {
583                         /* small resource */
584                         l = PNP_SRES_LEN(tag);
585                         if (len < l)
586                                 break;
587                         if ((*cb)(dev, tag, p, l, config, ldn))
588                                 return (p + l);
589                         if (PNP_SRES_NUM(tag) == PNP_TAG_END)
590                                 return (p + l);
591                 } else {
592                         /* large resource */
593                         if (len < 2)
594                                 break;
595                         l = I16(p);
596                         p += 2;
597                         len -= 2;
598                         if (len < l)
599                                 break;
600                         if ((*cb)(dev, tag, p, l, config, ldn))
601                                 return (p + l);
602                 }
603                 p += l;
604                 len -= l;
605         }
606         return NULL;
607 }