rsparser.c 17.9 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_dmaresource(struct pnp_dev *dev, int dma)
L
Linus Torvalds 已提交
58
{
59 60 61 62 63 64 65 66
	struct resource *res;
	int i;

	for (i = 0; i < PNP_MAX_DMA; i++) {
		res = pnp_get_resource(dev, IORESOURCE_DMA, i);
		if (!pnp_resource_valid(res))
			break;
	}
B
Bjorn Helgaas 已提交
67

L
Linus Torvalds 已提交
68
	if (i < PNP_MAX_DMA) {
69
		res->flags = IORESOURCE_DMA;	// Also clears _UNSET flag
L
Linus Torvalds 已提交
70
		if (dma == -1) {
71
			res->flags |= IORESOURCE_DISABLED;
L
Linus Torvalds 已提交
72 73
			return;
		}
74
		res->start = res->end = (unsigned long)dma;
L
Linus Torvalds 已提交
75 76 77
	}
}

78
static void pnpbios_parse_allocated_ioresource(struct pnp_dev *dev,
B
Bjorn Helgaas 已提交
79
					       int io, int len)
L
Linus Torvalds 已提交
80
{
81 82 83 84 85 86 87 88
	struct resource *res;
	int i;

	for (i = 0; i < PNP_MAX_PORT; i++) {
		res = pnp_get_resource(dev, IORESOURCE_IO, i);
		if (!pnp_resource_valid(res))
			break;
	}
B
Bjorn Helgaas 已提交
89

L
Linus Torvalds 已提交
90
	if (i < PNP_MAX_PORT) {
91
		res->flags = IORESOURCE_IO;	// Also clears _UNSET flag
B
Bjorn Helgaas 已提交
92
		if (len <= 0 || (io + len - 1) >= 0x10003) {
93
			res->flags |= IORESOURCE_DISABLED;
L
Linus Torvalds 已提交
94 95
			return;
		}
96 97
		res->start = (unsigned long)io;
		res->end = (unsigned long)(io + len - 1);
L
Linus Torvalds 已提交
98 99 100
	}
}

101
static void pnpbios_parse_allocated_memresource(struct pnp_dev *dev,
B
Bjorn Helgaas 已提交
102
						int mem, int len)
L
Linus Torvalds 已提交
103
{
104 105 106 107 108 109 110 111
	struct resource *res;
	int i;

	for (i = 0; i < PNP_MAX_MEM; i++) {
		res = pnp_get_resource(dev, IORESOURCE_MEM, i);
		if (!pnp_resource_valid(res))
			break;
	}
B
Bjorn Helgaas 已提交
112

L
Linus Torvalds 已提交
113
	if (i < PNP_MAX_MEM) {
114
		res->flags = IORESOURCE_MEM;	// Also clears _UNSET flag
L
Linus Torvalds 已提交
115
		if (len <= 0) {
116
			res->flags |= IORESOURCE_DISABLED;
L
Linus Torvalds 已提交
117 118
			return;
		}
119 120
		res->start = (unsigned long)mem;
		res->end = (unsigned long)(mem + len - 1);
L
Linus Torvalds 已提交
121 122 123
	}
}

124 125 126
static unsigned char *pnpbios_parse_allocated_resource_data(struct pnp_dev *dev,
							    unsigned char *p,
							    unsigned char *end)
