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

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

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

#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
 */

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

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

static void
B
Bjorn Helgaas 已提交
95 96
pnpbios_parse_allocated_ioresource(struct pnp_resource_table *res, int io,
				   int len)
L
Linus Torvalds 已提交
97 98
{
	int i = 0;
B
Bjorn Helgaas 已提交
99 100 101
	while (!(res->port_resource[i].flags & IORESOURCE_UNSET)
	       && i < PNP_MAX_PORT)
		i++;
L
Linus Torvalds 已提交
102
	if (i < PNP_MAX_PORT) {
B
Bjorn Helgaas 已提交
103 104
		res->port_resource[i].flags = IORESOURCE_IO;	// Also clears _UNSET flag
		if (len <= 0 || (io + len - 1) >= 0x10003) {
L
Linus Torvalds 已提交
105 106 107
			res->port_resource[i].flags |= IORESOURCE_DISABLED;
			return;
		}
B
Bjorn Helgaas 已提交
108
		res->port_resource[i].start = (unsigned long)io;
L
Linus Torvalds 已提交
109 110 111 112 113
		res->port_resource[i].end = (unsigned long)(io + len - 1);
	}
}

static void
B
Bjorn Helgaas 已提交
114 115
pnpbios_parse_allocated_memresource(struct pnp_resource_table *res, int mem,
				    int len)
L
Linus Torvalds 已提交
116 117
{
	int i = 0;
B
Bjorn Helgaas 已提交
118 119 120
	while (!(res->mem_resource[i].flags & IORESOURCE_UNSET)
	       && i < PNP_MAX_MEM)
		i++;
L
Linus Torvalds 已提交
121
	if (i < PNP_MAX_MEM) {
B
Bjorn Helgaas 已提交
122
		res->mem_resource[i].flags = IORESOURCE_MEM;	// Also clears _UNSET flag
L
Linus Torvalds 已提交
123 124 125 126
		if (len <= 0) {
			res->mem_resource[i].flags |= IORESOURCE_DISABLED;
			return;
		}
B
Bjorn Helgaas 已提交
127
		res->mem_resource[i].start = (unsigned long)mem;
L
Linus Torvalds 已提交
128 129 130 131
		res->mem_resource[i].end = (unsigned long)(mem + len - 1);
	}
}

B
Bjorn Helgaas 已提交
132 133 134 135 136
static unsigned char *pnpbios_parse_allocated_resource_data(unsigned char *p,
							    unsigned char *end,
							    struct
							    pnp_resource_table
							    *res)
L
Linus Torvalds 已提交
137 138 139 140 141 142 143 144 145 146 147 148 149
{
	unsigned int len, tag;
	int io, size, mask, i;

	if (!p)
		return NULL;

	/* Blank the resource table values */
	pnp_init_resource_table(res);

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

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

		switch (tag) {

		case LARGE_TAG_MEM:
			if (len != 9)
				goto len_err;
B
Bjorn Helgaas 已提交
163 164
			io = *(short *)&p[4];
			size = *(short *)&p[10];
L
Linus Torvalds 已提交
165 166 167 168 169 170 171 172 173 174 175 176 177 178
			pnpbios_parse_allocated_memresource(res, io, size);
			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 已提交
179 180
			io = *(int *)&p[4];
			size = *(int *)&p[16];
L
Linus Torvalds 已提交
181 182 183 184 185 186
			pnpbios_parse_allocated_memresource(res, io, size);
			break;

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

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

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

		case SMALL_TAG_PORT:
			if (len != 7)
				goto len_err;
B
Bjorn Helgaas 已提交
217
			io = p[2] + p[3] * 256;
L
Linus Torvalds 已提交
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
			size = p[7];
			pnpbios_parse_allocated_ioresource(res, io, size);
			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];
			pnpbios_parse_allocated_ioresource(res, io, size);
			break;

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

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

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

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

	return NULL;
}

/*
 * Resource Configuration Options
 */

static void
pnpbios_parse_mem_option(unsigned char *p, int size, struct pnp_option *option)
{
B
Bjorn Helgaas 已提交
267
	struct pnp_mem *mem;
268
	mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
L
Linus Torvalds 已提交
269 270 271 272 273 274 275
	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];
B
Bjorn Helgaas 已提交
276
	pnp_register_mem_resource(option, mem);
