rsparser.c 18.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7
/*
 * rsparser.c - parses and encodes pnpbios resource data streams
 */

#include <linux/ctype.h>
#include <linux/pnp.h>
#include <linux/pnpbios.h>
T
Tim Schmielau 已提交
8 9
#include <linux/string.h>
#include <linux/slab.h>
L
Linus Torvalds 已提交
10 11 12 13

#ifdef CONFIG_PCI
#include <linux/pci.h>
#else
B
Bjorn Helgaas 已提交
14 15 16
inline void pcibios_penalize_isa_irq(int irq, int active)
{
}
B
Bjorn Helgaas 已提交
17
#endif				/* CONFIG_PCI */
L
Linus Torvalds 已提交
18

19
#include "../base.h"
L
Linus Torvalds 已提交
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
#include "pnpbios.h"

/* standard resource tags */
#define SMALL_TAG_PNPVERNO		0x01
#define SMALL_TAG_LOGDEVID		0x02
#define SMALL_TAG_COMPATDEVID		0x03
#define SMALL_TAG_IRQ			0x04
#define SMALL_TAG_DMA			0x05
#define SMALL_TAG_STARTDEP		0x06
#define SMALL_TAG_ENDDEP		0x07
#define SMALL_TAG_PORT			0x08
#define SMALL_TAG_FIXEDPORT		0x09
#define SMALL_TAG_VENDOR		0x0e
#define SMALL_TAG_END			0x0f
#define LARGE_TAG			0x80
#define LARGE_TAG_MEM			0x81
#define LARGE_TAG_ANSISTR		0x82
#define LARGE_TAG_UNICODESTR		0x83
#define LARGE_TAG_VENDOR		0x84
#define LARGE_TAG_MEM32			0x85
#define LARGE_TAG_FIXEDMEM32		0x86

/*
 * Resource Data Stream Format:
 *
 * Allocated Resources (required)
 * end tag ->
 * Resource Configuration Options (optional)
 * end tag ->
 * Compitable Device IDs (optional)
 * final end tag ->
 */

/*
 * Allocated Resources
 */

57
static void pnpbios_parse_allocated_irqresource(struct pnp_dev *dev, int irq)
L
Linus Torvalds 已提交
58
{
59
	struct pnp_resource_table *res = &dev->res;
L
Linus Torvalds 已提交
60
	int i = 0;
B
Bjorn Helgaas 已提交
61

B
Bjorn Helgaas 已提交
62 63 64
	while (!(res->irq_resource[i].flags & IORESOURCE_UNSET)
	       && i < PNP_MAX_IRQ)
		i++;
L
Linus Torvalds 已提交
65
	if (i < PNP_MAX_IRQ) {
B
Bjorn Helgaas 已提交
66
		res->irq_resource[i].flags = IORESOURCE_IRQ;	// Also clears _UNSET flag
L
Linus Torvalds 已提交
67 68 69 70 71
		if (irq == -1) {
			res->irq_resource[i].flags |= IORESOURCE_DISABLED;
			return;
		}
		res->irq_resource[i].start =
B
Bjorn Helgaas 已提交
72
		    res->irq_resource[i].end = (unsigned long)irq;
D
David Shaohua Li 已提交
73
		pcibios_penalize_isa_irq(irq, 1);
L
Linus Torvalds 已提交
74 75 76
	}
}

77
static void pnpbios_parse_allocated_dmaresource(struct pnp_dev *dev, int dma)
L
Linus Torvalds 已提交
78
{
79
	struct pnp_resource_table *res = &dev->res;
L
Linus Torvalds 已提交
80
	int i = 0;
B
Bjorn Helgaas 已提交
81

82
	while (i < PNP_MAX_DMA &&
B
Bjorn Helgaas 已提交
83
	       !(res->dma_resource[i].flags & IORESOURCE_UNSET))
84
		i++;
L
Linus Torvalds 已提交
85
	if (i < PNP_MAX_DMA) {
B
Bjorn Helgaas 已提交
86
		res->dma_resource[i].flags = IORESOURCE_DMA;	// Also clears _UNSET flag
L
Linus Torvalds 已提交
87 88 89 90 91
		if (dma == -1) {
			res->dma_resource[i].flags |= IORESOURCE_DISABLED;
			return;
		}
		res->dma_resource[i].start =
B
Bjorn Helgaas 已提交
92
		    res->dma_resource[i].end = (unsigned long)dma;
L
Linus Torvalds 已提交
93 94 95
	}
}

96
static void pnpbios_parse_allocated_ioresource(struct pnp_dev *dev,
B
Bjorn Helgaas 已提交
97
					       int io, int len)