L
Linus Torvalds 已提交
127 128
{
	unsigned int len, tag;
129
	int io, size, mask, i, flags;
L
Linus Torvalds 已提交
130 131 132 133

	if (!p)
		return NULL;

134 135
	dev_dbg(&dev->dev, "parse allocated resources\n");

136
	pnp_init_resources(dev);
L
Linus Torvalds 已提交
137 138 139 140

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

		/* determine the type of tag */
B
Bjorn Helgaas 已提交
141
		if (p[0] & LARGE_TAG) {	/* large tag */
L
Linus Torvalds 已提交
142 143
			len = (p[2] << 8) | p[1];
			tag = p[0];
B
Bjorn Helgaas 已提交
144
		} else {	/* small tag */
L
Linus Torvalds 已提交
145
			len = p[0] & 0x07;
B
Bjorn Helgaas 已提交
146
			tag = ((p[0] >> 3) & 0x0f);
L
Linus Torvalds 已提交
147 148 149 150 151 152 153
		}

		switch (tag) {

		case LARGE_TAG_MEM:
			if (len != 9)
				goto len_err;
B
Bjorn Helgaas 已提交
154 155
			io = *(short *)&p[4];
			size = *(short *)&p[10];
156
			pnpbios_parse_allocated_memresource(dev, io, size);
L
Linus Torvalds 已提交
157 158 159 160 161 162 163 164 165 166 167 168 169
			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 已提交
170 171
			io = *(int *)&p[4];
			size = *(int *)&p[16];
172
			pnpbios_parse_allocated_memresource(dev, io, size);
L
Linus Torvalds 已提交
173 174 175 176 177
			break;

		case LARGE_TAG_FIXEDMEM32:
			if (len != 9)
				goto len_err;
B
Bjorn Helgaas 已提交
178 179
			io = *(int *)&p[4];
			size = *(int *)&p[8];
180
			pnpbios_parse_allocated_memresource(dev, io, size);
L
Linus Torvalds 已提交
181 182 183 184 185
			break;

		case SMALL_TAG_IRQ:
			if (len < 2 || len > 3)
				goto len_err;
186
			flags = 0;
L
Linus Torvalds 已提交
187
			io = -1;
B
Bjorn Helgaas 已提交
188 189 190 191
			mask = p[1] + p[2] * 256;
			for (i = 0; i < 16; i++, mask = mask >> 1)
				if (mask & 0x01)
					io = i;
192 193 194 195 196
			if (io != -1)
				pcibios_penalize_isa_irq(io, 1);
			else
				flags = IORESOURCE_DISABLED;
			pnp_add_irq_resource(dev, io, flags);
L
Linus Torvalds 已提交
197 198 199 200 201 202 203
			break;

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

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

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

B
Bjorn Helgaas 已提交
235
		default:	/* an unkown tag */
B
Bjorn Helgaas 已提交
236
len_err:
237 238
			dev_err(&dev->dev, "unknown tag %#x length %d\n",
				tag, len);
L
Linus Torvalds 已提交
239 240 241 242 243 244 245 246 247 248
			break;
		}

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

249
	dev_err(&dev->dev, "no end tag in resource structure\n");
L
Linus Torvalds 已提交
250 251 252 253 254 255 256 257

	return NULL;
}

/*
 * Resource Configuration Options
 */

258 259
static __init void pnpbios_parse_mem_option(struct pnp_dev *dev,
					    unsigned char *p, int size,
260
					    struct pnp_option *option)
L
Linus Torvalds 已提交
261
{
B
Bjorn Helgaas 已提交
262
	struct pnp_mem *mem;
B
Bjorn Helgaas 已提交
263

264
	mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
L
Linus Torvalds 已提交
265 266 267 268 269 270 271
	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];
272
	pnp_register_mem_resource(dev, option, mem);
L
Linus Torvalds 已提交
273 274
}

275 276
static __init void pnpbios_parse_mem32_option(struct pnp_dev *dev,
					      unsigned char *p, int size,
277
					      struct pnp_option *option)
L
Linus Torvalds 已提交
278
{
B
Bjorn Helgaas 已提交
279
	struct pnp_mem *mem;
B
Bjorn Helgaas 已提交
280

281
	mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
L
Linus Torvalds 已提交
282 283 284 285 286 287 288
	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];
289
	pnp_register_mem_resource(dev, option, mem);
L
Linus Torvalds 已提交
290 291
}

292 293
static __init void pnpbios_parse_fixed_mem32_option(struct pnp_dev *dev,
						    unsigned char *p, int size,
294
						    struct pnp_option *option)
L
Linus Torvalds 已提交
295
{
B
Bjorn Helgaas 已提交
296
	struct pnp_mem *mem;
B
Bjorn Helgaas 已提交
297

298
	mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL);
