diff options
Diffstat (limited to 'drivers/pnp/pnpbios/rsparser.c')
-rw-r--r-- | drivers/pnp/pnpbios/rsparser.c | 795 |
1 files changed, 795 insertions, 0 deletions
diff --git a/drivers/pnp/pnpbios/rsparser.c b/drivers/pnp/pnpbios/rsparser.c new file mode 100644 index 000000000000..618ac15a9e90 --- /dev/null +++ b/drivers/pnp/pnpbios/rsparser.c | |||
@@ -0,0 +1,795 @@ | |||
1 | /* | ||
2 | * rsparser.c - parses and encodes pnpbios resource data streams | ||
3 | * | ||
4 | */ | ||
5 | |||
6 | #include <linux/config.h> | ||
7 | #include <linux/ctype.h> | ||
8 | #include <linux/pnp.h> | ||
9 | #include <linux/pnpbios.h> | ||
10 | |||
11 | #ifdef CONFIG_PCI | ||
12 | #include <linux/pci.h> | ||
13 | #else | ||
14 | inline void pcibios_penalize_isa_irq(int irq) {} | ||
15 | #endif /* CONFIG_PCI */ | ||
16 | |||
17 | #include "pnpbios.h" | ||
18 | |||
19 | /* standard resource tags */ | ||
20 | #define SMALL_TAG_PNPVERNO 0x01 | ||
21 | #define SMALL_TAG_LOGDEVID 0x02 | ||
22 | #define SMALL_TAG_COMPATDEVID 0x03 | ||
23 | #define SMALL_TAG_IRQ 0x04 | ||
24 | #define SMALL_TAG_DMA 0x05 | ||
25 | #define SMALL_TAG_STARTDEP 0x06 | ||
26 | #define SMALL_TAG_ENDDEP 0x07 | ||
27 | #define SMALL_TAG_PORT 0x08 | ||
28 | #define SMALL_TAG_FIXEDPORT 0x09 | ||
29 | #define SMALL_TAG_VENDOR 0x0e | ||
30 | #define SMALL_TAG_END 0x0f | ||
31 | #define LARGE_TAG 0x80 | ||
32 | #define LARGE_TAG_MEM 0x81 | ||
33 | #define LARGE_TAG_ANSISTR 0x82 | ||
34 | #define LARGE_TAG_UNICODESTR 0x83 | ||
35 | #define LARGE_TAG_VENDOR 0x84 | ||
36 | #define LARGE_TAG_MEM32 0x85 | ||
37 | #define LARGE_TAG_FIXEDMEM32 0x86 | ||
38 | |||
39 | /* | ||
40 | * Resource Data Stream Format: | ||
41 | * | ||
42 | * Allocated Resources (required) | ||
43 | * end tag -> | ||
44 | * Resource Configuration Options (optional) | ||
45 | * end tag -> | ||
46 | * Compitable Device IDs (optional) | ||
47 | * final end tag -> | ||
48 | */ | ||
49 | |||
50 | /* | ||
51 | * Allocated Resources | ||
52 | */ | ||
53 | |||
54 | static void | ||
55 | pnpbios_parse_allocated_irqresource(struct pnp_resource_table * res, int irq) | ||
56 | { | ||
57 | int i = 0; | ||
58 | while (!(res->irq_resource[i].flags & IORESOURCE_UNSET) && i < PNP_MAX_IRQ) i++; | ||
59 | if (i < PNP_MAX_IRQ) { | ||
60 | res->irq_resource[i].flags = IORESOURCE_IRQ; // Also clears _UNSET flag | ||
61 | if (irq == -1) { | ||
62 | res->irq_resource[i].flags |= IORESOURCE_DISABLED; | ||
63 | return; | ||
64 | } | ||
65 | res->irq_resource[i].start = | ||
66 | res->irq_resource[i].end = (unsigned long) irq; | ||
67 | pcibios_penalize_isa_irq(irq); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | static void | ||
72 | pnpbios_parse_allocated_dmaresource(struct pnp_resource_table * res, int dma) | ||
73 | { | ||
74 | int i = 0; | ||
75 | while (!(res->dma_resource[i].flags & IORESOURCE_UNSET) && i < PNP_MAX_DMA) i++; | ||
76 | if (i < PNP_MAX_DMA) { | ||
77 | res->dma_resource[i].flags = IORESOURCE_DMA; // Also clears _UNSET flag | ||
78 | if (dma == -1) { | ||
79 | res->dma_resource[i].flags |= IORESOURCE_DISABLED; | ||
80 | return; | ||
81 | } | ||
82 | res->dma_resource[i].start = | ||
83 | res->dma_resource[i].end = (unsigned long) dma; | ||
84 | } | ||
85 | } | ||
86 | |||
87 | static void | ||
88 | pnpbios_parse_allocated_ioresource(struct pnp_resource_table * res, int io, int len) | ||
89 | { | ||
90 | int i = 0; | ||
91 | while (!(res->port_resource[i].flags & IORESOURCE_UNSET) && i < PNP_MAX_PORT) i++; | ||
92 | if (i < PNP_MAX_PORT) { | ||
93 | res->port_resource[i].flags = IORESOURCE_IO; // Also clears _UNSET flag | ||
94 | if (len <= 0 || (io + len -1) >= 0x10003) { | ||
95 | res->port_resource[i].flags |= IORESOURCE_DISABLED; | ||
96 | return; | ||
97 | } | ||
98 | res->port_resource[i].start = (unsigned long) io; | ||
99 | res->port_resource[i].end = (unsigned long)(io + len - 1); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | static void | ||
104 | pnpbios_parse_allocated_memresource(struct pnp_resource_table * res, int mem, int len) | ||
105 | { | ||
106 | int i = 0; | ||
107 | while (!(res->mem_resource[i].flags & IORESOURCE_UNSET) && i < PNP_MAX_MEM) i++; | ||
108 | if (i < PNP_MAX_MEM) { | ||
109 | res->mem_resource[i].flags = IORESOURCE_MEM; // Also clears _UNSET flag | ||
110 | if (len <= 0) { | ||
111 | res->mem_resource[i].flags |= IORESOURCE_DISABLED; | ||
112 | return; | ||
113 | } | ||
114 | res->mem_resource[i].start = (unsigned long) mem; | ||
115 | res->mem_resource[i].end = (unsigned long)(mem + len - 1); | ||
116 | } | ||
117 | } | ||
118 | |||
119 | static unsigned char * | ||
120 | pnpbios_parse_allocated_resource_data(unsigned char * p, unsigned char * end, struct pnp_resource_table * res) | ||
121 | { | ||
122 | unsigned int len, tag; | ||
123 | int io, size, mask, i; | ||
124 | |||
125 | if (!p) | ||
126 | return NULL; | ||
127 | |||
128 | /* Blank the resource table values */ | ||
129 | pnp_init_resource_table(res); | ||
130 | |||
131 | while ((char *)p < (char *)end) { | ||
132 | |||
133 | /* determine the type of tag */ | ||
134 | if (p[0] & LARGE_TAG) { /* large tag */ | ||
135 | len = (p[2] << 8) | p[1]; | ||
136 | tag = p[0]; | ||
137 | } else { /* small tag */ | ||
138 | len = p[0] & 0x07; | ||
139 | tag = ((p[0]>>3) & 0x0f); | ||
140 | } | ||
141 | |||
142 | switch (tag) { | ||
143 | |||
144 | case LARGE_TAG_MEM: | ||
145 | if (len != 9) | ||
146 | goto len_err; | ||
147 | io = *(short *) &p[4]; | ||
148 | size = *(short *) &p[10]; | ||
149 | pnpbios_parse_allocated_memresource(res, io, size); | ||
150 | break; | ||
151 | |||
152 | case LARGE_TAG_ANSISTR: | ||
153 | /* ignore this for now */ | ||
154 | break; | ||
155 | |||
156 | case LARGE_TAG_VENDOR: | ||
157 | /* do nothing */ | ||
158 | break; | ||
159 | |||
160 | case LARGE_TAG_MEM32: | ||
161 | if (len != 17) | ||
162 | goto len_err; | ||
163 | io = *(int *) &p[4]; | ||
164 | size = *(int *) &p[16]; | ||
165 | pnpbios_parse_allocated_memresource(res, io, size); | ||
166 | break; | ||
167 | |||
168 | case LARGE_TAG_FIXEDMEM32: | ||
169 | if (len != 9) | ||
170 | goto len_err; | ||
171 | io = *(int *) &p[4]; | ||
172 | size = *(int *) &p[8]; | ||
173 | pnpbios_parse_allocated_memresource(res, io, size); | ||
174 | break; | ||
175 | |||
176 | case SMALL_TAG_IRQ: | ||
177 | if (len < 2 || len > 3) | ||
178 | goto len_err; | ||
179 | io = -1; | ||
180 | mask= p[1] + p[2]*256; | ||
181 | for (i=0;i<16;i++, mask=mask>>1) | ||
182 | if(mask & 0x01) io=i; | ||
183 | pnpbios_parse_allocated_irqresource(res, io); | ||
184 | break; | ||
185 | |||
186 | case SMALL_TAG_DMA: | ||
187 | if (len != 2) | ||
188 | goto len_err; | ||
189 | io = -1; | ||
190 | mask = p[1]; | ||
191 | for (i=0;i<8;i++, mask = mask>>1) | ||
192 | if(mask & 0x01) io=i; | ||
193 | pnpbios_parse_allocated_dmaresource(res, io); | ||
194 | break; | ||
195 | |||
196 | case SMALL_TAG_PORT: | ||
197 | if (len != 7) | ||
198 | goto len_err; | ||
199 | io = p[2] + p[3] *256; | ||
200 | size = p[7]; | ||
201 | pnpbios_parse_allocated_ioresource(res, io, size); | ||
202 | break; | ||
203 | |||
204 | case SMALL_TAG_VENDOR: | ||
205 | /* do nothing */ | ||
206 | break; | ||
207 | |||
208 | case SMALL_TAG_FIXEDPORT: | ||
209 | if (len != 3) | ||
210 | goto len_err; | ||
211 | io = p[1] + p[2] * 256; | ||
212 | size = p[3]; | ||
213 | pnpbios_parse_allocated_ioresource(res, io, size); | ||
214 | break; | ||
215 | |||
216 | case SMALL_TAG_END: | ||
217 | p = p + 2; | ||
218 | return (unsigned char *)p; | ||
219 | break; | ||
220 | |||
221 | default: /* an unkown tag */ | ||
222 | len_err: | ||
223 | printk(KERN_ERR "PnPBIOS: Unknown tag '0x%x', length '%d'.\n", tag, len); | ||
224 | break; | ||
225 | } | ||
226 | |||
227 | /* continue to the next tag */ | ||
228 | if (p[0] & LARGE_TAG) | ||
229 | p += len + 3; | ||
230 | else | ||
231 | p += len + 1; | ||
232 | } | ||
233 | |||
234 | printk(KERN_ERR "PnPBIOS: Resource structure does not contain an end tag.\n"); | ||
235 | |||
236 | return NULL; | ||
237 | } | ||
238 | |||
239 | |||
240 | /* | ||
241 | * Resource Configuration Options | ||
242 | */ | ||
243 | |||
244 | static void | ||
245 | pnpbios_parse_mem_option(unsigned char *p, int size, struct pnp_option *option) | ||
246 | { | ||
247 | struct pnp_mem * mem; | ||
248 | mem = pnpbios_kmalloc(sizeof(struct pnp_mem), GFP_KERNEL); | ||
249 | if (!mem) | ||
250 | return; | ||
251 | mem->min = ((p[5] << 8) | p[4]) << 8; | ||
252 | mem->max = ((p[7] << 8) | p[6]) << 8; | ||
253 | mem->align = (p[9] << 8) | p[8]; | ||
254 | mem->size = ((p[11] << 8) | p[10]) << 8; | ||
255 | mem->flags = p[3]; | ||
256 | pnp_register_mem_resource(option,mem); | ||
257 | return; | ||
258 | } | ||
259 | |||
260 | static void | ||
261 | pnpbios_parse_mem32_option(unsigned char *p, int size, struct pnp_option *option) | ||
262 | { | ||
263 | struct pnp_mem * mem; | ||
264 | mem = pnpbios_kmalloc(sizeof(struct pnp_mem), GFP_KERNEL); | ||
265 | if (!mem) | ||
266 | return; | ||
267 | mem->min = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]; | ||
268 | mem->max = (p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8]; | ||
269 | mem->align = (p[15] << 24) | (p[14] << 16) | (p[13] << 8) | p[12]; | ||
270 | mem->size = (p[19] << 24) | (p[18] << 16) | (p[17] << 8) | p[16]; | ||
271 | mem->flags = p[3]; | ||
272 | pnp_register_mem_resource(option,mem); | ||
273 | return; | ||
274 | } | ||
275 | |||
276 | static void | ||
277 | pnpbios_parse_fixed_mem32_option(unsigned char *p, int size, struct pnp_option *option) | ||
278 | { | ||
279 | struct pnp_mem * mem; | ||
280 | mem = pnpbios_kmalloc(sizeof(struct pnp_mem), GFP_KERNEL); | ||
281 | if (!mem) | ||
282 | return; | ||
283 | mem->min = mem->max = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]; | ||
284 | mem->size = (p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8]; | ||
285 | mem->align = 0; | ||
286 | mem->flags = p[3]; | ||
287 | pnp_register_mem_resource(option,mem); | ||
288 | return; | ||
289 | } | ||
290 | |||
291 | static void | ||
292 | pnpbios_parse_irq_option(unsigned char *p, int size, struct pnp_option *option) | ||
293 | { | ||
294 | struct pnp_irq * irq; | ||
295 | unsigned long bits; | ||
296 | |||
297 | irq = pnpbios_kmalloc(sizeof(struct pnp_irq), GFP_KERNEL); | ||
298 | if (!irq) | ||
299 | return; | ||
300 | bits = (p[2] << 8) | p[1]; | ||
301 | bitmap_copy(irq->map, &bits, 16); | ||
302 | if (size > 2) | ||
303 | irq->flags = p[3]; | ||
304 | else | ||
305 | irq->flags = IORESOURCE_IRQ_HIGHEDGE; | ||
306 | pnp_register_irq_resource(option,irq); | ||
307 | return; | ||
308 | } | ||
309 | |||
310 | static void | ||
311 | pnpbios_parse_dma_option(unsigned char *p, int size, struct pnp_option *option) | ||
312 | { | ||
313 | struct pnp_dma * dma; | ||
314 | dma = pnpbios_kmalloc(sizeof(struct pnp_dma), GFP_KERNEL); | ||
315 | if (!dma) | ||
316 | return; | ||
317 | dma->map = p[1]; | ||
318 | dma->flags = p[2]; | ||
319 | pnp_register_dma_resource(option,dma); | ||
320 | return; | ||
321 | } | ||
322 | |||
323 | static void | ||
324 | pnpbios_parse_port_option(unsigned char *p, int size, struct pnp_option *option) | ||
325 | { | ||
326 | struct pnp_port * port; | ||
327 | port = pnpbios_kmalloc(sizeof(struct pnp_port), GFP_KERNEL); | ||
328 | if (!port) | ||
329 | return; | ||
330 | port->min = (p[3] << 8) | p[2]; | ||
331 | port->max = (p[5] << 8) | p[4]; | ||
332 | port->align = p[6]; | ||
333 | port->size = p[7]; | ||
334 | port->flags = p[1] ? PNP_PORT_FLAG_16BITADDR : 0; | ||
335 | pnp_register_port_resource(option,port); | ||
336 | return; | ||
337 | } | ||
338 | |||
339 | static void | ||
340 | pnpbios_parse_fixed_port_option(unsigned char *p, int size, struct pnp_option *option) | ||
341 | { | ||
342 | struct pnp_port * port; | ||
343 | port = pnpbios_kmalloc(sizeof(struct pnp_port), GFP_KERNEL); | ||
344 | if (!port) | ||
345 | return; | ||
346 | port->min = port->max = (p[2] << 8) | p[1]; | ||
347 | port->size = p[3]; | ||
348 | port->align = 0; | ||
349 | port->flags = PNP_PORT_FLAG_FIXED; | ||
350 | pnp_register_port_resource(option,port); | ||
351 | return; | ||
352 | } | ||
353 | |||
354 | static unsigned char * | ||
355 | pnpbios_parse_resource_option_data(unsigned char * p, unsigned char * end, struct pnp_dev *dev) | ||
356 | { | ||
357 | unsigned int len, tag; | ||
358 | int priority = 0; | ||
359 | struct pnp_option *option, *option_independent; | ||
360 | |||
361 | if (!p) | ||
362 | return NULL; | ||
363 | |||
364 | option_independent = option = pnp_register_independent_option(dev); | ||
365 | if (!option) | ||
366 | return NULL; | ||
367 | |||
368 | while ((char *)p < (char *)end) { | ||
369 | |||
370 | /* determine the type of tag */ | ||
371 | if (p[0] & LARGE_TAG) { /* large tag */ | ||
372 | len = (p[2] << 8) | p[1]; | ||
373 | tag = p[0]; | ||
374 | } else { /* small tag */ | ||
375 | len = p[0] & 0x07; | ||
376 | tag = ((p[0]>>3) & 0x0f); | ||
377 | } | ||
378 | |||
379 | switch (tag) { | ||
380 | |||
381 | case LARGE_TAG_MEM: | ||
382 | if (len != 9) | ||
383 | goto len_err; | ||
384 | pnpbios_parse_mem_option(p, len, option); | ||
385 | break; | ||
386 | |||
387 | case LARGE_TAG_MEM32: | ||
388 | if (len != 17) | ||
389 | goto len_err; | ||
390 | pnpbios_parse_mem32_option(p, len, option); | ||
391 | break; | ||
392 | |||
393 | case LARGE_TAG_FIXEDMEM32: | ||
394 | if (len != 9) | ||
395 | goto len_err; | ||
396 | pnpbios_parse_fixed_mem32_option(p, len, option); | ||
397 | break; | ||
398 | |||
399 | case SMALL_TAG_IRQ: | ||
400 | if (len < 2 || len > 3) | ||
401 | goto len_err; | ||
402 | pnpbios_parse_irq_option(p, len, option); | ||
403 | break; | ||
404 | |||
405 | case SMALL_TAG_DMA: | ||
406 | if (len != 2) | ||
407 | goto len_err; | ||
408 | pnpbios_parse_dma_option(p, len, option); | ||
409 | break; | ||
410 | |||
411 | case SMALL_TAG_PORT: | ||
412 | if (len != 7) | ||
413 | goto len_err; | ||
414 | pnpbios_parse_port_option(p, len, option); | ||
415 | break; | ||
416 | |||
417 | case SMALL_TAG_VENDOR: | ||
418 | /* do nothing */ | ||
419 | break; | ||
420 | |||
421 | case SMALL_TAG_FIXEDPORT: | ||
422 | if (len != 3) | ||
423 | goto len_err; | ||
424 | pnpbios_parse_fixed_port_option(p, len, option); | ||
425 | break; | ||
426 | |||
427 | case SMALL_TAG_STARTDEP: | ||
428 | if (len > 1) | ||
429 | goto len_err; | ||
430 | priority = 0x100 | PNP_RES_PRIORITY_ACCEPTABLE; | ||
431 | if (len > 0) | ||
432 | priority = 0x100 | p[1]; | ||
433 | option = pnp_register_dependent_option(dev, priority); | ||
434 | if (!option) | ||
435 | return NULL; | ||
436 | break; | ||
437 | |||
438 | case SMALL_TAG_ENDDEP: | ||
439 | if (len != 0) | ||
440 | goto len_err; | ||
441 | if (option_independent == option) | ||
442 | printk(KERN_WARNING "PnPBIOS: Missing SMALL_TAG_STARTDEP tag\n"); | ||
443 | option = option_independent; | ||
444 | break; | ||
445 | |||
446 | case SMALL_TAG_END: | ||
447 | if (option_independent != option) | ||
448 | printk(KERN_WARNING "PnPBIOS: Missing SMALL_TAG_ENDDEP tag\n"); | ||
449 | p = p + 2; | ||
450 | return (unsigned char *)p; | ||
451 | break; | ||
452 | |||
453 | default: /* an unkown tag */ | ||
454 | len_err: | ||
455 | printk(KERN_ERR "PnPBIOS: Unknown tag '0x%x', length '%d'.\n", tag, len); | ||
456 | break; | ||
457 | } | ||
458 | |||
459 | /* continue to the next tag */ | ||
460 | if (p[0] & LARGE_TAG) | ||
461 | p += len + 3; | ||
462 | else | ||
463 | p += len + 1; | ||
464 | } | ||
465 | |||
466 | printk(KERN_ERR "PnPBIOS: Resource structure does not contain an end tag.\n"); | ||
467 | |||
468 | return NULL; | ||
469 | } | ||
470 | |||
471 | |||
472 | /* | ||
473 | * Compatible Device IDs | ||
474 | */ | ||
475 | |||
476 | #define HEX(id,a) hex[((id)>>a) & 15] | ||
477 | #define CHAR(id,a) (0x40 + (((id)>>a) & 31)) | ||
478 | // | ||
479 | |||
480 | void pnpid32_to_pnpid(u32 id, char *str) | ||
481 | { | ||
482 | const char *hex = "0123456789abcdef"; | ||
483 | |||
484 | id = be32_to_cpu(id); | ||
485 | str[0] = CHAR(id, 26); | ||
486 | str[1] = CHAR(id, 21); | ||
487 | str[2] = CHAR(id,16); | ||
488 | str[3] = HEX(id, 12); | ||
489 | str[4] = HEX(id, 8); | ||
490 | str[5] = HEX(id, 4); | ||
491 | str[6] = HEX(id, 0); | ||
492 | str[7] = '\0'; | ||
493 | |||
494 | return; | ||
495 | } | ||
496 | // | ||
497 | #undef CHAR | ||
498 | #undef HEX | ||
499 | |||
500 | static unsigned char * | ||
501 | pnpbios_parse_compatible_ids(unsigned char *p, unsigned char *end, struct pnp_dev *dev) | ||
502 | { | ||
503 | int len, tag; | ||
504 | char id[8]; | ||
505 | struct pnp_id *dev_id; | ||
506 | |||
507 | if (!p) | ||
508 | return NULL; | ||
509 | |||
510 | while ((char *)p < (char *)end) { | ||
511 | |||
512 | /* determine the type of tag */ | ||
513 | if (p[0] & LARGE_TAG) { /* large tag */ | ||
514 | len = (p[2] << 8) | p[1]; | ||
515 | tag = p[0]; | ||
516 | } else { /* small tag */ | ||
517 | len = p[0] & 0x07; | ||
518 | tag = ((p[0]>>3) & 0x0f); | ||
519 | } | ||
520 | |||
521 | switch (tag) { | ||
522 | |||
523 | case LARGE_TAG_ANSISTR: | ||
524 | strncpy(dev->name, p + 3, len >= PNP_NAME_LEN ? PNP_NAME_LEN - 2 : len); | ||
525 | dev->name[len >= PNP_NAME_LEN ? PNP_NAME_LEN - 1 : len] = '\0'; | ||
526 | break; | ||
527 | |||
528 | case SMALL_TAG_COMPATDEVID: /* compatible ID */ | ||
529 | if (len != 4) | ||
530 | goto len_err; | ||
531 | dev_id = pnpbios_kmalloc(sizeof (struct pnp_id), GFP_KERNEL); | ||
532 | if (!dev_id) | ||
533 | return NULL; | ||
534 | memset(dev_id, 0, sizeof(struct pnp_id)); | ||
535 | pnpid32_to_pnpid(p[1] | p[2] << 8 | p[3] << 16 | p[4] << 24,id); | ||
536 | memcpy(&dev_id->id, id, 7); | ||
537 | pnp_add_id(dev_id, dev); | ||
538 | break; | ||
539 | |||
540 | case SMALL_TAG_END: | ||
541 | p = p + 2; | ||
542 | return (unsigned char *)p; | ||
543 | break; | ||
544 | |||
545 | default: /* an unkown tag */ | ||
546 | len_err: | ||
547 | printk(KERN_ERR "PnPBIOS: Unknown tag '0x%x', length '%d'.\n", tag, len); | ||
548 | break; | ||
549 | } | ||
550 | |||
551 | /* continue to the next tag */ | ||
552 | if (p[0] & LARGE_TAG) | ||
553 | p += len + 3; | ||
554 | else | ||
555 | p += len + 1; | ||
556 | } | ||
557 | |||
558 | printk(KERN_ERR "PnPBIOS: Resource structure does not contain an end tag.\n"); | ||
559 | |||
560 | return NULL; | ||
561 | } | ||
562 | |||
563 | |||
564 | /* | ||
565 | * Allocated Resource Encoding | ||
566 | */ | ||
567 | |||
568 | static void pnpbios_encode_mem(unsigned char *p, struct resource * res) | ||
569 | { | ||
570 | unsigned long base = res->start; | ||
571 | unsigned long len = res->end - res->start + 1; | ||
572 | p[4] = (base >> 8) & 0xff; | ||
573 | p[5] = ((base >> 8) >> 8) & 0xff; | ||
574 | p[6] = (base >> 8) & 0xff; | ||
575 | p[7] = ((base >> 8) >> 8) & 0xff; | ||
576 | p[10] = (len >> 8) & 0xff; | ||
577 | p[11] = ((len >> 8) >> 8) & 0xff; | ||
578 | return; | ||
579 | } | ||
580 | |||
581 | static void pnpbios_encode_mem32(unsigned char *p, struct resource * res) | ||
582 | { | ||
583 | unsigned long base = res->start; | ||
584 | unsigned long len = res->end - res->start + 1; | ||
585 | p[4] = base & 0xff; | ||
586 | p[5] = (base >> 8) & 0xff; | ||
587 | p[6] = (base >> 16) & 0xff; | ||
588 | p[7] = (base >> 24) & 0xff; | ||
589 | p[8] = base & 0xff; | ||
590 | p[9] = (base >> 8) & 0xff; | ||
591 | p[10] = (base >> 16) & 0xff; | ||
592 | p[11] = (base >> 24) & 0xff; | ||
593 | p[16] = len & 0xff; | ||
594 | p[17] = (len >> 8) & 0xff; | ||
595 | p[18] = (len >> 16) & 0xff; | ||
596 | p[19] = (len >> 24) & 0xff; | ||
597 | return; | ||
598 | } | ||
599 | |||
600 | static void pnpbios_encode_fixed_mem32(unsigned char *p, struct resource * res) | ||
601 | { unsigned long base = res->start; | ||
602 | unsigned long len = res->end - res->start + 1; | ||
603 | p[4] = base & 0xff; | ||
604 | p[5] = (base >> 8) & 0xff; | ||
605 | p[6] = (base >> 16) & 0xff; | ||
606 | p[7] = (base >> 24) & 0xff; | ||
607 | p[8] = len & 0xff; | ||
608 | p[9] = (len >> 8) & 0xff; | ||
609 | p[10] = (len >> 16) & 0xff; | ||
610 | p[11] = (len >> 24) & 0xff; | ||
611 | return; | ||
612 | } | ||
613 | |||
614 | static void pnpbios_encode_irq(unsigned char *p, struct resource * res) | ||
615 | { | ||
616 | unsigned long map = 0; | ||
617 | map = 1 << res->start; | ||
618 | p[1] = map & 0xff; | ||
619 | p[2] = (map >> 8) & 0xff; | ||
620 | return; | ||
621 | } | ||
622 | |||
623 | static void pnpbios_encode_dma(unsigned char *p, struct resource * res) | ||
624 | { | ||
625 | unsigned long map = 0; | ||
626 | map = 1 << res->start; | ||
627 | p[1] = map & 0xff; | ||
628 | return; | ||
629 | } | ||
630 | |||
631 | static void pnpbios_encode_port(unsigned char *p, struct resource * res) | ||
632 | { | ||
633 | unsigned long base = res->start; | ||
634 | unsigned long len = res->end - res->start + 1; | ||
635 | p[2] = base & 0xff; | ||
636 | p[3] = (base >> 8) & 0xff; | ||
637 | p[4] = base & 0xff; | ||
638 | p[5] = (base >> 8) & 0xff; | ||
639 | p[7] = len & 0xff; | ||
640 | return; | ||
641 | } | ||
642 | |||
643 | static void pnpbios_encode_fixed_port(unsigned char *p, struct resource * res) | ||
644 | { | ||
645 | unsigned long base = res->start; | ||
646 | unsigned long len = res->end - res->start + 1; | ||
647 | p[1] = base & 0xff; | ||
648 | p[2] = (base >> 8) & 0xff; | ||
649 | p[3] = len & 0xff; | ||
650 | return; | ||
651 | } | ||
652 | |||
653 | static unsigned char * | ||
654 | pnpbios_encode_allocated_resource_data(unsigned char * p, unsigned char * end, struct pnp_resource_table * res) | ||
655 | { | ||
656 | unsigned int len, tag; | ||
657 | int port = 0, irq = 0, dma = 0, mem = 0; | ||
658 | |||
659 | if (!p) | ||
660 | return NULL; | ||
661 | |||
662 | while ((char *)p < (char *)end) { | ||
663 | |||
664 | /* determine the type of tag */ | ||
665 | if (p[0] & LARGE_TAG) { /* large tag */ | ||
666 | len = (p[2] << 8) | p[1]; | ||
667 | tag = p[0]; | ||
668 | } else { /* small tag */ | ||
669 | len = p[0] & 0x07; | ||
670 | tag = ((p[0]>>3) & 0x0f); | ||
671 | } | ||
672 | |||
673 | switch (tag) { | ||
674 | |||
675 | case LARGE_TAG_MEM: | ||
676 | if (len != 9) | ||
677 | goto len_err; | ||
678 | pnpbios_encode_mem(p, &res->mem_resource[mem]); | ||
679 | mem++; | ||
680 | break; | ||
681 | |||
682 | case LARGE_TAG_MEM32: | ||
683 | if (len != 17) | ||
684 | goto len_err; | ||
685 | pnpbios_encode_mem32(p, &res->mem_resource[mem]); | ||
686 | mem++; | ||
687 | break; | ||
688 | |||
689 | case LARGE_TAG_FIXEDMEM32: | ||
690 | if (len != 9) | ||
691 | goto len_err; | ||
692 | pnpbios_encode_fixed_mem32(p, &res->mem_resource[mem]); | ||
693 | mem++; | ||
694 | break; | ||
695 | |||
696 | case SMALL_TAG_IRQ: | ||
697 | if (len < 2 || len > 3) | ||
698 | goto len_err; | ||
699 | pnpbios_encode_irq(p, &res->irq_resource[irq]); | ||
700 | irq++; | ||
701 | break; | ||
702 | |||
703 | case SMALL_TAG_DMA: | ||
704 | if (len != 2) | ||
705 | goto len_err; | ||
706 | pnpbios_encode_dma(p, &res->dma_resource[dma]); | ||
707 | dma++; | ||
708 | break; | ||
709 | |||
710 | case SMALL_TAG_PORT: | ||
711 | if (len != 7) | ||
712 | goto len_err; | ||
713 | pnpbios_encode_port(p, &res->port_resource[port]); | ||
714 | port++; | ||
715 | break; | ||
716 | |||
717 | case SMALL_TAG_VENDOR: | ||
718 | /* do nothing */ | ||
719 | break; | ||
720 | |||
721 | case SMALL_TAG_FIXEDPORT: | ||
722 | if (len != 3) | ||
723 | goto len_err; | ||
724 | pnpbios_encode_fixed_port(p, &res->port_resource[port]); | ||
725 | port++; | ||
726 | break; | ||
727 | |||
728 | case SMALL_TAG_END: | ||
729 | p = p + 2; | ||
730 | return (unsigned char *)p; | ||
731 | break; | ||
732 | |||
733 | default: /* an unkown tag */ | ||
734 | len_err: | ||
735 | printk(KERN_ERR "PnPBIOS: Unknown tag '0x%x', length '%d'.\n", tag, len); | ||
736 | break; | ||
737 | } | ||
738 | |||
739 | /* continue to the next tag */ | ||
740 | if (p[0] & LARGE_TAG) | ||
741 | p += len + 3; | ||
742 | else | ||
743 | p += len + 1; | ||
744 | } | ||
745 | |||
746 | printk(KERN_ERR "PnPBIOS: Resource structure does not contain an end tag.\n"); | ||
747 | |||
748 | return NULL; | ||
749 | } | ||
750 | |||
751 | |||
752 | /* | ||
753 | * Core Parsing Functions | ||
754 | */ | ||
755 | |||
756 | int | ||
757 | pnpbios_parse_data_stream(struct pnp_dev *dev, struct pnp_bios_node * node) | ||
758 | { | ||
759 | unsigned char * p = (char *)node->data; | ||
760 | unsigned char * end = (char *)(node->data + node->size); | ||
761 | p = pnpbios_parse_allocated_resource_data(p,end,&dev->res); | ||
762 | if (!p) | ||
763 | return -EIO; | ||
764 | p = pnpbios_parse_resource_option_data(p,end,dev); | ||
765 | if (!p) | ||
766 | return -EIO; | ||
767 | p = pnpbios_parse_compatible_ids(p,end,dev); | ||
768 | if (!p) | ||
769 | return -EIO; | ||
770 | return 0; | ||
771 | } | ||
772 | |||
773 | int | ||
774 | pnpbios_read_resources_from_node(struct pnp_resource_table *res, | ||
775 | struct pnp_bios_node * node) | ||
776 | { | ||
777 | unsigned char * p = (char *)node->data; | ||
778 | unsigned char * end = (char *)(node->data + node->size); | ||
779 | p = pnpbios_parse_allocated_resource_data(p,end,res); | ||
780 | if (!p) | ||
781 | return -EIO; | ||
782 | return 0; | ||
783 | } | ||
784 | |||
785 | int | ||
786 | pnpbios_write_resources_to_node(struct pnp_resource_table *res, | ||
787 | struct pnp_bios_node * node) | ||
788 | { | ||
789 | unsigned char * p = (char *)node->data; | ||
790 | unsigned char * end = (char *)(node->data + node->size); | ||
791 | p = pnpbios_encode_allocated_resource_data(p,end,res); | ||
792 | if (!p) | ||
793 | return -EIO; | ||
794 | return 0; | ||
795 | } | ||