L
Linus Torvalds 已提交
98
{
99
	struct pnp_resource_table *res = &dev->res;
L
Linus Torvalds 已提交
100
	int i = 0;
B
Bjorn Helgaas 已提交
101

B
Bjorn Helgaas 已提交
102 103 104
	while (!(res->port_resource[i].flags & IORESOURCE_UNSET)
	       && i < PNP_MAX_PORT)
		i++;
L
Linus Torvalds 已提交
105
	if (i < PNP_MAX_PORT) {
B
Bjorn Helgaas 已提交
106 107
		res->port_resource[i].flags = IORESOURCE_IO;	// Also clears _UNSET flag
		if (len <= 0 || (io + len - 1) >= 0x10003) {
L
Linus Torvalds 已提交
108 109 110
			res->port_resource[i].flags |= IORESOURCE_DISABLED;
			return;
		}
B
Bjorn Helgaas 已提交
111
		res->port_resource[i].start = (unsigned long)io;
L
Linus Torvalds 已提交
112 113 114 115
		res->port_resource[i].end = (unsigned long)(io + len - 1);
	}
}

116
static void pnpbios_parse_allocated_memresource(struct pnp_dev *dev,
B
Bjorn Helgaas 已提交
117
						int mem, int len)
L
Linus Torvalds 已提交
118
{
119
	struct pnp_resource_table *res = &dev->res;
L
Linus Torvalds 已提交
120
	int i = 0;
B
Bjorn Helgaas 已提交
121

B
Bjorn Helgaas 已提交
122 123 124
	while (!(res->mem_resource[i].flags & IORESOURCE_UNSET)
	       && i < PNP_MAX_MEM)
		i++;
L
Linus Torvalds 已提交
125
	if (i < PNP_MAX_MEM) {
B
Bjorn Helgaas 已提交
126
		res->mem_resource[i].flags = IORESOURCE_MEM;	// Also clears _UNSET flag
L
Linus Torvalds 已提交
127 128 129 130
		if (len <= 0) {
			res->mem_resource[i].flags |= IORESOURCE_DISABLED;
			return;
		}
B
Bjorn Helgaas 已提交
131
		res->mem_resource[i].start = (unsigned long)mem;
L
Linus Torvalds 已提交
132 133 134 135
		res->mem_resource[i].end = (unsigned long)(mem + len - 1);
	}
}

136 137 138
static unsigned char *pnpbios_parse_allocated_resource_data(struct pnp_dev *dev,
							    unsigned char *p,
							    unsigned char *end)