L
Linus Torvalds 已提交
299 300 301 302 303 304
	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];
305
	pnp_register_mem_resource(dev, option, mem);
L
Linus Torvalds 已提交
306 307
}

308 309 310
static __init void pnpbios_parse_irq_option(struct pnp_dev *dev,
					    unsigned char *p, int size,
					    struct pnp_option *option)
L
Linus Torvalds 已提交
311
{
B
Bjorn Helgaas 已提交
312
	struct pnp_irq *irq;
L
Linus Torvalds 已提交
313 314
	unsigned long bits;

315
	irq = kzalloc(sizeof(struct pnp_irq), GFP_KERNEL);
L
Linus Torvalds 已提交
316 317 318 319 320 321 322 323
	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;
324
	pnp_register_irq_resource(dev, option, irq);
L
Linus Torvalds 已提交
325 326
}

327 328 329
static __init void pnpbios_parse_dma_option(struct pnp_dev *dev,
					    unsigned char *p, int size,
					    struct pnp_option *option)
L
Linus Torvalds 已提交
330
{
B
Bjorn Helgaas 已提交
331
	struct pnp_dma *dma;
B
Bjorn Helgaas 已提交
332

333
	dma = kzalloc(sizeof(struct pnp_dma), GFP_KERNEL);
L
Linus Torvalds 已提交
334 335 336 337
	if (!dma)
		return;
	dma->map = p[1];
	dma->flags = p[2];
338
	pnp_register_dma_resource(dev, option, dma);
L
Linus Torvalds 已提交
339 340
}

341 342
static __init void pnpbios_parse_port_option(struct pnp_dev *dev,
					     unsigned char *p, int size,
343
					     struct pnp_option *option)
L
Linus Torvalds 已提交
344
{
B
Bjorn Helgaas 已提交
345
	struct pnp_port *port;
B
Bjorn Helgaas 已提交
346

347
	port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL);
L
Linus Torvalds 已提交
348 349 350 351 352 353 354
	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;
355
	pnp_register_port_resource(dev, option, port);
L
Linus Torvalds 已提交
356 357
}

358 359
static __init void pnpbios_parse_fixed_port_option(struct pnp_dev *dev,
						   unsigned char *p, int size,
360
						   struct pnp_option *option)
L
Linus Torvalds 已提交
361
{
B
Bjorn Helgaas 已提交
362
	struct pnp_port *port;
B
Bjorn Helgaas 已提交
363

364
	port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL);
L
Linus Torvalds 已提交
365 366 367 368 369 370
	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;
371
	pnp_register_port_resource(dev, option, port);
L
Linus Torvalds 已提交
372 373
}

374 375 376
static __init unsigned char *
pnpbios_parse_resource_option_data(unsigned char *p, unsigned char *end,
					struct pnp_dev *dev)