L
Linus Torvalds 已提交
277 278 279 280
	return;
}

static void
B
Bjorn Helgaas 已提交
281 282
pnpbios_parse_mem32_option(unsigned char *p, int size,
			   struct pnp_option *option)
L
Linus Torvalds 已提交
283
{
B
Bjorn Helgaas 已提交
284
	struct pnp_mem *mem;
285
	mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
L
Linus Torvalds 已提交
286 287 288 289 290 291 292
	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];
B
Bjorn Helgaas 已提交
293
	pnp_register_mem_resource(option, mem);
L
Linus Torvalds 已提交
294 295 296 297
	return;
}

static void
B
Bjorn Helgaas 已提交
298 299
pnpbios_parse_fixed_mem32_option(unsigned char *p, int size,
				 struct pnp_option *option)
L
Linus Torvalds 已提交
300
{
B
Bjorn Helgaas 已提交
301
	struct pnp_mem *mem;
302
	mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
L
Linus Torvalds 已提交
303 304 305 306 307 308
	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];
B
Bjorn Helgaas 已提交
309
	pnp_register_mem_resource(option, mem);
L
Linus Torvalds 已提交
310 311 312 313 314 315
	return;
}

static void
pnpbios_parse_irq_option(unsigned char *p, int size, struct pnp_option *option)
{
B
Bjorn Helgaas 已提交
316
	struct pnp_irq *irq;
L
Linus Torvalds 已提交
317 318
	unsigned long bits;

319
	irq = kzalloc(sizeof(struct pnp_irq), GFP_KERNEL);
L
Linus Torvalds 已提交
320 321 322 323 324 325 326 327
	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;
B
Bjorn Helgaas 已提交
328
	pnp_register_irq_resource(option, irq);
L
Linus Torvalds 已提交
329 330 331 332 333 334
	return;
}

static void
pnpbios_parse_dma_option(unsigned char *p, int size, struct pnp_option *option)
{
B
Bjorn Helgaas 已提交
335
	struct pnp_dma *dma;
336
	dma = kzalloc(sizeof(struct pnp_dma), GFP_KERNEL);
L
Linus Torvalds 已提交
337 338 339 340
	if (!dma)
		return;
	dma->map = p[1];
	dma->flags = p[2];
B
Bjorn Helgaas 已提交
341
	pnp_register_dma_resource(option, dma);
L
Linus Torvalds 已提交
342 343 344 345 346 347
	return;
}

static void
pnpbios_parse_port_option(unsigned char *p, int size, struct pnp_option *option)
{
B
Bjorn Helgaas 已提交
348
	struct pnp_port *port;
349
	port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL);
L
Linus Torvalds 已提交
350 351 352 353 354 355 356
	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;
B
Bjorn Helgaas 已提交
357
	pnp_register_port_resource(option, port);
L
Linus Torvalds 已提交
358 359 360 361
	return;
}

static void
B
Bjorn Helgaas 已提交
362 363
pnpbios_parse_fixed_port_option(unsigned char *p, int size,
				struct pnp_option *option)
L
Linus Torvalds 已提交
364
{
B
Bjorn Helgaas 已提交
365
	struct pnp_port *port;
366
	port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL);
L
Linus Torvalds 已提交
367 368 369 370 371 372
	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;
B
Bjorn Helgaas 已提交
373
	pnp_register_port_resource(option, port);
L
Linus Torvalds 已提交
374 375 376
	return;
}

B
Bjorn Helgaas 已提交
377 378 379
static unsigned char *pnpbios_parse_resource_option_data(unsigned char *p,
							 unsigned char *end,
							 struct pnp_dev *dev)
L
Linus Torvalds 已提交
380 381 382 383 384 385 386 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;

	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 已提交