L
Linus Torvalds 已提交
139 140 141 142 143 144 145
{
	unsigned int len, tag;
	int io, size, mask, i;

	if (!p)
		return NULL;

146 147
	dev_dbg(&dev->dev, "parse allocated resources\n");

L
Linus Torvalds 已提交
148
	/* Blank the resource table values */
149
	pnp_init_resource_table(&dev->res);
L
Linus Torvalds 已提交
150 151 152 153

	while ((char *)p < (char *)end) {

		/* determine the type of tag */
B
Bjorn Helgaas 已提交
154
		if (p[0] & LARGE_TAG) {	/* large tag */
L
Linus Torvalds 已提交
155 156
			len = (p[2] << 8) | p[1];
			tag = p[0];
B
Bjorn Helgaas 已提交
157
		} else {	/* small tag */
L
Linus Torvalds 已提交
158
			len = p[0] & 0x07;
B
Bjorn Helgaas 已提交
159
			tag = ((p[0] >> 3) & 0x0f);
L
Linus Torvalds 已提交
160 161 162 163 164 165 166
		}

		switch (tag) {

		case LARGE_TAG_MEM:
			if (len != 9)
				goto len_err;
B
Bjorn Helgaas 已提交
167 168
			io = *(short *)&p[4];
			size = *(short *)&p[10];
169
			pnpbios_parse_allocated_memresource(dev, io, size);
L
Linus Torvalds 已提交
170 171 172 173 174 175 176 177 178 179 180 181 182
			break;

		case LARGE_TAG_ANSISTR:
			/* ignore this for now */
			break;

		case LARGE_TAG_VENDOR:
			/* do nothing */
			break;

		case LARGE_TAG_MEM32:
			if (len != 17)
				goto len_err;
B
Bjorn Helgaas 已提交
183 184
			io = *(int *)&p[4];
			size = *(int *)&p[16];
185
			pnpbios_parse_allocated_memresource(dev, io, size);
L
Linus Torvalds 已提交
186 187 188 189 190
			break;

		case LARGE_TAG_FIXEDMEM32:
			if (len != 9)
				goto len_err;
B
Bjorn Helgaas 已提交
191 192
			io = *(int *)&p[4];
			size = *(int *)&p[8];
193
			pnpbios_parse_allocated_memresource(dev, io, size);
L
Linus Torvalds 已提交
194 195 196 197 198 199
			break;

		case SMALL_TAG_IRQ:
			if (len < 2 || len > 3)
				goto len_err;
			io = -1;
B
Bjorn Helgaas 已提交
200 201 202 203
			mask = p[1] + p[2] * 256;
			for (i = 0; i < 16; i++, mask = mask >> 1)
				if (mask & 0x01)
					io = i;
204
			pnpbios_parse_allocated_irqresource(dev, io);
L
Linus Torvalds 已提交
205 206 207 208 209 210 211
			break;

		case SMALL_TAG_DMA:
			if (len != 2)
				goto len_err;
			io = -1;
			mask = p[1];
B
Bjorn Helgaas 已提交
212 213 214
			for (i = 0; i < 8; i++, mask = mask >> 1)
				if (mask & 0x01)
					io = i;
215
			pnpbios_parse_allocated_dmaresource(dev, io);
L
Linus Torvalds 已提交
216 217 218 219 220
			break;

		case SMALL_TAG_PORT:
			if (len != 7)
				goto len_err;
B
Bjorn Helgaas 已提交
221
			io = p[2] + p[3] * 256;
L
Linus Torvalds 已提交
222
			size = p[7];
223
			pnpbios_parse_allocated_ioresource(dev, io, size);
L
Linus Torvalds 已提交
224 225 226 227 228 229 230 231 232 233 234
			break;

		case SMALL_TAG_VENDOR:
			/* do nothing */
			break;

		case SMALL_TAG_FIXEDPORT:
			if (len != 3)
				goto len_err;
			io = p[1] + p[2] * 256;
			size = p[3];
235
			pnpbios_parse_allocated_ioresource(dev, io, size);
L
Linus Torvalds 已提交
236 237 238 239
			break;

		case SMALL_TAG_END:
			p = p + 2;
B
Bjorn Helgaas 已提交
240
			return (unsigned char *)p;
L
Linus Torvalds 已提交
241 242
			break;

B
Bjorn Helgaas 已提交
243
		default:	/* an unkown tag */
B
Bjorn Helgaas 已提交
244
len_err:
B
Bjorn Helgaas 已提交
245 246 247
			printk(KERN_ERR
			       "PnPBIOS: Unknown tag '0x%x', length '%d'.\n",
			       tag, len);
L
Linus Torvalds 已提交
248 249 250 251 252 253 254 255 256 257
			break;
		}

		/* continue to the next tag */
		if (p[0] & LARGE_TAG)
			p += len + 3;
		else
			p += len + 1;
	}

B
Bjorn Helgaas 已提交
258 259
	printk(KERN_ERR
	       "PnPBIOS: Resource structure does not contain an end tag.\n");
L
Linus Torvalds 已提交
260 261 262 263 264 265 266 267

	return NULL;
}

/*
 * Resource Configuration Options
 */

268 269
static __init void pnpbios_parse_mem_option(struct pnp_dev *dev,
					    unsigned char *p, int size,
270
					    struct pnp_option *option)
L
Linus Torvalds 已提交
271
{
B
Bjorn Helgaas 已提交
272
	struct pnp_mem *mem;
B
Bjorn Helgaas 已提交
273

274
	mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
L
Linus Torvalds 已提交
275 276 277 278 279 280 281
	if (!mem)
		return;
	mem->min = ((p[5] << 8) | p[4]) << 8;
	mem->max = ((p[7] << 8) | p[6]) << 8;
	mem->align = (p[9] << 8) | p[8];
	mem->size = ((p[11] << 8) | p[10]) << 8;
	mem->flags = p[3];
282
	pnp_register_mem_resource(dev, option, mem);
L
Linus Torvalds 已提交
283 284
}

285 286
static __init void pnpbios_parse_mem32_option(struct pnp_dev *dev,
					      unsigned char *p, int size,
287
					      struct pnp_option *option)
L
Linus Torvalds 已提交
288
{
B
Bjorn Helgaas 已提交
289
	struct pnp_mem *mem;
B
Bjorn Helgaas 已提交
290

291
	mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
L
Linus Torvalds 已提交
292 293 294 295 296 297 298
	if (!mem)
		return;
	mem->min = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4];
	mem->max = (p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8];
	mem->align = (p[15] << 24) | (p[14] << 16) | (p[13] << 8) | p[12];
	mem->size = (p[19] << 24) | (p[18] << 16) | (p[17] << 8) | p[16];
	mem->flags = p[3];
299
	pnp_register_mem_resource(dev, option, mem);
L
Linus Torvalds 已提交
300 301
}

302 303
static __init void pnpbios_parse_fixed_mem32_option(struct pnp_dev *dev,
						    unsigned char *p, int size,
304
						    struct pnp_option *option)
