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