395
		if (p[0] & LARGE_TAG) {	/* large tag */
L
Linus Torvalds 已提交
396 397
			len = (p[2] << 8) | p[1];
			tag = p[0];
B
Bjorn Helgaas 已提交
398
		} else {	/* small tag */
L
Linus Torvalds 已提交
399
			len = p[0] & 0x07;
B
Bjorn Helgaas 已提交
400
			tag = ((p[0] >> 3) & 0x0f);
L
Linus Torvalds 已提交
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
		}

		switch (tag) {

		case LARGE_TAG_MEM:
			if (len != 9)
				goto len_err;
			pnpbios_parse_mem_option(p, len, option);
			break;

		case LARGE_TAG_MEM32:
			if (len != 17)
				goto len_err;
			pnpbios_parse_mem32_option(p, len, option);
			break;

		case LARGE_TAG_FIXEDMEM32:
			if (len != 9)
				goto len_err;
			pnpbios_parse_fixed_mem32_option(p, len, option);
			break;

		case SMALL_TAG_IRQ:
			if (len < 2 || len > 3)
				goto len_err;
			pnpbios_parse_irq_option(p, len, option);
			break;

		case SMALL_TAG_DMA:
			if (len != 2)
				goto len_err;
			pnpbios_parse_dma_option(p, len, option);
			break;

		case SMALL_TAG_PORT:
			if (len != 7)
				goto len_err;
			pnpbios_parse_port_option(p, len, option);
			break;

		case SMALL_TAG_VENDOR:
			/* do nothing */
			break;

		case SMALL_TAG_FIXEDPORT:
			if (len != 3)
				goto len_err;
			pnpbios_parse_fixed_port_option(p, len, option);
			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 已提交
466 467
				printk(KERN_WARNING
				       "PnPBIOS: Missing SMALL_TAG_STARTDEP tag\n");
L
Linus Torvalds 已提交
468 469 470 471
			option = option_independent;
			break;

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

B
Bjorn Helgaas 已提交
474 475 476 477 478
		default:	/* an unkown tag */
		      len_err:
			printk(KERN_ERR
			       "PnPBIOS: Unknown tag '0x%x', length '%d'.\n",
			       tag, len);
L
Linus Torvalds 已提交
479 480 481 482 483 484 485 486 487 488
			break;
		}

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

B
Bjorn Helgaas 已提交
489 490
	printk(KERN_ERR
	       "PnPBIOS: Resource structure does not contain an end tag.\n");
L
Linus Torvalds 已提交
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509

	return NULL;
}

/*
 * Compatible Device IDs
 */

#define HEX(id,a) hex[((id)>>a) & 15]
#define CHAR(id,a) (0x40 + (((id)>>a) & 31))
//

void pnpid32_to_pnpid(u32 id, char *str)
{
	const char *hex = "0123456789abcdef";

	id = be32_to_cpu(id);
	str[0] = CHAR(id, 26);
	str[1] = CHAR(id, 21);
B
Bjorn Helgaas 已提交
510
	str[2] = CHAR(id, 16);
L
Linus Torvalds 已提交
511 512 513 514 515 516 517 518
	str[3] = HEX(id, 12);
	str[4] = HEX(id, 8);
	str[5] = HEX(id, 4);
	str[6] = HEX(id, 0);
	str[7] = '\0';

	return;
}
B
Bjorn Helgaas 已提交
519

L
Linus Torvalds 已提交
520 521 522 523
//
#undef CHAR
#undef HEX

B
Bjorn Helgaas 已提交
524 525 526
static unsigned char *pnpbios_parse_compatible_ids(unsigned char *p,
						   unsigned char *end,
						   struct pnp_dev *dev)
L
Linus Torvalds 已提交
527 528 529 530 531 532 533 534 535 536 537
{
	int len, tag;
	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 已提交
538
		if (p[0] & LARGE_TAG) {	/* large tag */
L
Linus Torvalds 已提交
539 540
			len = (p[2] << 8) | p[1];
			tag = p[0];
B
Bjorn Helgaas 已提交
541
		} else {	/* small tag */
L
Linus Torvalds 已提交
542
			len = p[0] & 0x07;
B
Bjorn Helgaas 已提交
543
			tag = ((p[0] >> 3) & 0x0f);
L
Linus Torvalds 已提交
544 545 546 547 548
		}

		switch (tag) {

		case LARGE_TAG_ANSISTR:
B
Bjorn Helgaas 已提交
549 550 551 552
			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 已提交
553 554
			break;

B
Bjorn Helgaas 已提交
555
		case SMALL_TAG_COMPATDEVID:	/* compatible ID */
L
Linus Torvalds 已提交
556 557
			if (len != 4)
				goto len_err;
B
Bjorn Helgaas 已提交
558
			dev_id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL);
L
Linus Torvalds 已提交
559 560
			if (!dev_id)
				return NULL;
B
Bjorn Helgaas 已提交
561 562
			pnpid32_to_pnpid(p[1] | p[2] << 8 | p[3] << 16 | p[4] <<
					 24, id);
L
Linus Torvalds 已提交
563 564 565 566 567 568
			memcpy(&dev_id->id, id, 7);
			pnp_add_id(dev_id, dev);
			break;

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

B
Bjorn Helgaas 已提交
572 573 574 575 576
		default:	/* an unkown tag */
		      len_err:
			printk(KERN_ERR
			       "PnPBIOS: Unknown tag '0x%x', length '%d'.\n",
			       tag, len);
L
Linus Torvalds 已提交
577 578 579 580 581 582 583 584 585 586
			break;
		}

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