L
Linus Torvalds 已提交
305
{
B
Bjorn Helgaas 已提交
306
	struct pnp_mem *mem;
B
Bjorn Helgaas 已提交
307

308
	mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
L
Linus Torvalds 已提交
309 310 311 312 313 314
	if (!mem)
		return;
	mem->min = mem->max = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4];
	mem->size = (p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8];
	mem->align = 0;
	mem->flags = p[3];
315
	pnp_register_mem_resource(dev, option, mem);
L
Linus Torvalds 已提交
316 317
}

318 319 320
static __init void pnpbios_parse_irq_option(struct pnp_dev *dev,
					    unsigned char *p, int size,
					    struct pnp_option *option)
L
Linus Torvalds 已提交
321
{
B
Bjorn Helgaas 已提交
322
	struct pnp_irq *irq;
L
Linus Torvalds 已提交
323 324
	unsigned long bits;

325
	irq = kzalloc(sizeof(struct pnp_irq), GFP_KERNEL);
L
Linus Torvalds 已提交
326 327 328 329 330 331 332 333
	if (!irq)
		return;
	bits = (p[2] << 8) | p[1];
	bitmap_copy(irq->map, &bits, 16);
	if (size > 2)
		irq->flags = p[3];
	else
		irq->flags = IORESOURCE_IRQ_HIGHEDGE;
334
	pnp_register_irq_resource(dev, option, irq);
L
Linus Torvalds 已提交
335 336
}

337 338 339
static __init void pnpbios_parse_dma_option(struct pnp_dev *dev,
					    unsigned char *p, int size,
					    struct pnp_option *option)
L
Linus Torvalds 已提交
340
{
B
Bjorn Helgaas 已提交
341
	struct pnp_dma *dma;
B
Bjorn Helgaas 已提交
342

343
	dma = kzalloc(sizeof(struct pnp_dma), GFP_KERNEL);
L
Linus Torvalds 已提交
344 345 346 347
	if (!dma)
		return;
	dma->map = p[1];
	dma->flags = p[2];
348
	pnp_register_dma_resource(dev, option, dma);
L
Linus Torvalds 已提交
349 350
}

351 352
static __init void pnpbios_parse_port_option(struct pnp_dev *dev,
					     unsigned char *p, int size,
353
					     struct pnp_option *option)
L
Linus Torvalds 已提交
354
{
B
Bjorn Helgaas 已提交
355
	struct pnp_port *port;
B
Bjorn Helgaas 已提交
356

357
	port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL);
L
Linus Torvalds 已提交
358 359 360 361 362 363 364
	if (!port)
		return;
	port->min = (p[3] << 8) | p[2];
	port->max = (p[5] << 8) | p[4];
	port->align = p[6];
	port->size = p[7];
	port->flags = p[1] ? PNP_PORT_FLAG_16BITADDR : 0;
365
	pnp_register_port_resource(dev, option, port);
L
Linus Torvalds 已提交
366 367
}

368 369
static __init void pnpbios_parse_fixed_port_option(struct pnp_dev *dev,
						   unsigned char *p, int size,
370
						   struct pnp_option *option)
L
Linus Torvalds 已提交
371
{
B
Bjorn Helgaas 已提交
372
	struct pnp_port *port;
B
Bjorn Helgaas 已提交
373

374
	port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL);
L
Linus Torvalds 已提交
375 376 377 378 379 380
	if (!port)
		return;
	port->min = port->max = (p[2] << 8) | p[1];
	port->size = p[3];
	port->align = 0;
	port->flags = PNP_PORT_FLAG_FIXED;
381
	pnp_register_port_resource(dev, option, port);
L
Linus Torvalds 已提交
382 383
}

384 385 386
static __init unsigned char *
pnpbios_parse_resource_option_data(unsigned char *p, unsigned char *end,
					struct pnp_dev *dev)