L
Linus Torvalds 已提交
377 378 379 380 381 382 383 384
{
	unsigned int len, tag;
	int priority = 0;
	struct pnp_option *option, *option_independent;

	if (!p)
		return NULL;

385 386
	dev_dbg(&dev->dev, "parse resource options\n");

L
Linus Torvalds 已提交
387 388 389 390 391 392 393
	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 已提交
394
		if (p[0] & LARGE_TAG) {	/* large tag */
L
Linus Torvalds 已提交
395 396
			len = (p[2] << 8) | p[1];
			tag = p[0];
B
Bjorn Helgaas 已提交
397
		} else {	/* small tag */
L
Linus Torvalds 已提交
398
			len = p[0] & 0x07;
B
Bjorn Helgaas 已提交
399
			tag = ((p[0] >> 3) & 0x0f);
L
Linus Torvalds 已提交
400 401 402 403 404 405 406
		}

		switch (tag) {

		case LARGE_TAG_MEM:
			if (len != 9)
				goto len_err;
407
			pnpbios_parse_mem_option(dev, p, len, option);
L
Linus Torvalds 已提交
408 409 410 411 412
			break;

		case LARGE_TAG_MEM32:
			if (len != 17)
				goto len_err;
413
			pnpbios_parse_mem32_option(dev, p, len, option);
L
Linus Torvalds 已提交
414 415 416 417 418
			break;

		case LARGE_TAG_FIXEDMEM32:
			if (len != 9)
				goto len_err;
419
			pnpbios_parse_fixed_mem32_option(dev, p, len, option);
L
Linus Torvalds 已提交
420 421 422 423 424
			break;

		case SMALL_TAG_IRQ:
			if (len < 2 || len > 3)
				goto len_err;
425
			pnpbios_parse_irq_option(dev, p, len, option);
L
Linus Torvalds 已提交
426 427 428 429 430
			break;

		case SMALL_TAG_DMA:
			if (len != 2)
				goto len_err;
431
			pnpbios_parse_dma_option(dev, p, len, option);
L
Linus Torvalds 已提交
432 433 434 435 436
			break;

		case SMALL_TAG_PORT:
			if (len != 7)
				goto len_err;
437
			pnpbios_parse_port_option(dev, p, len, option);
L
Linus Torvalds 已提交
438 439 440 441 442 443 444 445 446
			break;

		case SMALL_TAG_VENDOR:
			/* do nothing */
			break;

		case SMALL_TAG_FIXEDPORT:
			if (len != 3)
				goto len_err;
447
			pnpbios_parse_fixed_port_option(dev, p, len, option);
L
Linus Torvalds 已提交
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
			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)
465 466
				dev_warn(&dev->dev, "missing "
					 "SMALL_TAG_STARTDEP tag\n");
L
Linus Torvalds 已提交
467
			option = option_independent;
468
			dev_dbg(&dev->dev, "end dependent options\n");
L
Linus Torvalds 已提交
469 470 471
			break;

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

B
Bjorn Helgaas 已提交
474
		default:	/* an unkown tag */
B
Bjorn Helgaas 已提交
475
len_err:
476 477
			dev_err(&dev->dev, "unknown tag %#x length %d\n",
				tag, len);
L
Linus Torvalds 已提交
478 479 480 481 482 483 484 485 486 487
			break;
		}

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

488
	dev_err(&dev->dev, "no end tag in resource structure\n");
L
Linus Torvalds 已提交
489 490 491 492 493 494 495 496

	return NULL;
}

/*
 * Compatible Device IDs
 */

B
Bjorn Helgaas 已提交
497 498 499
static unsigned char *pnpbios_parse_compatible_ids(unsigned char *p,
						   unsigned char *end,
						   struct pnp_dev *dev)
L
Linus Torvalds 已提交
500 501
{
	int len, tag;
B
Bjorn Helgaas 已提交
502
	u32 eisa_id;
L
Linus Torvalds 已提交
503 504 505 506 507 508 509 510 511
	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 已提交
512
		if (p[0] & LARGE_TAG) {	/* large tag */
L
Linus Torvalds 已提交
513 514
			len = (p[2] << 8) | p[1];
			tag = p[0];
B
Bjorn Helgaas 已提交
515
		} else {	/* small tag */
L
Linus Torvalds 已提交
516
			len = p[0] & 0x07;
B
Bjorn Helgaas 已提交
517
			tag = ((p[0] >> 3) & 0x0f);
L
Linus Torvalds 已提交
518 519 520 521 522
		}

		switch (tag) {

		case LARGE_TAG_ANSISTR:
B
Bjorn Helgaas 已提交
523 524 525 526
			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 已提交
527 528
			break;

B
Bjorn Helgaas 已提交
529
		case SMALL_TAG_COMPATDEVID:	/* compatible ID */
L
Linus Torvalds 已提交
530 531
			if (len != 4)
				goto len_err;
B
Bjorn Helgaas 已提交
532 533
			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);
534 535 536
			dev_id = pnp_add_id(dev, id);
			if (!dev_id)
				return NULL;
L
Linus Torvalds 已提交
537 538 539 540
			break;

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

B
Bjorn Helgaas 已提交
544
		default:	/* an unkown tag */
B
Bjorn Helgaas 已提交
545
len_err:
546 547
			dev_err(&dev->dev, "unknown tag %#x length %d\n",
				tag, len);
L
Linus Torvalds 已提交
548 549 550 551 552 553 554 555 556 557
			break;
		}

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