B
Bjorn Helgaas 已提交
587 588
	printk(KERN_ERR
	       "PnPBIOS: Resource structure does not contain an end tag.\n");
L
Linus Torvalds 已提交
589 590 591 592 593 594 595 596

	return NULL;
}

/*
 * Allocated Resource Encoding
 */

B
Bjorn Helgaas 已提交
597
static void pnpbios_encode_mem(unsigned char *p, struct resource *res)
L
Linus Torvalds 已提交
598 599 600 601 602 603 604 605 606 607 608 609
{
	unsigned long base = res->start;
	unsigned long len = res->end - res->start + 1;
	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;
	return;
}

B
Bjorn Helgaas 已提交
610
static void pnpbios_encode_mem32(unsigned char *p, struct resource *res)
L
Linus Torvalds 已提交
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
{
	unsigned long base = res->start;
	unsigned long len = res->end - res->start + 1;
	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;
	return;
}

B
Bjorn Helgaas 已提交
629 630 631
static void pnpbios_encode_fixed_mem32(unsigned char *p, struct resource *res)
{
	unsigned long base = res->start;
L
Linus Torvalds 已提交
632 633 634 635 636 637 638 639 640 641 642 643
	unsigned long len = res->end - res->start + 1;
	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;
	return;
}

B
Bjorn Helgaas 已提交
644
static void pnpbios_encode_irq(unsigned char *p, struct resource *res)
L
Linus Torvalds 已提交
645 646 647 648 649 650 651 652
{
	unsigned long map = 0;
	map = 1 << res->start;
	p[1] = map & 0xff;
	p[2] = (map >> 8) & 0xff;
	return;
}

B
Bjorn Helgaas 已提交
653
static void pnpbios_encode_dma(unsigned char *p, struct resource *res)
L
Linus Torvalds 已提交
654 655 656 657 658 659 660
{
	unsigned long map = 0;
	map = 1 << res->start;
	p[1] = map & 0xff;
	return;
}

B
Bjorn Helgaas 已提交
661
static void pnpbios_encode_port(unsigned char *p, struct resource *res)
L
Linus Torvalds 已提交
662 663 664 665 666 667 668 669 670 671 672
{
	unsigned long base = res->start;
	unsigned long len = res->end - res->start + 1;
	p[2] = base & 0xff;
	p[3] = (base >> 8) & 0xff;
	p[4] = base & 0xff;
	p[5] = (base >> 8) & 0xff;
	p[7] = len & 0xff;
	return;
}

B
Bjorn Helgaas 已提交
673
static void pnpbios_encode_fixed_port(unsigned char *p, struct resource *res)
L
Linus Torvalds 已提交
674 675 676 677 678 679 680 681 682
{
	unsigned long base = res->start;
	unsigned long len = res->end - res->start + 1;
	p[1] = base & 0xff;
	p[2] = (base >> 8) & 0xff;
	p[3] = len & 0xff;
	return;
}

B
Bjorn Helgaas 已提交
683 684 685 686 687
static unsigned char *pnpbios_encode_allocated_resource_data(unsigned char *p,
							     unsigned char *end,
							     struct
							     pnp_resource_table
							     *res)