L
Linus Torvalds 已提交
387 388 389 390 391 392 393 394
{
	unsigned int len, tag;
	int priority = 0;
	struct pnp_option *option, *option_independent;

	if (!p)
		return NULL;

395 396
	dev_dbg(&dev->dev, "parse resource options\n");

L
Linus Torvalds 已提交
397 398 399 400 401 402 403
	option_independent = option = pnp_register_independent_option(dev);
	if (!option)
		return NULL;

	while ((char *)p < (char *)end) {

		/* determine the type of tag */
B
Bjorn Helgaas 已提交
404
		if (p[0] & LARGE_TAG) {	/* large tag */
L
Linus Torvalds 已提交
405 406
			len = (p[2] << 8) | p[1];
			tag = p[0];
B
Bjorn Helgaas 已提交
407
		} else {	/* small tag */
L
Linus Torvalds 已提交
408
			len = p[0] & 0x07;
B
Bjorn Helgaas 已提交
409
			tag = ((p[0] >> 3) & 0x0f);
L
Linus Torvalds 已提交
410 411 412 413 414 415 416
		}

		switch (tag) {

		case LARGE_TAG_MEM:
			if (len != 9)
				goto len_err;
417
			pnpbios_parse_mem_option(dev, p, len, option);
L
Linus Torvalds 已提交
418 419 420 421 422
			break;

		case LARGE_TAG_MEM32:
			if (len != 17)
				goto len_err;
423
			pnpbios_parse_mem32_option(dev, p, len, option);
L
Linus Torvalds 已提交
424 425 426 427 428
			break;

		case LARGE_TAG_FIXEDMEM32:
			if (len != 9)
				goto len_err;
429
			pnpbios_parse_fixed_mem32_option(dev, p, len, option);
L
Linus Torvalds 已提交
430 431 432 433 434
			break;

		case SMALL_TAG_IRQ:
			if (len < 2 || len > 3)
				goto len_err;
435
			pnpbios_parse_irq_option(dev, p, len, option);
L
Linus Torvalds 已提交
436 437 438 439 440
			break;

		case SMALL_TAG_DMA:
			if (len != 2)
				goto len_err;
441
			pnpbios_parse_dma_option(dev, p, len, option);
L
Linus Torvalds 已提交
442 443 444 445 446
			break;

		case SMALL_TAG_PORT:
			if (len != 7)
				goto len_err;
447
			pnpbios_parse_port_option(dev, p, len, option);
L
Linus Torvalds 已提交
448 449 450 451 452 453 454 455 456
			break;

		case SMALL_TAG_VENDOR:
			/* do nothing */
			break;

		case SMALL_TAG_FIXEDPORT:
			if (len != 3)
				goto len_err;
457
			pnpbios_parse_fixed_port_option(dev, p, len, option);
L
Linus Torvalds 已提交
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
			break;

		case SMALL_TAG_STARTDEP:
			if (len > 1)
				goto len_err;
			priority = 0x100 | PNP_RES_PRIORITY_ACCEPTABLE;
			if (len > 0)
				priority = 0x100 | p[1];
			option = pnp_register_dependent_option(dev, priority);
			if (!option)
				return NULL;
			break;

		case SMALL_TAG_ENDDEP:
			if (len != 0)
				goto len_err;
			if (option_independent == option)
B
Bjorn Helgaas 已提交
475 476
				printk(KERN_WARNING
				       "PnPBIOS: Missing SMALL_TAG_STARTDEP tag\n");
L
Linus Torvalds 已提交
477
			option = option_independent;
478
			dev_dbg(&dev->dev, "end dependent options\n");
L
Linus Torvalds 已提交
479 480 481
			break;

		case SMALL_TAG_END:
B
Bjorn Helgaas 已提交
482
			return p + 2;
L
Linus Torvalds 已提交
483

B
Bjorn Helgaas 已提交
484
		default:	/* an unkown tag */
B
Bjorn Helgaas 已提交
485
len_err:
B
Bjorn Helgaas 已提交
486 487 488
			printk(KERN_ERR
			       "PnPBIOS: Unknown tag '0x%x', length '%d'.\n",
			       tag, len);
L
Linus Torvalds 已提交
489 490 491 492 493 494 495 496 497 498
			break;
		}

		/* continue to the next tag */
		if (p[0] & LARGE_TAG)
			p += len + 3;
		else
			p += len + 1;
	}

B
Bjorn Helgaas 已提交
499 500
	printk(KERN_ERR
	       "PnPBIOS: Resource structure does not contain an end tag.\n");
L
Linus Torvalds 已提交
501 502 503 504 505 506 507 508

	return NULL;
}

/*
 * Compatible Device IDs
 */

B
Bjorn Helgaas 已提交
509 510 511
static unsigned char *pnpbios_parse_compatible_ids(unsigned char *p,
						   unsigned char *end,
						   struct pnp_dev *dev)
