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