L
Linus Torvalds 已提交
688 689 690 691 692 693 694 695 696 697
{
	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 已提交
698
		if (p[0] & LARGE_TAG) {	/* large tag */
L
Linus Torvalds 已提交
699 700
			len = (p[2] << 8) | p[1];
			tag = p[0];
B
Bjorn Helgaas 已提交
701
		} else {	/* small tag */
L
Linus Torvalds 已提交
702
			len = p[0] & 0x07;
B
Bjorn Helgaas 已提交
703
			tag = ((p[0] >> 3) & 0x0f);
L
Linus Torvalds 已提交
704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
		}

		switch (tag) {

		case LARGE_TAG_MEM:
			if (len != 9)
				goto len_err;
			pnpbios_encode_mem(p, &res->mem_resource[mem]);
			mem++;
			break;

		case LARGE_TAG_MEM32:
			if (len != 17)
				goto len_err;
			pnpbios_encode_mem32(p, &res->mem_resource[mem]);
			mem++;
			break;

		case LARGE_TAG_FIXEDMEM32:
			if (len != 9)
				goto len_err;
			pnpbios_encode_fixed_mem32(p, &res->mem_resource[mem]);
			mem++;
			break;

		case SMALL_TAG_IRQ:
			if (len < 2 || len > 3)
				goto len_err;
			pnpbios_encode_irq(p, &res->irq_resource[irq]);
			irq++;
			break;

		case SMALL_TAG_DMA:
			if (len != 2)
				goto len_err;
			pnpbios_encode_dma(p, &res->dma_resource[dma]);
			dma++;
			break;

		case SMALL_TAG_PORT:
			if (len != 7)
				goto len_err;
			pnpbios_encode_port(p, &res->port_resource[port]);
			port++;
			break;

		case SMALL_TAG_VENDOR:
			/* do nothing */
			break;

		case SMALL_TAG_FIXEDPORT:
			if (len != 3)
				goto len_err;
			pnpbios_encode_fixed_port(p, &res->port_resource[port]);
			port++;
			break;

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

B
Bjorn Helgaas 已提交
766 767 768 769 770
		default:	/* an unkown tag */
		      len_err:
			printk(KERN_ERR
			       "PnPBIOS: Unknown tag '0x%x', length '%d'.\n",
			       tag, len);
L
Linus Torvalds 已提交
771 772 773 774 775 776 777 778 779 780
			break;
		}

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

B
Bjorn Helgaas 已提交
781 782
	printk(KERN_ERR
	       "PnPBIOS: Resource structure does not contain an end tag.\n");
L
Linus Torvalds 已提交
783 784 785 786 787 788 789 790

	return NULL;
}

/*
 * Core Parsing Functions
 */

B
Bjorn Helgaas 已提交
791
int pnpbios_parse_data_stream(struct pnp_dev *dev, struct pnp_bios_node *node)
L
Linus Torvalds 已提交
792
{
B
Bjorn Helgaas 已提交
793 794 795
	unsigned char *p = (char *)node->data;
	unsigned char *end = (char *)(node->data + node->size);
	p = pnpbios_parse_allocated_resource_data(p, end, &dev->res);
L
Linus Torvalds 已提交
796 797
	if (!p)
		return -EIO;
B
Bjorn Helgaas 已提交
798
	p = pnpbios_parse_resource_option_data(p, end, dev);
L
Linus Torvalds 已提交
799 800
	if (!p)
		return -EIO;
B
Bjorn Helgaas 已提交
801
	p = pnpbios_parse_compatible_ids(p, end, dev);
L
Linus Torvalds 已提交
802 803 804 805 806 807 808
	if (!p)
		return -EIO;
	return 0;
}

int
pnpbios_read_resources_from_node(struct pnp_resource_table *res,
B
Bjorn Helgaas 已提交
809
				 struct pnp_bios_node *node)
L
Linus Torvalds 已提交
810
{
B
Bjorn Helgaas 已提交
811 812 813
	unsigned char *p = (char *)node->data;
	unsigned char *end = (char *)(node->data + node->size);
	p = pnpbios_parse_allocated_resource_data(p, end, res);
L
Linus Torvalds 已提交
814 815 816 817 818 819 820
	if (!p)
		return -EIO;
	return 0;
}

int
pnpbios_write_resources_to_node(struct pnp_resource_table *res,
B
Bjorn Helgaas 已提交
821
				struct pnp_bios_node *node)
L
Linus Torvalds 已提交
822
{
B
Bjorn Helgaas 已提交
823 824 825
	unsigned char *p = (char *)node->data;
	unsigned char *end = (char *)(node->data + node->size);
	p = pnpbios_encode_allocated_resource_data(p, end, res);
L
Linus Torvalds 已提交
826 827 828 829
	if (!p)
		return -EIO;
	return 0;
}