L
Linus Torvalds 已提交
512 513
{
	int len, tag;
B
Bjorn Helgaas 已提交
514
	u32 eisa_id;
L
Linus Torvalds 已提交
515 516 517 518 519 520 521 522 523
	char id[8];
	struct pnp_id *dev_id;

	if (!p)
		return NULL;

	while ((char *)p < (char *)end) {

		/* determine the type of tag */
B
Bjorn Helgaas 已提交
524
		if (p[0] & LARGE_TAG) {	/* large tag */
L
Linus Torvalds 已提交
525 526
			len = (p[2] << 8) | p[1];
			tag = p[0];
B
Bjorn Helgaas 已提交
527
		} else {	/* small tag */
L
Linus Torvalds 已提交
528
			len = p[0] & 0x07;
B
Bjorn Helgaas 已提交
529
			tag = ((p[0] >> 3) & 0x0f);
L
Linus Torvalds 已提交
530 531 532 533 534
		}

		switch (tag) {

		case LARGE_TAG_ANSISTR:
B
Bjorn Helgaas 已提交
535 536 537 538
			strncpy(dev->name, p + 3,
				len >= PNP_NAME_LEN ? PNP_NAME_LEN - 2 : len);
			dev->name[len >=
				  PNP_NAME_LEN ? PNP_NAME_LEN - 1 : len] = '\0';
L
Linus Torvalds 已提交
539 540
			break;

B
Bjorn Helgaas 已提交
541
		case SMALL_TAG_COMPATDEVID:	/* compatible ID */
L
Linus Torvalds 已提交
542 543
			if (len != 4)
				goto len_err;
B
Bjorn Helgaas 已提交
544 545
			eisa_id = p[1] | p[2] << 8 | p[3] << 16 | p[4] << 24;
			pnp_eisa_id_to_string(eisa_id & PNP_EISA_ID_MASK, id);
546 547 548
			dev_id = pnp_add_id(dev, id);
			if (!dev_id)
				return NULL;
L
Linus Torvalds 已提交
549 550 551 552
			break;

		case SMALL_TAG_END:
			p = p + 2;
B
Bjorn Helgaas 已提交
553
			return (unsigned char *)p;
L
Linus Torvalds 已提交
554 555
			break;

B
Bjorn Helgaas 已提交
556
		default:	/* an unkown tag */
B
Bjorn Helgaas 已提交
557
len_err:
B
Bjorn Helgaas 已提交
558 559 560
			printk(KERN_ERR
			       "PnPBIOS: Unknown tag '0x%x', length '%d'.\n",
			       tag, len);
L
Linus Torvalds 已提交
561 562 563 564 565 566 567 568 569 570
			break;
		}

		/* continue to the next tag */
		if (p[0] & LARGE_TAG)
			p += len + 3;
		else
			p += len + 1;
	}

B
Bjorn Helgaas 已提交
571 572
	printk(KERN_ERR
	       "PnPBIOS: Resource structure does not contain an end tag.\n");
L
Linus Torvalds 已提交
573 574 575 576 577 578 579 580

	return NULL;
}

/*
 * Allocated Resource Encoding
 */

581 582
static void pnpbios_encode_mem(struct pnp_dev *dev, unsigned char *p,
			       struct resource *res)
L
Linus Torvalds 已提交
583 584 585
{
	unsigned long base = res->start;
	unsigned long len = res->end - res->start + 1;
B
Bjorn Helgaas 已提交
586

L
Linus Torvalds 已提交
587 588 589 590 591 592
	p[4] = (base >> 8) & 0xff;
	p[5] = ((base >> 8) >> 8) & 0xff;
	p[6] = (base >> 8) & 0xff;
	p[7] = ((base >> 8) >> 8) & 0xff;
	p[10] = (len >> 8) & 0xff;
	p[11] = ((len >> 8) >> 8) & 0xff;
593 594 595

	dev_dbg(&dev->dev, "  encode mem %#llx-%#llx\n",
		(unsigned long long) res->start, (unsigned long long) res->end);
L
Linus Torvalds 已提交
596 597
}

598 599
static void pnpbios_encode_mem32(struct pnp_dev *dev, unsigned char *p,
				 struct resource *res)
L
Linus Torvalds 已提交
600 601 602
{
	unsigned long base = res->start;
	unsigned long len = res->end - res->start + 1;
B
Bjorn Helgaas 已提交
603

L
Linus Torvalds 已提交
604 605 606 607 608 609 610 611 612 613 614 615
	p[4] = base & 0xff;
	p[5] = (base >> 8) & 0xff;
	p[6] = (base >> 16) & 0xff;
	p[7] = (base >> 24) & 0xff;
	p[8] = base & 0xff;
	p[9] = (base >> 8) & 0xff;
	p[10] = (base >> 16) & 0xff;
	p[11] = (base >> 24) & 0xff;
	p[16] = len & 0xff;
	p[17] = (len >> 8) & 0xff;
	p[18] = (len >> 16) & 0xff;
	p[19] = (len >> 24) & 0xff;
616 617 618

	dev_dbg(&dev->dev, "  encode mem32 %#llx-%#llx\n",
		(unsigned long long) res->start, (unsigned long long) res->end);
L
Linus Torvalds 已提交
619 620
}

621 622
static void pnpbios_encode_fixed_mem32(struct pnp_dev *dev, unsigned char *p,
				       struct resource *res)