558
	dev_err(&dev->dev, "no end tag in resource structure\n");
L
Linus Torvalds 已提交
559 560 561 562 563 564 565 566

	return NULL;
}

/*
 * Allocated Resource Encoding
 */

567 568
static void pnpbios_encode_mem(struct pnp_dev *dev, unsigned char *p,
			       struct resource *res)
L
Linus Torvalds 已提交
569 570 571
{
	unsigned long base = res->start;
	unsigned long len = res->end - res->start + 1;
B
Bjorn Helgaas 已提交
572

L
Linus Torvalds 已提交
573 574 575 576 577 578
	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;
579 580 581

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

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

L
Linus Torvalds 已提交
590 591 592 593 594 595 596 597 598 599 600 601
	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;
602 603 604

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

607 608
static void pnpbios_encode_fixed_mem32(struct pnp_dev *dev, unsigned char *p,
				       struct resource *res)
B
Bjorn Helgaas 已提交
609 610
{
	unsigned long base = res->start;
L
Linus Torvalds 已提交
611
	unsigned long len = res->end - res->start + 1;
B
Bjorn Helgaas 已提交
612

L
Linus Torvalds 已提交
613 614 615 616 617 618 619 620
	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;
621 622 623

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

626 627
static void pnpbios_encode_irq(struct pnp_dev *dev, unsigned char *p,
			       struct resource *res)
L
Linus Torvalds 已提交
628 629
{
	unsigned long map = 0;
B
Bjorn Helgaas 已提交
630

L
Linus Torvalds 已提交
631 632 633
	map = 1 << res->start;
	p[1] = map & 0xff;
	p[2] = (map >> 8) & 0xff;
634 635

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

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

L
Linus Torvalds 已提交
643 644
	map = 1 << res->start;
	p[1] = map & 0xff;
645 646

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

649 650
static void pnpbios_encode_port(struct pnp_dev *dev, unsigned char *p,
				struct resource *res)
L
Linus Torvalds 已提交
651 652 653
{
	unsigned long base = res->start;
	unsigned long len = res->end - res->start + 1;
B
Bjorn Helgaas 已提交
654

L
Linus Torvalds 已提交
655 656 657 658 659
	p[2] = base & 0xff;
	p[3] = (base >> 8) & 0xff;
	p[4] = base & 0xff;
	p[5] = (base >> 8) & 0xff;
	p[7] = len & 0xff;
660 661 662

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

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

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

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

679 680 681 682
static unsigned char *pnpbios_encode_allocated_resource_data(struct pnp_dev
								*dev,
							     unsigned char *p,
							     unsigned char *end)
L
Linus Torvalds 已提交
683 684 685 686 687 688 689 690 691 692
{
	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 已提交
693
		if (p[0] & LARGE_TAG) {	/* large tag */
L
Linus Torvalds 已提交
694 695
			len = (p[2] << 8) | p[1];
			tag = p[0];
B
Bjorn Helgaas 已提交
696
		} else {	/* small tag */
L
Linus Torvalds 已提交
697
			len = p[0] & 0x07;
B
Bjorn Helgaas 已提交
698
			tag = ((p[0] >> 3) & 0x0f);
L
Linus Torvalds 已提交
699 700 701 702 703 704 705
		}

		switch (tag) {

		case LARGE_TAG_MEM:
			if (len != 9)
				goto len_err;
706 707
			pnpbios_encode_mem(dev, p,
				pnp_get_resource(dev, IORESOURCE_MEM, mem));
L
Linus Torvalds 已提交
708 709 710 711 712 713
			mem++;
			break;

		case LARGE_TAG_MEM32:
			if (len != 17)
				goto len_err;
714 715
			pnpbios_encode_mem32(dev, p,
				pnp_get_resource(dev, IORESOURCE_MEM, mem));
L
Linus Torvalds 已提交
716 717 718 719 720 721
			mem++;
			break;

		case LARGE_TAG_FIXEDMEM32:
			if (len != 9)
				goto len_err;
722 723
			pnpbios_encode_fixed_mem32(dev, p,
				pnp_get_resource(dev, IORESOURCE_MEM, mem));
L
Linus Torvalds 已提交
724 725 726 727 728 729
			mem++;
			break;

		case SMALL_TAG_IRQ:
			if (len < 2 || len > 3)
				goto len_err;
730 731
			pnpbios_encode_irq(dev, p,
				pnp_get_resource(dev, IORESOURCE_IRQ, irq));
L
Linus Torvalds 已提交
732 733 734 735 736 737
			irq++;
			break;

		case SMALL_TAG_DMA:
			if (len != 2)
				goto len_err;
738 739
			pnpbios_encode_dma(dev, p,
				pnp_get_resource(dev, IORESOURCE_DMA, dma));
L
Linus Torvalds 已提交
740 741 742 743 744 745
			dma++;
			break;

		case SMALL_TAG_PORT:
			if (len != 7)
				goto len_err;
746 747
			pnpbios_encode_port(dev, p,
				pnp_get_resource(dev, IORESOURCE_IO, port));
L
Linus Torvalds 已提交
748 749 750 751 752 753 754 755 756 757
			port++;
			break;

		case SMALL_TAG_VENDOR:
			/* do nothing */
			break;

		case SMALL_TAG_FIXEDPORT:
			if (len != 3)
				goto len_err;
758 759
			pnpbios_encode_fixed_port(dev, p,
				pnp_get_resource(dev, IORESOURCE_IO, port));
L
Linus Torvalds 已提交
760 761 762 763 764
			port++;
			break;

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

B
Bjorn Helgaas 已提交
768
		default:	/* an unkown tag */
B
Bjorn Helgaas 已提交
769
len_err:
770 771
			dev_err(&dev->dev, "unknown tag %#x length %d\n",
				tag, len);
L
Linus Torvalds 已提交
772 773 774 775 776 777 778 779 780 781
			break;
		}

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

782
	dev_err(&dev->dev, "no end tag in resource structure\n");
L
Linus Torvalds 已提交
783 784 785 786 787 788 789 790

	return NULL;
}

/*
 * Core Parsing Functions
 */

791 792
int __init pnpbios_parse_data_stream(struct pnp_dev *dev,
					struct pnp_bios_node *node)
L
Linus Torvalds 已提交
793
{
B
Bjorn Helgaas 已提交
794 795
	unsigned char *p = (char *)node->data;
	unsigned char *end = (char *)(node->data + node->size);
B
Bjorn Helgaas 已提交
796

797
	p = pnpbios_parse_allocated_resource_data(dev, p, end);
L
Linus Torvalds 已提交
798 799
	if (!p)
		return -EIO;
B
Bjorn Helgaas 已提交
800
	p = pnpbios_parse_resource_option_data(p, end, dev);
L
Linus Torvalds 已提交
801 802
	if (!p)
		return -EIO;
B
Bjorn Helgaas 已提交
803
	p = pnpbios_parse_compatible_ids(p, end, dev);
L
Linus Torvalds 已提交
804 805 806 807 808
	if (!p)
		return -EIO;
	return 0;
}

809
int pnpbios_read_resources_from_node(struct pnp_dev *dev,
B
Bjorn Helgaas 已提交
810
				     struct pnp_bios_node *node)
L
Linus Torvalds 已提交
811
{
B
Bjorn Helgaas 已提交
812 813
	unsigned char *p = (char *)node->data;
	unsigned char *end = (char *)(node->data + node->size);
B
Bjorn Helgaas 已提交
814

815
	p = pnpbios_parse_allocated_resource_data(dev, p, end);
L
Linus Torvalds 已提交
816 817 818 819 820
	if (!p)
		return -EIO;
	return 0;
}

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

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