pnpparse.c revision 59002
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 59002 2000-04-04 07:48:04Z dfr $
2752059Sdfr */
2852059Sdfr
2952059Sdfr#include <sys/param.h>
3052059Sdfr#include <sys/systm.h>
3152059Sdfr#include <sys/kernel.h>
3258873Sdfr#include <sys/malloc.h>
3352059Sdfr#include <sys/module.h>
3452059Sdfr#include <sys/bus.h>
3552059Sdfr#include <isa/isavar.h>
3652059Sdfr#include <isa/pnpreg.h>
3752059Sdfr#include <isa/pnpvar.h>
3852059Sdfr
3958850Sdfr#define	MAXDEP	8
4058850Sdfr
4152241Sdfr#define I16(p)	((p)[0] + ((p)[1] << 8))
4252241Sdfr#define I32(p)	(I16(p) + (I16(p+2) << 16))
4352241Sdfr
4452059Sdfr/*
4552059Sdfr * Parse resource data for Logical Devices.
4652059Sdfr *
4752059Sdfr * This function exits as soon as it gets an error reading *ANY*
4858850Sdfr * Resource Data or it reaches the end of Resource Data.
4952059Sdfr */
5052059Sdfrvoid
5152059Sdfrpnp_parse_resources(device_t dev, u_char *resources, int len)
5252059Sdfr{
5352059Sdfr	device_t parent = device_get_parent(dev);
5452059Sdfr	u_char tag, *resp, *resinfo;
5552059Sdfr	int large_len, scanning = len;
5652241Sdfr	u_int32_t id, compat_id;
5752059Sdfr	struct isa_config *config;
5858850Sdfr	int ncfgs = 1;
5958850Sdfr	int priorities[1 + MAXDEP];
6058873Sdfr	struct isa_config *configs;
6152059Sdfr	char buf[100];
6258850Sdfr	int i;
6352059Sdfr
6452241Sdfr	id = isa_get_logicalid(dev);
6558873Sdfr	configs = (struct isa_config *)malloc(sizeof(*configs) * (1 + MAXDEP),
6658873Sdfr						M_DEVBUF, M_NOWAIT);
6758873Sdfr	if (configs == NULL) {
6858873Sdfr		device_printf(dev, "No memory to parse PNP data\n");
6958873Sdfr		return;
7058873Sdfr	}
7158873Sdfr	bzero(configs, sizeof(*configs) * (1 + MAXDEP));
7258850Sdfr	config = &configs[0];
7358850Sdfr	priorities[0] = 0;
7452059Sdfr	resp = resources;
7552059Sdfr	while (scanning > 0) {
7652059Sdfr		tag = *resp++;
7752059Sdfr		scanning--;
7852059Sdfr		if (PNP_RES_TYPE(tag) == 0) {
7952059Sdfr			/* Small resource */
8052059Sdfr			if (scanning < PNP_SRES_LEN(tag)) {
8152059Sdfr				scanning = 0;
8252059Sdfr				continue;
8352059Sdfr			}
8452059Sdfr			resinfo = resp;
8552059Sdfr			resp += PNP_SRES_LEN(tag);
8652059Sdfr			scanning -= PNP_SRES_LEN(tag);;
8752059Sdfr
8852059Sdfr			switch (PNP_SRES_NUM(tag)) {
8952059Sdfr			case PNP_TAG_COMPAT_DEVICE:
9052059Sdfr				/*
9152059Sdfr				 * Got a compatible device id
9252059Sdfr				 * resource. Should keep a list of
9352059Sdfr				 * compat ids in the device.
9452059Sdfr				 */
9552059Sdfr				bcopy(resinfo, &compat_id, 4);
9652059Sdfr				isa_set_compatid(dev, compat_id);
9752059Sdfr				break;
9852059Sdfr
9952059Sdfr			case PNP_TAG_IRQ_FORMAT:
10052241Sdfr				if (bootverbose) {
10152241Sdfr					printf("%s: adding irq mask %#04x\n",
10252241Sdfr					       pnp_eisaformat(id),
10352241Sdfr					       I16(resinfo));
10452241Sdfr				}
10552059Sdfr				if (config->ic_nirq == ISA_NIRQ) {
10658850Sdfr					device_printf(parent, "too many irqs\n");
10752059Sdfr					scanning = 0;
10852059Sdfr					break;
10952059Sdfr				}
11052059Sdfr				config->ic_irqmask[config->ic_nirq] =
11152241Sdfr					I16(resinfo);
11252059Sdfr				config->ic_nirq++;
11352059Sdfr				break;
11452059Sdfr
11552059Sdfr			case PNP_TAG_DMA_FORMAT:
11652241Sdfr				if (bootverbose) {
11752241Sdfr					printf("%s: adding dma mask %#02x\n",
11852241Sdfr					       pnp_eisaformat(id),
11952241Sdfr					       resinfo[0]);
12052241Sdfr				}
12152059Sdfr				if (config->ic_ndrq == ISA_NDRQ) {
12258850Sdfr					device_printf(parent, "too many drqs\n");
12352059Sdfr					scanning = 0;
12452059Sdfr					break;
12552059Sdfr				}
12652059Sdfr				config->ic_drqmask[config->ic_ndrq] =
12752059Sdfr					resinfo[0];
12852059Sdfr				config->ic_ndrq++;
12952059Sdfr				break;
13052059Sdfr
13152059Sdfr			case PNP_TAG_START_DEPENDANT:
13252241Sdfr				if (bootverbose) {
13352241Sdfr					printf("%s: start dependant\n",
13452241Sdfr					       pnp_eisaformat(id));
13552241Sdfr				}
13659002Sdfr				if (ncfgs > MAXDEP) {
13758850Sdfr					device_printf(parent, "too many dependant configs (%d)\n", MAXDEP);
13852059Sdfr					scanning = 0;
13952059Sdfr					break;
14052059Sdfr				}
14158850Sdfr				config = &configs[ncfgs];
14252059Sdfr				/*
14352059Sdfr				 * If the priority is not specified,
14452059Sdfr				 * then use the default of
14552059Sdfr				 * 'acceptable'
14652059Sdfr				 */
14752059Sdfr				if (PNP_SRES_LEN(tag) > 0)
14858850Sdfr					priorities[ncfgs] = resinfo[0];
14952059Sdfr				else
15058850Sdfr					priorities[ncfgs] = 1;
15158850Sdfr				ncfgs++;
15252059Sdfr				break;
15352059Sdfr
15452059Sdfr			case PNP_TAG_END_DEPENDANT:
15552241Sdfr				if (bootverbose) {
15652241Sdfr					printf("%s: end dependant\n",
15752241Sdfr					       pnp_eisaformat(id));
15852241Sdfr				}
15958850Sdfr				config = &configs[0];	/* back to main config */
16052059Sdfr				break;
16152059Sdfr
16252059Sdfr			case PNP_TAG_IO_RANGE:
16352241Sdfr				if (bootverbose) {
16452241Sdfr					printf("%s: adding io range "
16552241Sdfr					       "%#x-%#x, size=%#x, "
16652241Sdfr					       "align=%#x\n",
16752241Sdfr					       pnp_eisaformat(id),
16852241Sdfr					       I16(resinfo + 1),
16952241Sdfr					       I16(resinfo + 3) + resinfo[6]-1,
17052241Sdfr					       resinfo[6],
17152241Sdfr					       resinfo[5]);
17252241Sdfr				}
17352059Sdfr				if (config->ic_nport == ISA_NPORT) {
17458850Sdfr					device_printf(parent, "too many ports\n");
17552059Sdfr					scanning = 0;
17652059Sdfr					break;
17752059Sdfr				}
17852059Sdfr				config->ic_port[config->ic_nport].ir_start =
17952241Sdfr					I16(resinfo + 1);
18052059Sdfr				config->ic_port[config->ic_nport].ir_end =
18152241Sdfr					I16(resinfo + 3) + resinfo[6] - 1;
18252241Sdfr				config->ic_port[config->ic_nport].ir_size =
18352059Sdfr					resinfo[6];
18452241Sdfr				if (resinfo[5] == 0) {
18552241Sdfr				    /* Make sure align is at least one */
18652241Sdfr				    resinfo[5] = 1;
18752241Sdfr				}
18852059Sdfr				config->ic_port[config->ic_nport].ir_align =
18952059Sdfr					resinfo[5];
19052059Sdfr				config->ic_nport++;
19152059Sdfr				break;
19252059Sdfr
19352059Sdfr			case PNP_TAG_IO_FIXED:
19452241Sdfr				if (bootverbose) {
19558850Sdfr					printf("%s: adding fixed io range "
19652241Sdfr					       "%#x-%#x, size=%#x, "
19752241Sdfr					       "align=%#x\n",
19852241Sdfr					       pnp_eisaformat(id),
19952241Sdfr					       I16(resinfo),
20052241Sdfr					       I16(resinfo) + resinfo[2] - 1,
20152241Sdfr					       resinfo[2],
20252241Sdfr					       1);
20352241Sdfr				}
20452059Sdfr				if (config->ic_nport == ISA_NPORT) {
20558850Sdfr					device_printf(parent, "too many ports\n");
20652059Sdfr					scanning = 0;
20752059Sdfr					break;
20852059Sdfr				}
20952059Sdfr				config->ic_port[config->ic_nport].ir_start =
21052241Sdfr					I16(resinfo);
21152059Sdfr				config->ic_port[config->ic_nport].ir_end =
21252241Sdfr					I16(resinfo) + resinfo[2] - 1;
21352059Sdfr				config->ic_port[config->ic_nport].ir_size
21452059Sdfr					= resinfo[2];
21552059Sdfr				config->ic_port[config->ic_nport].ir_align = 1;
21652059Sdfr				config->ic_nport++;
21752059Sdfr				break;
21852059Sdfr
21952241Sdfr			case PNP_TAG_END:
22052241Sdfr				if (bootverbose) {
22158850Sdfr					printf("%s: end config\n",
22252241Sdfr					       pnp_eisaformat(id));
22352241Sdfr				}
22452241Sdfr				scanning = 0;
22552241Sdfr				break;
22652241Sdfr
22752059Sdfr			default:
22852059Sdfr				/* Skip this resource */
22958850Sdfr				device_printf(parent, "unexpected small tag %d\n",
23052059Sdfr					      PNP_SRES_NUM(tag));
23152059Sdfr				break;
23252059Sdfr			}
23352059Sdfr		} else {
23452059Sdfr			/* Large resource */
23552059Sdfr			if (scanning < 2) {
23652059Sdfr				scanning = 0;
23752059Sdfr				continue;
23852059Sdfr			}
23952241Sdfr			large_len = I16(resp);
24052059Sdfr			resp += 2;
24152059Sdfr			scanning -= 2;
24252059Sdfr
24352059Sdfr			if (scanning < large_len) {
24452059Sdfr				scanning = 0;
24552059Sdfr				continue;
24652059Sdfr			}
24752059Sdfr			resinfo = resp;
24852059Sdfr			resp += large_len;
24952059Sdfr			scanning -= large_len;
25052059Sdfr
25152241Sdfr			switch (PNP_LRES_NUM(tag)) {
25252241Sdfr			case PNP_TAG_ID_ANSI:
25352059Sdfr				if (large_len > sizeof(buf) - 1)
25452059Sdfr					large_len = sizeof(buf) - 1;
25552059Sdfr				bcopy(resinfo, buf, large_len);
25652059Sdfr
25752059Sdfr				/*
25858850Sdfr				 * Trim trailing spaces and garbage.
25952059Sdfr				 */
26058850Sdfr				while (large_len > 0 && buf[large_len - 1] <= ' ')
26152059Sdfr					large_len--;
26252059Sdfr				buf[large_len] = '\0';
26352059Sdfr				device_set_desc_copy(dev, buf);
26452241Sdfr				break;
26552241Sdfr
26652241Sdfr			case PNP_TAG_MEMORY_RANGE:
26752241Sdfr				if (bootverbose) {
26858847Sdfr					int temp = I16(resinfo + 7) << 8;
26958847Sdfr
27052241Sdfr					printf("%s: adding memory range "
27152241Sdfr					       "%#x-%#x, size=%#x, "
27252241Sdfr					       "align=%#x\n",
27352241Sdfr					       pnp_eisaformat(id),
27452241Sdfr					       I16(resinfo + 1)<<8,
27558847Sdfr					       (I16(resinfo + 3)<<8) + temp - 1,
27658847Sdfr					       temp,
27752241Sdfr					       I16(resinfo + 5));
27852241Sdfr				}
27952059Sdfr
28052241Sdfr				if (config->ic_nmem == ISA_NMEM) {
28158873Sdfr					device_printf(parent, "too many memory ranges\n");
28252241Sdfr					scanning = 0;
28352241Sdfr					break;
28452241Sdfr				}
28552059Sdfr
28652241Sdfr				config->ic_mem[config->ic_nmem].ir_start =
28752241Sdfr					I16(resinfo + 1)<<8;
28852241Sdfr				config->ic_mem[config->ic_nmem].ir_end =
28952241Sdfr					(I16(resinfo + 3)<<8)
29058847Sdfr					+ (I16(resinfo + 7) << 8) - 1;
29152241Sdfr				config->ic_mem[config->ic_nmem].ir_size =
29258847Sdfr					I16(resinfo + 7) << 8;
29352241Sdfr				config->ic_mem[config->ic_nmem].ir_align =
29452241Sdfr					I16(resinfo + 5);
29552241Sdfr				if (!config->ic_mem[config->ic_nmem].ir_align)
29652241Sdfr					config->ic_mem[config->ic_nmem]
29752241Sdfr						.ir_align = 0x10000;
29852241Sdfr				config->ic_nmem++;
29952059Sdfr				break;
30052059Sdfr
30152241Sdfr			case PNP_TAG_MEMORY32_RANGE:
30252241Sdfr				if (bootverbose) {
30358850Sdfr					printf("%s: adding memory32 range "
30452241Sdfr					       "%#x-%#x, size=%#x, "
30552241Sdfr					       "align=%#x\n",
30652241Sdfr					       pnp_eisaformat(id),
30752241Sdfr					       I32(resinfo + 1),
30852241Sdfr					       I32(resinfo + 5)
30952241Sdfr					       + I32(resinfo + 13) - 1,
31052241Sdfr					       I32(resinfo + 13),
31152241Sdfr					       I32(resinfo + 9));
31252241Sdfr				}
31352241Sdfr
31452241Sdfr				if (config->ic_nmem == ISA_NMEM) {
31558850Sdfr					device_printf(parent, "too many memory ranges\n");
31652241Sdfr					scanning = 0;
31752241Sdfr					break;
31852241Sdfr				}
31952241Sdfr
32052241Sdfr				config->ic_mem[config->ic_nmem].ir_start =
32152241Sdfr					I32(resinfo + 1);
32252241Sdfr				config->ic_mem[config->ic_nmem].ir_end =
32352241Sdfr					I32(resinfo + 5)
32452241Sdfr					+ I32(resinfo + 13) - 1;
32552241Sdfr				config->ic_mem[config->ic_nmem].ir_size =
32652241Sdfr					I32(resinfo + 13);
32752059Sdfr				config->ic_mem[config->ic_nmem].ir_align =
32852241Sdfr					I32(resinfo + 9);
32952241Sdfr				config->ic_nmem++;
33052241Sdfr				break;
33152241Sdfr
33252241Sdfr			case PNP_TAG_MEMORY32_FIXED:
33352241Sdfr				if (I32(resinfo + 5) == 0) {
33452241Sdfr					if (bootverbose) {
33552241Sdfr						printf("%s: skipping empty range\n",
33652241Sdfr						       pnp_eisaformat(id));
33752241Sdfr					}
33852241Sdfr					continue;
33952241Sdfr				}
34052241Sdfr				if (bootverbose) {
34158850Sdfr					printf("%s: adding fixed memory32 range "
34252241Sdfr					       "%#x-%#x, size=%#x\n",
34352241Sdfr					       pnp_eisaformat(id),
34452241Sdfr					       I32(resinfo + 1),
34552241Sdfr					       I32(resinfo + 1)
34652241Sdfr					       + I32(resinfo + 5) - 1,
34752241Sdfr					       I32(resinfo + 5));
34852241Sdfr				}
34952241Sdfr
35052241Sdfr				if (config->ic_nmem == ISA_NMEM) {
35158850Sdfr					device_printf(parent, "too many memory ranges\n");
35252241Sdfr					scanning = 0;
35352241Sdfr					break;
35452241Sdfr				}
35552241Sdfr
35652241Sdfr				config->ic_mem[config->ic_nmem].ir_start =
35752241Sdfr					I32(resinfo + 1);
35852241Sdfr				config->ic_mem[config->ic_nmem].ir_end =
35952241Sdfr					I32(resinfo + 1)
36052241Sdfr					+ I32(resinfo + 5) - 1;
36152241Sdfr				config->ic_mem[config->ic_nmem].ir_size =
36252241Sdfr					I32(resinfo + 5);
36352241Sdfr				config->ic_mem[config->ic_nmem].ir_align = 1;
36452241Sdfr				config->ic_nmem++;
36552241Sdfr				break;
36652241Sdfr
36752241Sdfr			default:
36852241Sdfr				/* Skip this resource */
36958850Sdfr				device_printf(parent, "unexpected large tag %d\n",
37052241Sdfr					      PNP_SRES_NUM(tag));
37152241Sdfr			}
37252059Sdfr		}
37352059Sdfr	}
37458850Sdfr	if(ncfgs == 1) {
37558850Sdfr		/* Single config without dependants */
37658850Sdfr		(void)ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]);
37758873Sdfr		free(configs, M_DEVBUF);
37858850Sdfr		return;
37958850Sdfr	}
38058850Sdfr	/* Cycle through dependant configs merging primary details */
38158850Sdfr	for(i = 1; i < ncfgs; i++) {
38258850Sdfr		int j;
38358850Sdfr		config = &configs[i];
38458850Sdfr		for(j = 0; j < configs[0].ic_nmem; j++) {
38558850Sdfr			if (config->ic_nmem == ISA_NMEM) {
38658850Sdfr				device_printf(parent, "too many memory ranges\n");
38758873Sdfr				free(configs, M_DEVBUF);
38858850Sdfr				return;
38958850Sdfr			}
39058850Sdfr			config->ic_mem[config->ic_nmem] = configs[0].ic_mem[j];
39158850Sdfr			config->ic_nmem++;
39258850Sdfr		}
39358850Sdfr		for(j = 0; j < configs[0].ic_nport; j++) {
39458850Sdfr			if (config->ic_nport == ISA_NPORT) {
39558850Sdfr				device_printf(parent, "too many port ranges\n");
39658873Sdfr				free(configs, M_DEVBUF);
39758850Sdfr				return;
39858850Sdfr			}
39958850Sdfr			config->ic_port[config->ic_nport] = configs[0].ic_port[j];
40058850Sdfr			config->ic_nport++;
40158850Sdfr		}
40258850Sdfr		for(j = 0; j < configs[0].ic_nirq; j++) {
40358850Sdfr			if (config->ic_nirq == ISA_NIRQ) {
40458850Sdfr				device_printf(parent, "too many irq ranges\n");
40558873Sdfr				free(configs, M_DEVBUF);
40658850Sdfr				return;
40758850Sdfr			}
40858850Sdfr			config->ic_irqmask[config->ic_nirq] = configs[0].ic_irqmask[j];
40958850Sdfr			config->ic_nirq++;
41058850Sdfr		}
41158850Sdfr		for(j = 0; j < configs[0].ic_ndrq; j++) {
41258850Sdfr			if (config->ic_ndrq == ISA_NDRQ) {
41358850Sdfr				device_printf(parent, "too many drq ranges\n");
41458873Sdfr				free(configs, M_DEVBUF);
41558850Sdfr				return;
41658850Sdfr			}
41758850Sdfr			config->ic_drqmask[config->ic_ndrq] = configs[0].ic_drqmask[j];
41858850Sdfr			config->ic_ndrq++;
41958850Sdfr		}
42058850Sdfr		(void)ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]);
42158850Sdfr	}
42258873Sdfr	free(configs, M_DEVBUF);
42352059Sdfr}
424