B
Bjorn Helgaas 已提交
623 624
{
	unsigned long base = res->start;
L
Linus Torvalds 已提交
625
	unsigned long len = res->end - res->start + 1;
B
Bjorn Helgaas 已提交
626

L
Linus Torvalds 已提交
627 628 629 630 631 632 633 634
	p[4] = base & 0xff;
	p[5] = (base >> 8) & 0xff;
	p[6] = (base >> 16) & 0xff;
	p[7] = (base >> 24) & 0xff;
	p[8] = len & 0xff;
	p[9] = (len >> 8) & 0xff;
	p[10] = (len >> 16) & 0xff;
	p[11] = (len >> 24) & 0xff;
635 636 637

	dev_dbg(&dev->dev, "  encode fixed_mem32 %#llx-%#llx\n",
		(unsigned long long) res->start, (unsigned long long) res->end);
L
Linus Torvalds 已提交
638 639
}

640 641
static void pnpbios_encode_irq(struct pnp_dev *dev, unsigned char *p,
			       struct resource *res)
L
Linus Torvalds 已提交
642 643
{
	unsigned long map = 0;
B
Bjorn Helgaas 已提交
644

L
Linus Torvalds 已提交
645 646 647
	map = 1 << res->start;
	p[1] = map & 0xff;
	p[2] = (map >> 8) & 0xff;
648 649

	dev_dbg(&dev->dev, "  encode irq %d\n", res->start);
L
Linus Torvalds 已提交
650 651
}

652 653
static void pnpbios_encode_dma(struct pnp_dev *dev, unsigned char *p,
			       struct resource *res)
L
Linus Torvalds 已提交
654 655
{
	unsigned long map = 0;
B
Bjorn Helgaas 已提交
656

L
Linus Torvalds 已提交
657 658
	map = 1 << res->start;
	p[1] = map & 0xff;
659 660

	dev_dbg(&dev->dev, "  encode dma %d\n", res->start);
L
Linus Torvalds 已提交
661 662
}

663 664
static void pnpbios_encode_port(struct pnp_dev *dev, unsigned char *p,
				struct resource *res)
L
Linus Torvalds 已提交
665 666 667
{
	unsigned long base = res->start;
	unsigned long len = res->end - res->start + 1;
B
Bjorn Helgaas 已提交
668

L
Linus Torvalds 已提交
669 670 671 672 673
	p[2] = base & 0xff;
	p[3] = (base >> 8) & 0xff;
	p[4] = base & 0xff;
	p[5] = (base >> 8) & 0xff;
	p[7] = len & 0xff;
674 675 676

	dev_dbg(&dev->dev, "  encode io %#llx-%#llx\n",
		(unsigned long long) res->start, (unsigned long long) res->end);
L
Linus Torvalds 已提交
677 678
}

679 680
static void pnpbios_encode_fixed_port(struct pnp_dev *dev, unsigned char *p,
				      struct resource *res)
L
Linus Torvalds 已提交
681 682 683
{
	unsigned long base = res->start;
	unsigned long len = res->end - res->start + 1;
B
Bjorn Helgaas 已提交
684

L
Linus Torvalds 已提交
685 686 687
	p[1] = base & 0xff;
	p[2] = (base >> 8) & 0xff;
	p[3] = len & 0xff;
688 689 690

	dev_dbg(&dev->dev, "  encode fixed_io %#llx-%#llx\n",
		(unsigned long long) res->start, (unsigned long long) res->end);
L
Linus Torvalds 已提交
691 692
}

693 694 695 696
static unsigned char *pnpbios_encode_allocated_resource_data(struct pnp_dev
								*dev,
							     unsigned char *p,
							     unsigned char *end)
