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