pnpparse.c revision 58850
152059Sdfr/*-
252059Sdfr * Copyright (c) 1999 Doug Rabson
352059Sdfr * All rights reserved.
452059Sdfr *
552059Sdfr * Redistribution and use in source and binary forms, with or without
652059Sdfr * modification, are permitted provided that the following conditions
752059Sdfr * are met:
852059Sdfr * 1. Redistributions of source code must retain the above copyright
952059Sdfr *    notice, this list of conditions and the following disclaimer.
1052059Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1152059Sdfr *    notice, this list of conditions and the following disclaimer in the
1252059Sdfr *    documentation and/or other materials provided with the distribution.
1352059Sdfr *
1452059Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1552059Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1652059Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1752059Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1852059Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1952059Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2052059Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2152059Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2252059Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2352059Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2452059Sdfr * SUCH DAMAGE.
2552059Sdfr *
2652059Sdfr *	$FreeBSD: head/sys/isa/pnpparse.c 58850 2000-03-31 07:12:12Z dfr $
2752059Sdfr */
2852059Sdfr
2952059Sdfr#include <sys/param.h>
3052059Sdfr#include <sys/systm.h>
3152059Sdfr#include <sys/kernel.h>
3252059Sdfr#include <sys/module.h>
3352059Sdfr#include <sys/bus.h>
3452059Sdfr#include <isa/isavar.h>
3552059Sdfr#include <isa/pnpreg.h>
3652059Sdfr#include <isa/pnpvar.h>
3752059Sdfr
3858850Sdfr#define	MAXDEP	8
3958850Sdfr
4052241Sdfr#define I16(p)	((p)[0] + ((p)[1] << 8))
4152241Sdfr#define I32(p)	(I16(p) + (I16(p+2) << 16))
4252241Sdfr
4352059Sdfr/*
4452059Sdfr * Parse resource data for Logical Devices.
4552059Sdfr *
4652059Sdfr * This function exits as soon as it gets an error reading *ANY*
4758850Sdfr * Resource Data or it reaches the end of Resource Data.
4852059Sdfr */
4952059Sdfrvoid
5052059Sdfrpnp_parse_resources(device_t dev, u_char *resources, int len)
5152059Sdfr{
5252059Sdfr	device_t parent = device_get_parent(dev);
5352059Sdfr	u_char tag, *resp, *resinfo;
5452059Sdfr	int large_len, scanning = len;
5552241Sdfr	u_int32_t id, compat_id;
5652059Sdfr	struct isa_config *config;
5758850Sdfr	int ncfgs = 1;
5858850Sdfr	int priorities[1 + MAXDEP];
5958850Sdfr	struct isa_config configs[1 + MAXDEP];
6052059Sdfr	char buf[100];
6158850Sdfr	int i;
6252059Sdfr
6352241Sdfr	id = isa_get_logicalid(dev);
6458850Sdfr	bzero(configs, sizeof configs);
6558850Sdfr	config = &configs[0];
6658850Sdfr	priorities[0] = 0;
6752059Sdfr	resp = resources;
6852059Sdfr	while (scanning > 0) {
6952059Sdfr		tag = *resp++;
7052059Sdfr		scanning--;
7152059Sdfr		if (PNP_RES_TYPE(tag) == 0) {
7252059Sdfr			/* Small resource */
7352059Sdfr			if (scanning < PNP_SRES_LEN(tag)) {
7452059Sdfr				scanning = 0;
7552059Sdfr				continue;
7652059Sdfr			}
7752059Sdfr			resinfo = resp;
7852059Sdfr			resp += PNP_SRES_LEN(tag);
7952059Sdfr			scanning -= PNP_SRES_LEN(tag);;
8052059Sdfr
8152059Sdfr			switch (PNP_SRES_NUM(tag)) {
8252059Sdfr			case PNP_TAG_COMPAT_DEVICE:
8352059Sdfr				/*
8452059Sdfr				 * Got a compatible device id
8552059Sdfr				 * resource. Should keep a list of
8652059Sdfr				 * compat ids in the device.
8752059Sdfr				 */
8852059Sdfr				bcopy(resinfo, &compat_id, 4);
8952059Sdfr				isa_set_compatid(dev, compat_id);
9052059Sdfr				break;
9152059Sdfr
9252059Sdfr			case PNP_TAG_IRQ_FORMAT:
9352241Sdfr				if (bootverbose) {
9452241Sdfr					printf("%s: adding irq mask %#04x\n",
9552241Sdfr					       pnp_eisaformat(id),
9652241Sdfr					       I16(resinfo));
9752241Sdfr				}
9852059Sdfr				if (config->ic_nirq == ISA_NIRQ) {
9958850Sdfr					device_printf(parent, "too many irqs\n");
10052059Sdfr					scanning = 0;
10152059Sdfr					break;
10252059Sdfr				}
10352059Sdfr				config->ic_irqmask[config->ic_nirq] =
10452241Sdfr					I16(resinfo);
10552059Sdfr				config->ic_nirq++;
10652059Sdfr				break;
10752059Sdfr
10852059Sdfr			case PNP_TAG_DMA_FORMAT:
10952241Sdfr				if (bootverbose) {
11052241Sdfr					printf("%s: adding dma mask %#02x\n",
11152241Sdfr					       pnp_eisaformat(id),
11252241Sdfr					       resinfo[0]);
11352241Sdfr				}
11452059Sdfr				if (config->ic_ndrq == ISA_NDRQ) {
11558850Sdfr					device_printf(parent, "too many drqs\n");
11652059Sdfr					scanning = 0;
11752059Sdfr					break;
11852059Sdfr				}
11952059Sdfr				config->ic_drqmask[config->ic_ndrq] =
12052059Sdfr					resinfo[0];
12152059Sdfr				config->ic_ndrq++;
12252059Sdfr				break;
12352059Sdfr
12452059Sdfr			case PNP_TAG_START_DEPENDANT:
12552241Sdfr				if (bootverbose) {
12652241Sdfr					printf("%s: start dependant\n",
12752241Sdfr					       pnp_eisaformat(id));
12852241Sdfr				}
12958850Sdfr				if (ncfgs >= MAXDEP) {
13058850Sdfr					device_printf(parent, "too many dependant configs (%d)\n", MAXDEP);
13152059Sdfr					scanning = 0;
13252059Sdfr					break;
13352059Sdfr				}
13458850Sdfr				config = &configs[ncfgs];
13552059Sdfr				/*
13652059Sdfr				 * If the priority is not specified,
13752059Sdfr				 * then use the default of
13852059Sdfr				 * 'acceptable'
13952059Sdfr				 */
14052059Sdfr				if (PNP_SRES_LEN(tag) > 0)
14158850Sdfr					priorities[ncfgs] = resinfo[0];
14252059Sdfr				else
14358850Sdfr					priorities[ncfgs] = 1;
14458850Sdfr				ncfgs++;
14552059Sdfr				break;
14652059Sdfr
14752059Sdfr			case PNP_TAG_END_DEPENDANT:
14852241Sdfr				if (bootverbose) {
14952241Sdfr					printf("%s: end dependant\n",
15052241Sdfr					       pnp_eisaformat(id));
15152241Sdfr				}
15258850Sdfr				config = &configs[0];	/* back to main config */
15352059Sdfr				break;
15452059Sdfr
15552059Sdfr			case PNP_TAG_IO_RANGE:
15652241Sdfr				if (bootverbose) {
15752241Sdfr					printf("%s: adding io range "
15852241Sdfr					       "%#x-%#x, size=%#x, "
15952241Sdfr					       "align=%#x\n",
16052241Sdfr					       pnp_eisaformat(id),
16152241Sdfr					       I16(resinfo + 1),
16252241Sdfr					       I16(resinfo + 3) + resinfo[6]-1,
16352241Sdfr					       resinfo[6],
16452241Sdfr					       resinfo[5]);
16552241Sdfr				}
16652059Sdfr				if (config->ic_nport == ISA_NPORT) {
16758850Sdfr					device_printf(parent, "too many ports\n");
16852059Sdfr					scanning = 0;
16952059Sdfr					break;
17052059Sdfr				}
17152059Sdfr				config->ic_port[config->ic_nport].ir_start =
17252241Sdfr					I16(resinfo + 1);
17352059Sdfr				config->ic_port[config->ic_nport].ir_end =
17452241Sdfr					I16(resinfo + 3) + resinfo[6] - 1;
17552241Sdfr				config->ic_port[config->ic_nport].ir_size =
17652059Sdfr					resinfo[6];
17752241Sdfr				if (resinfo[5] == 0) {
17852241Sdfr				    /* Make sure align is at least one */
17952241Sdfr				    resinfo[5] = 1;
18052241Sdfr				}
18152059Sdfr				config->ic_port[config->ic_nport].ir_align =
18252059Sdfr					resinfo[5];
18352059Sdfr				config->ic_nport++;
18452059Sdfr				break;
18552059Sdfr
18652059Sdfr			case PNP_TAG_IO_FIXED:
18752241Sdfr				if (bootverbose) {
18858850Sdfr					printf("%s: adding fixed io range "
18952241Sdfr					       "%#x-%#x, size=%#x, "
19052241Sdfr					       "align=%#x\n",
19152241Sdfr					       pnp_eisaformat(id),
19252241Sdfr					       I16(resinfo),
19352241Sdfr					       I16(resinfo) + resinfo[2] - 1,
19452241Sdfr					       resinfo[2],
19552241Sdfr					       1);
19652241Sdfr				}
19752059Sdfr				if (config->ic_nport == ISA_NPORT) {
19858850Sdfr					device_printf(parent, "too many ports\n");
19952059Sdfr					scanning = 0;
20052059Sdfr					break;
20152059Sdfr				}
20252059Sdfr				config->ic_port[config->ic_nport].ir_start =
20352241Sdfr					I16(resinfo);
20452059Sdfr				config->ic_port[config->ic_nport].ir_end =
20552241Sdfr					I16(resinfo) + resinfo[2] - 1;
20652059Sdfr				config->ic_port[config->ic_nport].ir_size
20752059Sdfr					= resinfo[2];
20852059Sdfr				config->ic_port[config->ic_nport].ir_align = 1;
20952059Sdfr				config->ic_nport++;
21052059Sdfr				break;
21152059Sdfr
21252241Sdfr			case PNP_TAG_END:
21352241Sdfr				if (bootverbose) {
21458850Sdfr					printf("%s: end config\n",
21552241Sdfr					       pnp_eisaformat(id));
21652241Sdfr				}
21752241Sdfr				scanning = 0;
21852241Sdfr				break;
21952241Sdfr
22052059Sdfr			default:
22152059Sdfr				/* Skip this resource */
22258850Sdfr				device_printf(parent, "unexpected small tag %d\n",
22352059Sdfr					      PNP_SRES_NUM(tag));
22452059Sdfr				break;
22552059Sdfr			}
22652059Sdfr		} else {
22752059Sdfr			/* Large resource */
22852059Sdfr			if (scanning < 2) {
22952059Sdfr				scanning = 0;
23052059Sdfr				continue;
23152059Sdfr			}
23252241Sdfr			large_len = I16(resp);
23352059Sdfr			resp += 2;
23452059Sdfr			scanning -= 2;
23552059Sdfr
23652059Sdfr			if (scanning < large_len) {
23752059Sdfr				scanning = 0;
23852059Sdfr				continue;
23952059Sdfr			}
24052059Sdfr			resinfo = resp;
24152059Sdfr			resp += large_len;
24252059Sdfr			scanning -= large_len;
24352059Sdfr
24452241Sdfr			switch (PNP_LRES_NUM(tag)) {
24552241Sdfr			case PNP_TAG_ID_ANSI:
24652059Sdfr				if (large_len > sizeof(buf) - 1)
24752059Sdfr					large_len = sizeof(buf) - 1;
24852059Sdfr				bcopy(resinfo, buf, large_len);
24952059Sdfr
25052059Sdfr				/*
25158850Sdfr				 * Trim trailing spaces and garbage.
25252059Sdfr				 */
25358850Sdfr				while (large_len > 0 && buf[large_len - 1] <= ' ')
25452059Sdfr					large_len--;
25552059Sdfr				buf[large_len] = '\0';
25652059Sdfr				device_set_desc_copy(dev, buf);
25752241Sdfr				break;
25852241Sdfr
25952241Sdfr			case PNP_TAG_MEMORY_RANGE:
26052241Sdfr				if (bootverbose) {
26158847Sdfr					int temp = I16(resinfo + 7) << 8;
26258847Sdfr
26352241Sdfr					printf("%s: adding memory range "
26452241Sdfr					       "%#x-%#x, size=%#x, "
26552241Sdfr					       "align=%#x\n",
26652241Sdfr					       pnp_eisaformat(id),
26752241Sdfr					       I16(resinfo + 1)<<8,
26858847Sdfr					       (I16(resinfo + 3)<<8) + temp - 1,
26958847Sdfr					       temp,
27052241Sdfr					       I16(resinfo + 5));
27152241Sdfr				}
27252059Sdfr
27352241Sdfr				if (config->ic_nmem == ISA_NMEM) {
27452241Sdfr					device_printf(parent, "too many memory ranges");
27552241Sdfr					scanning = 0;
27652241Sdfr					break;
27752241Sdfr				}
27852059Sdfr
27952241Sdfr				config->ic_mem[config->ic_nmem].ir_start =
28052241Sdfr					I16(resinfo + 1)<<8;
28152241Sdfr				config->ic_mem[config->ic_nmem].ir_end =
28252241Sdfr					(I16(resinfo + 3)<<8)
28358847Sdfr					+ (I16(resinfo + 7) << 8) - 1;
28452241Sdfr				config->ic_mem[config->ic_nmem].ir_size =
28558847Sdfr					I16(resinfo + 7) << 8;
28652241Sdfr				config->ic_mem[config->ic_nmem].ir_align =
28752241Sdfr					I16(resinfo + 5);
28852241Sdfr				if (!config->ic_mem[config->ic_nmem].ir_align)
28952241Sdfr					config->ic_mem[config->ic_nmem]
29052241Sdfr						.ir_align = 0x10000;
29152241Sdfr				config->ic_nmem++;
29252059Sdfr				break;
29352059Sdfr
29452241Sdfr			case PNP_TAG_MEMORY32_RANGE:
29552241Sdfr				if (bootverbose) {
29658850Sdfr					printf("%s: adding memory32 range "
29752241Sdfr					       "%#x-%#x, size=%#x, "
29852241Sdfr					       "align=%#x\n",
29952241Sdfr					       pnp_eisaformat(id),
30052241Sdfr					       I32(resinfo + 1),
30152241Sdfr					       I32(resinfo + 5)
30252241Sdfr					       + I32(resinfo + 13) - 1,
30352241Sdfr					       I32(resinfo + 13),
30452241Sdfr					       I32(resinfo + 9));
30552241Sdfr				}
30652241Sdfr
30752241Sdfr				if (config->ic_nmem == ISA_NMEM) {
30858850Sdfr					device_printf(parent, "too many memory ranges\n");
30952241Sdfr					scanning = 0;
31052241Sdfr					break;
31152241Sdfr				}
31252241Sdfr
31352241Sdfr				config->ic_mem[config->ic_nmem].ir_start =
31452241Sdfr					I32(resinfo + 1);
31552241Sdfr				config->ic_mem[config->ic_nmem].ir_end =
31652241Sdfr					I32(resinfo + 5)
31752241Sdfr					+ I32(resinfo + 13) - 1;
31852241Sdfr				config->ic_mem[config->ic_nmem].ir_size =
31952241Sdfr					I32(resinfo + 13);
32052059Sdfr				config->ic_mem[config->ic_nmem].ir_align =
32152241Sdfr					I32(resinfo + 9);
32252241Sdfr				config->ic_nmem++;
32352241Sdfr				break;
32452241Sdfr
32552241Sdfr			case PNP_TAG_MEMORY32_FIXED:
32652241Sdfr				if (I32(resinfo + 5) == 0) {
32752241Sdfr					if (bootverbose) {
32852241Sdfr						printf("%s: skipping empty range\n",
32952241Sdfr						       pnp_eisaformat(id));
33052241Sdfr					}
33152241Sdfr					continue;
33252241Sdfr				}
33352241Sdfr				if (bootverbose) {
33458850Sdfr					printf("%s: adding fixed memory32 range "
33552241Sdfr					       "%#x-%#x, size=%#x\n",
33652241Sdfr					       pnp_eisaformat(id),
33752241Sdfr					       I32(resinfo + 1),
33852241Sdfr					       I32(resinfo + 1)
33952241Sdfr					       + I32(resinfo + 5) - 1,
34052241Sdfr					       I32(resinfo + 5));
34152241Sdfr				}
34252241Sdfr
34352241Sdfr				if (config->ic_nmem == ISA_NMEM) {
34458850Sdfr					device_printf(parent, "too many memory ranges\n");
34552241Sdfr					scanning = 0;
34652241Sdfr					break;
34752241Sdfr				}
34852241Sdfr
34952241Sdfr				config->ic_mem[config->ic_nmem].ir_start =
35052241Sdfr					I32(resinfo + 1);
35152241Sdfr				config->ic_mem[config->ic_nmem].ir_end =
35252241Sdfr					I32(resinfo + 1)
35352241Sdfr					+ I32(resinfo + 5) - 1;
35452241Sdfr				config->ic_mem[config->ic_nmem].ir_size =
35552241Sdfr					I32(resinfo + 5);
35652241Sdfr				config->ic_mem[config->ic_nmem].ir_align = 1;
35752241Sdfr				config->ic_nmem++;
35852241Sdfr				break;
35952241Sdfr
36052241Sdfr			default:
36152241Sdfr				/* Skip this resource */
36258850Sdfr				device_printf(parent, "unexpected large tag %d\n",
36352241Sdfr					      PNP_SRES_NUM(tag));
36452241Sdfr			}
36552059Sdfr		}
36652059Sdfr	}
36758850Sdfr	if(ncfgs == 1) {
36858850Sdfr		/* Single config without dependants */
36958850Sdfr		(void)ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]);
37058850Sdfr		return;
37158850Sdfr	}
37258850Sdfr	/* Cycle through dependant configs merging primary details */
37358850Sdfr	for(i = 1; i < ncfgs; i++) {
37458850Sdfr		int j;
37558850Sdfr		config = &configs[i];
37658850Sdfr		for(j = 0; j < configs[0].ic_nmem; j++) {
37758850Sdfr			if (config->ic_nmem == ISA_NMEM) {
37858850Sdfr				device_printf(parent, "too many memory ranges\n");
37958850Sdfr				return;
38058850Sdfr			}
38158850Sdfr			config->ic_mem[config->ic_nmem] = configs[0].ic_mem[j];
38258850Sdfr			config->ic_nmem++;
38358850Sdfr		}
38458850Sdfr		for(j = 0; j < configs[0].ic_nport; j++) {
38558850Sdfr			if (config->ic_nport == ISA_NPORT) {
38658850Sdfr				device_printf(parent, "too many port ranges\n");
38758850Sdfr				return;
38858850Sdfr			}
38958850Sdfr			config->ic_port[config->ic_nport] = configs[0].ic_port[j];
39058850Sdfr			config->ic_nport++;
39158850Sdfr		}
39258850Sdfr		for(j = 0; j < configs[0].ic_nirq; j++) {
39358850Sdfr			if (config->ic_nirq == ISA_NIRQ) {
39458850Sdfr				device_printf(parent, "too many irq ranges\n");
39558850Sdfr				return;
39658850Sdfr			}
39758850Sdfr			config->ic_irqmask[config->ic_nirq] = configs[0].ic_irqmask[j];
39858850Sdfr			config->ic_nirq++;
39958850Sdfr		}
40058850Sdfr		for(j = 0; j < configs[0].ic_ndrq; j++) {
40158850Sdfr			if (config->ic_ndrq == ISA_NDRQ) {
40258850Sdfr				device_printf(parent, "too many drq ranges\n");
40358850Sdfr				return;
40458850Sdfr			}
40558850Sdfr			config->ic_drqmask[config->ic_ndrq] = configs[0].ic_drqmask[j];
40658850Sdfr			config->ic_ndrq++;
40758850Sdfr		}
40858850Sdfr		(void)ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]);
40958850Sdfr	}
41052059Sdfr}
411