L
Linus Torvalds 已提交
697
{
698
	struct pnp_resource_table *res = &dev->res;
L
Linus Torvalds 已提交
699 700 701 702 703 704 705 706 707
	unsigned int len, tag;
	int port = 0, irq = 0, dma = 0, mem = 0;

	if (!p)
		return NULL;

	while ((char *)p < (char *)end) {

		/* determine the type of tag */
B
Bjorn Helgaas 已提交
708
		if (p[0] & LARGE_TAG) {	/* large tag */
L
Linus Torvalds 已提交
709 710
			len = (p[2] << 8) | p[1];
			tag = p[0];
B
Bjorn Helgaas 已提交
711
		} else {	/* small tag */
L
Linus Torvalds 已提交
712
			len = p[0] & 0x07;
B
Bjorn Helgaas 已提交
713
			tag = ((p[0] >> 3) & 0x0f);
L
Linus Torvalds 已提交
714 715 716 717 718 719 720
		}

		switch (tag) {

		case LARGE_TAG_MEM:
			if (len != 9)
				goto len_err;
721
			pnpbios_encode_mem(dev, p, &res->mem_resource[mem]);
L
Linus Torvalds 已提交
722 723 724 725 726 727
			mem++;
			break;

		case LARGE_TAG_MEM32:
			if (len != 17)
				goto len_err;
728
			pnpbios_encode_mem32(dev, p, &res->mem_resource[mem]);
L
Linus Torvalds 已提交
729 730 731 732 733 734
			mem++;
			break;

		case LARGE_TAG_FIXEDMEM32:
			if (len != 9)
				goto len_err;
735
			pnpbios_encode_fixed_mem32(dev, p, &res->mem_resource[mem]);
L
Linus Torvalds 已提交
736 737 738 739 740 741
			mem++;
			break;

		case SMALL_TAG_IRQ:
			if (len < 2 || len > 3)
				goto len_err;
742
			pnpbios_encode_irq(dev, p, &res->irq_resource[irq]);
L
Linus Torvalds 已提交
743 744 745 746 747 748
			irq++;
			break;

		case SMALL_TAG_DMA:
			if (len != 2)
				goto len_err;
749
			pnpbios_encode_dma(dev, p, &res->dma_resource[dma]);
L
Linus Torvalds 已提交
750 751 752 753 754 755
			dma++;
			break;

		case SMALL_TAG_PORT:
			if (len != 7)
				goto len_err;
756
			pnpbios_encode_port(dev, p, &res->port_resource[port]);
L
Linus Torvalds 已提交
757 758 759 760 761 762 763 764 765 766
			port++;
			break;

		case SMALL_TAG_VENDOR:
			/* do nothing */
			break;

		case SMALL_TAG_FIXEDPORT:
			if (len != 3)
				goto len_err;
767
			pnpbios_encode_fixed_port(dev, p, &res->port_resource[port]);
L
Linus Torvalds 已提交
768 769 770 771 772
			port++;
			break;

		case SMALL_TAG_END:
			p = p + 2;
B
Bjorn Helgaas 已提交
773
			return (unsigned char *)p;
L
Linus Torvalds 已提交
774 775
			break;

B
Bjorn Helgaas 已提交
776
		default:	/* an unkown tag */
B
Bjorn Helgaas 已提交
777
len_err:
B
Bjorn Helgaas 已提交
778 779 780
			printk(KERN_ERR
			       "PnPBIOS: Unknown tag '0x%x', length '%d'.\n",
			       tag, len);
L
Linus Torvalds 已提交
781 782 783 784 785 786 787 788 789 790
			break;
		}

		/* continue to the next tag */
		if (p[0] & LARGE_TAG)
			p += len + 3;
		else
			p += len + 1;
	}

B
Bjorn Helgaas 已提交
791 792
	printk(KERN_ERR
	       "PnPBIOS: Resource structure does not contain an end tag.\n");
L
Linus Torvalds 已提交
793 794 795 796 797 798 799 800

	return NULL;
}

/*
 * Core Parsing Functions
 */

801 802
int __init pnpbios_parse_data_stream(struct pnp_dev *dev,
					struct pnp_bios_node *node)
L
Linus Torvalds 已提交
803
{
B
Bjorn Helgaas 已提交
804 805
	unsigned char *p = (char *)node->data;
	unsigned char *end = (char *)(node->data + node->size);
B
Bjorn Helgaas 已提交
806

807
	p = pnpbios_parse_allocated_resource_data(dev, p, end);
L
Linus Torvalds 已提交
808 809
	if (!p)
		return -EIO;
B
Bjorn Helgaas 已提交
810
	p = pnpbios_parse_resource_option_data(p, end, dev);
L
Linus Torvalds 已提交
811 812
	if (!p)
		return -EIO;
B
Bjorn Helgaas 已提交
813
	p = pnpbios_parse_compatible_ids(p, end, dev);
L
Linus Torvalds 已提交
814 815 816 817 818
	if (!p)
		return -EIO;
	return 0;
}

819
int pnpbios_read_resources_from_node(struct pnp_dev *dev,
B
Bjorn Helgaas 已提交
820
				     struct pnp_bios_node *node)
L
Linus Torvalds 已提交
821
{
B
Bjorn Helgaas 已提交
822 823
	unsigned char *p = (char *)node->data;
	unsigned char *end = (char *)(node->data + node->size);
B
Bjorn Helgaas 已提交
824

825
	p = pnpbios_parse_allocated_resource_data(dev, p, end);
L
Linus Torvalds 已提交
826 827 828 829 830
	if (!p)
		return -EIO;
	return 0;
}

831
int pnpbios_write_resources_to_node(struct pnp_dev *dev,
B
Bjorn Helgaas 已提交
832
				    struct pnp_bios_node *node)
L
Linus Torvalds 已提交
833
{
B
Bjorn Helgaas 已提交
834 835
	unsigned char *p = (char *)node->data;
	unsigned char *end = (char *)(node->data + node->size);
B
Bjorn Helgaas 已提交
836

837
	p = pnpbios_encode_allocated_resource_data(dev, p, end);
L
Linus Torvalds 已提交
838 839 840 841
	if (!p)
		return -EIO;
	return 0;
}