Initial import from FreeBSD RELENG_4:
[games.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.2.2.3 2000/11/07 05:53:55 msmith Exp $
27  */
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33 #include <sys/module.h>
34 #include <sys/bus.h>
35 #include <isa/isavar.h>
36 #include <isa/pnpreg.h>
37 #include <isa/pnpvar.h>
38
39 #define MAXDEP  8
40
41 #define I16(p)  ((p)[0] + ((p)[1] << 8))
42 #define I32(p)  (I16(p) + (I16(p+2) << 16))
43
44 /*
45  * Parse resource data for Logical Devices.
46  *
47  * This function exits as soon as it gets an error reading *ANY*
48  * Resource Data or it reaches the end of Resource Data.
49  */
50 void
51 pnp_parse_resources(device_t dev, u_char *resources, int len)
52 {
53         device_t parent = device_get_parent(dev);
54         u_char tag, *resp, *resinfo;
55         int large_len, scanning = len;
56         u_int32_t id, compat_id;
57         struct isa_config *config;
58         int ncfgs = 1;
59         int priorities[1 + MAXDEP];
60         struct isa_config *configs;
61         char buf[100];
62         int i;
63
64         id = isa_get_logicalid(dev);
65         configs = (struct isa_config *)malloc(sizeof(*configs) * (1 + MAXDEP),
66                                                 M_DEVBUF, M_NOWAIT);
67         if (configs == NULL) {
68                 device_printf(dev, "No memory to parse PNP data\n");
69                 return;
70         }
71         bzero(configs, sizeof(*configs) * (1 + MAXDEP));
72         config = &configs[0];
73         priorities[0] = 0;
74         resp = resources;
75         while (scanning > 0) {
76                 tag = *resp++;
77                 scanning--;
78                 if (PNP_RES_TYPE(tag) == 0) {
79                         /* Small resource */
80                         if (scanning < PNP_SRES_LEN(tag)) {
81                                 scanning = 0;
82                                 continue;
83                         }
84                         resinfo = resp;
85                         resp += PNP_SRES_LEN(tag);
86                         scanning -= PNP_SRES_LEN(tag);;
87                         
88                         switch (PNP_SRES_NUM(tag)) {
89                         case PNP_TAG_COMPAT_DEVICE:
90                                 /*
91                                  * Got a compatible device id
92                                  * resource. Should keep a list of
93                                  * compat ids in the device.
94                                  */
95                                 bcopy(resinfo, &compat_id, 4);
96                                 isa_set_compatid(dev, compat_id);
97                                 break;
98                     
99                         case PNP_TAG_IRQ_FORMAT:
100                                 if (bootverbose) {
101                                         printf("%s: adding irq mask %#04x\n",
102                                                pnp_eisaformat(id),
103                                                I16(resinfo));
104                                 }
105                                 if (config->ic_nirq == ISA_NIRQ) {
106                                         device_printf(parent, "too many irqs\n");
107                                         scanning = 0;
108                                         break;
109                                 }
110                                 config->ic_irqmask[config->ic_nirq] =
111                                         I16(resinfo);
112                                 config->ic_nirq++;
113                                 break;
114
115                         case PNP_TAG_DMA_FORMAT:
116                                 if (bootverbose) {
117                                         printf("%s: adding dma mask %#02x\n",
118                                                pnp_eisaformat(id),
119                                                resinfo[0]);
120                                 }
121                                 if (config->ic_ndrq == ISA_NDRQ) {
122                                         device_printf(parent, "too many drqs\n");
123                                         scanning = 0;
124                                         break;
125                                 }
126                                 config->ic_drqmask[config->ic_ndrq] =
127                                         resinfo[0];
128                                 config->ic_ndrq++;
129                                 break;
130
131                         case PNP_TAG_START_DEPENDANT:
132                                 if (bootverbose) {
133                                         printf("%s: start dependant\n",
134                                                pnp_eisaformat(id));
135                                 }
136                                 if (ncfgs >= MAXDEP) {
137                                         device_printf(parent, "too many dependant configs (%d)\n", MAXDEP);
138                                         scanning = 0;
139                                         break;
140                                 }
141                                 config = &configs[ncfgs];
142                                 /*
143                                  * If the priority is not specified,
144                                  * then use the default of
145                                  * 'acceptable'
146                                  */
147                                 if (PNP_SRES_LEN(tag) > 0)
148                                         priorities[ncfgs] = resinfo[0];
149                                 else
150                                         priorities[ncfgs] = 1;
151                                 ncfgs++;
152                                 break;
153
154                         case PNP_TAG_END_DEPENDANT:
155                                 if (bootverbose) {
156                                         printf("%s: end dependant\n",
157                                                pnp_eisaformat(id));
158                                 }
159                                 config = &configs[0];   /* back to main config */
160                                 break;
161
162                         case PNP_TAG_IO_RANGE:
163                                 if (bootverbose) {
164                                         printf("%s: adding io range "
165                                                "%#x-%#x, size=%#x, "
166                                                "align=%#x\n",
167                                                pnp_eisaformat(id),
168                                                I16(resinfo + 1),
169                                                I16(resinfo + 3) + resinfo[6]-1,
170                                                resinfo[6],
171                                                resinfo[5]);
172                                 }
173                                 if (config->ic_nport == ISA_NPORT) {
174                                         device_printf(parent, "too many ports\n");
175                                         scanning = 0;
176                                         break;
177                                 }
178                                 config->ic_port[config->ic_nport].ir_start =
179                                         I16(resinfo + 1);
180                                 config->ic_port[config->ic_nport].ir_end =
181                                         I16(resinfo + 3) + resinfo[6] - 1;
182                                 config->ic_port[config->ic_nport].ir_size =
183                                         resinfo[6];
184                                 if (resinfo[5] == 0) {
185                                     /* Make sure align is at least one */
186                                     resinfo[5] = 1;
187                                 }
188                                 config->ic_port[config->ic_nport].ir_align =
189                                         resinfo[5];
190                                 config->ic_nport++;
191                                 break;
192
193                         case PNP_TAG_IO_FIXED:
194                                 if (bootverbose) {
195                                         printf("%s: adding fixed io range "
196                                                "%#x-%#x, size=%#x, "
197                                                "align=%#x\n",
198                                                pnp_eisaformat(id),
199                                                I16(resinfo),
200                                                I16(resinfo) + resinfo[2] - 1,
201                                                resinfo[2],
202                                                1);
203                                 }
204                                 if (config->ic_nport == ISA_NPORT) {
205                                         device_printf(parent, "too many ports\n");
206                                         scanning = 0;
207                                         break;
208                                 }
209                                 config->ic_port[config->ic_nport].ir_start =
210                                         I16(resinfo);
211                                 config->ic_port[config->ic_nport].ir_end =
212                                         I16(resinfo) + resinfo[2] - 1;
213                                 config->ic_port[config->ic_nport].ir_size
214                                         = resinfo[2];
215                                 config->ic_port[config->ic_nport].ir_align = 1;
216                                 config->ic_nport++;
217                                 break;
218
219                         case PNP_TAG_END:
220                                 if (bootverbose) {
221                                         printf("%s: end config\n",
222                                                pnp_eisaformat(id));
223                                 }
224                                 scanning = 0;
225                                 break;
226
227                         default:
228                                 /* Skip this resource */
229                                 device_printf(parent, "unexpected small tag %d\n",
230                                               PNP_SRES_NUM(tag));
231                                 break;
232                         }
233                 } else {
234                         /* Large resource */
235                         if (scanning < 2) {
236                                 scanning = 0;
237                                 continue;
238                         }
239                         large_len = I16(resp);
240                         resp += 2;
241                         scanning -= 2;
242
243                         if (scanning < large_len) {
244                                 scanning = 0;
245                                 continue;
246                         }
247                         resinfo = resp;
248                         resp += large_len;
249                         scanning -= large_len;
250
251                         switch (PNP_LRES_NUM(tag)) {
252                         case PNP_TAG_ID_ANSI:
253                                 if (large_len > sizeof(buf) - 1)
254                                         large_len = sizeof(buf) - 1;
255                                 bcopy(resinfo, buf, large_len);
256
257                                 /*
258                                  * Trim trailing spaces and garbage.
259                                  */
260                                 while (large_len > 0 && buf[large_len - 1] <= ' ')
261                                         large_len--;
262                                 buf[large_len] = '\0';
263                                 device_set_desc_copy(dev, buf);
264                                 break;
265                                 
266                         case PNP_TAG_MEMORY_RANGE:
267                                 if (bootverbose) {
268                                         int temp = I16(resinfo + 7) << 8;
269
270                                         printf("%s: adding memory range "
271                                                "%#x-%#x, size=%#x, "
272                                                "align=%#x\n",
273                                                pnp_eisaformat(id),
274                                                I16(resinfo + 1)<<8,
275                                                (I16(resinfo + 3)<<8) + temp - 1,
276                                                temp,
277                                                I16(resinfo + 5));
278                                 }
279
280                                 if (config->ic_nmem == ISA_NMEM) {
281                                         device_printf(parent, "too many memory ranges\n");
282                                         scanning = 0;
283                                         break;
284                                 }
285
286                                 config->ic_mem[config->ic_nmem].ir_start =
287                                         I16(resinfo + 1)<<8;
288                                 config->ic_mem[config->ic_nmem].ir_end =
289                                         (I16(resinfo + 3)<<8)
290                                         + (I16(resinfo + 7) << 8) - 1;
291                                 config->ic_mem[config->ic_nmem].ir_size =
292                                         I16(resinfo + 7) << 8;
293                                 config->ic_mem[config->ic_nmem].ir_align =
294                                         I16(resinfo + 5);
295                                 if (!config->ic_mem[config->ic_nmem].ir_align)
296                                         config->ic_mem[config->ic_nmem]
297                                                 .ir_align = 0x10000;
298                                 config->ic_nmem++;
299                                 break;
300
301                         case PNP_TAG_MEMORY32_RANGE:
302                                 if (I32(resinfo + 13) == 0) {
303                                         if (bootverbose) {
304                                                 printf("%s: skipping empty range\n",
305                                                        pnp_eisaformat(id));
306                                         }
307                                         continue;
308                                 }
309                                 if (bootverbose) {
310                                         printf("%s: adding memory32 range "
311                                                "%#x-%#x, size=%#x, "
312                                                "align=%#x\n",
313                                                pnp_eisaformat(id),
314                                                I32(resinfo + 1),
315                                                I32(resinfo + 5)
316                                                + I32(resinfo + 13) - 1,
317                                                I32(resinfo + 13),
318                                                I32(resinfo + 9));
319                                 }
320
321                                 if (config->ic_nmem == ISA_NMEM) {
322                                         device_printf(parent, "too many memory ranges\n");
323                                         scanning = 0;
324                                         break;
325                                 }
326
327                                 config->ic_mem[config->ic_nmem].ir_start =
328                                         I32(resinfo + 1);
329                                 config->ic_mem[config->ic_nmem].ir_end =
330                                         I32(resinfo + 5)
331                                         + I32(resinfo + 13) - 1;
332                                 config->ic_mem[config->ic_nmem].ir_size =
333                                         I32(resinfo + 13);
334                                 config->ic_mem[config->ic_nmem].ir_align =
335                                         I32(resinfo + 9);
336                                 config->ic_nmem++;
337                                 break;
338
339                         case PNP_TAG_MEMORY32_FIXED:
340                                 if (I32(resinfo + 5) == 0) {
341                                         if (bootverbose) {
342                                                 printf("%s: skipping empty range\n",
343                                                        pnp_eisaformat(id));
344                                         }
345                                         continue;
346                                 }
347                                 if (bootverbose) {
348                                         printf("%s: adding fixed memory32 range "
349                                                "%#x-%#x, size=%#x\n",
350                                                pnp_eisaformat(id),
351                                                I32(resinfo + 1),
352                                                I32(resinfo + 1)
353                                                + I32(resinfo + 5) - 1,
354                                                I32(resinfo + 5));
355                                 }
356
357                                 if (config->ic_nmem == ISA_NMEM) {
358                                         device_printf(parent, "too many memory ranges\n");
359                                         scanning = 0;
360                                         break;
361                                 }
362
363                                 config->ic_mem[config->ic_nmem].ir_start =
364                                         I32(resinfo + 1);
365                                 config->ic_mem[config->ic_nmem].ir_end =
366                                         I32(resinfo + 1)
367                                         + I32(resinfo + 5) - 1;
368                                 config->ic_mem[config->ic_nmem].ir_size =
369                                         I32(resinfo + 5);
370                                 config->ic_mem[config->ic_nmem].ir_align = 1;
371                                 config->ic_nmem++;
372                                 break;
373
374                         default:
375                                 /* Skip this resource */
376                                 device_printf(parent, "unexpected large tag %d\n",
377                                               PNP_SRES_NUM(tag));
378                         }
379                 }
380         }
381         if(ncfgs == 1) {
382                 /* Single config without dependants */
383                 (void)ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]);
384                 free(configs, M_DEVBUF);
385                 return;
386         }
387         /* Cycle through dependant configs merging primary details */
388         for(i = 1; i < ncfgs; i++) {
389                 int j;
390                 config = &configs[i];
391                 for(j = 0; j < configs[0].ic_nmem; j++) {
392                         if (config->ic_nmem == ISA_NMEM) {
393                                 device_printf(parent, "too many memory ranges\n");
394                                 free(configs, M_DEVBUF);
395                                 return;
396                         }
397                         config->ic_mem[config->ic_nmem] = configs[0].ic_mem[j];
398                         config->ic_nmem++;
399                 }
400                 for(j = 0; j < configs[0].ic_nport; j++) {
401                         if (config->ic_nport == ISA_NPORT) {
402                                 device_printf(parent, "too many port ranges\n");
403                                 free(configs, M_DEVBUF);
404                                 return;
405                         }
406                         config->ic_port[config->ic_nport] = configs[0].ic_port[j];
407                         config->ic_nport++;
408                 }
409                 for(j = 0; j < configs[0].ic_nirq; j++) {
410                         if (config->ic_nirq == ISA_NIRQ) {
411                                 device_printf(parent, "too many irq ranges\n");
412                                 free(configs, M_DEVBUF);
413                                 return;
414                         }
415                         config->ic_irqmask[config->ic_nirq] = configs[0].ic_irqmask[j];
416                         config->ic_nirq++;
417                 }
418                 for(j = 0; j < configs[0].ic_ndrq; j++) {
419                         if (config->ic_ndrq == ISA_NDRQ) {
420                                 device_printf(parent, "too many drq ranges\n");
421                                 free(configs, M_DEVBUF);
422                                 return;
423                         }
424                         config->ic_drqmask[config->ic_ndrq] = configs[0].ic_drqmask[j];
425                         config->ic_ndrq++;
426                 }
427                 (void)ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]);
428         }
429         free(configs, M_DEVBUF);
430 }