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