summaryrefslogtreecommitdiffstats
path: root/sys/isa
diff options
context:
space:
mode:
authoryokota <yokota@FreeBSD.org>2001-09-05 03:54:33 +0000
committeryokota <yokota@FreeBSD.org>2001-09-05 03:54:33 +0000
commitfb9afad90ef704606abdfb01a279a685bd8483b7 (patch)
treed38fcd9f070b41f11a15c00daf65269c937a5a90 /sys/isa
parent72b8fa9d630b906a60463c3dd1d89e435769debe (diff)
downloadFreeBSD-src-fb9afad90ef704606abdfb01a279a685bd8483b7.zip
FreeBSD-src-fb9afad90ef704606abdfb01a279a685bd8483b7.tar.gz
Rework the ISA PnP driver pnp and the PnP resource parser to fix
the following bugs. - When constructing a resource configuration, respect the order in which resource descriptors are read, in order to establish the correct mapping between the descriptors and configuration registers. "Plug and Play ISA Specification, Version 1.0a", Sec 4.6.1, May 5, 1994. "Clarifications to the Plug and Play ISA Specification, Version 1.0a", Sec 6.2.1, Dec. 10, 1994. - Do not ignore null (empty) descriptors; they are valid descriptors acting as filler. "Clarifications to the Plug and Play ISA Specification, Version 1.0a", Sec 6.2.1. - Correctly set up logical device configuration registers for null resources. "Clarifications to the Plug and Play ISA Specification, Version 1.0a" - Handle null resources properly in the resource allocator for the ISA bus.
Diffstat (limited to 'sys/isa')
-rw-r--r--sys/isa/isa_common.c94
-rw-r--r--sys/isa/isa_common.h2
-rw-r--r--sys/isa/isavar.h7
-rw-r--r--sys/isa/pnp.c73
-rw-r--r--sys/isa/pnpparse.c823
-rw-r--r--sys/isa/pnpvar.h10
6 files changed, 659 insertions, 350 deletions
diff --git a/sys/isa/isa_common.c b/sys/isa/isa_common.c
index 22677e3..d5aee38 100644
--- a/sys/isa/isa_common.c
+++ b/sys/isa/isa_common.c
@@ -133,9 +133,20 @@ isa_find_memory(device_t child,
result->ic_nmem = config->ic_nmem;
for (i = 0; i < config->ic_nmem; i++) {
u_int32_t start, end, size, align;
+
+ size = config->ic_mem[i].ir_size;
+
+ /* the PnP device may have a null resource as filler */
+ if (size == 0) {
+ result->ic_mem[i].ir_start = 0;
+ result->ic_mem[i].ir_end = 0;
+ result->ic_mem[i].ir_size = 0;
+ result->ic_mem[i].ir_align = 0;
+ continue;
+ }
+
for (start = config->ic_mem[i].ir_start,
end = config->ic_mem[i].ir_end,
- size = config->ic_mem[i].ir_size,
align = config->ic_mem[i].ir_align;
start + size - 1 <= end;
start += align) {
@@ -197,9 +208,20 @@ isa_find_port(device_t child,
result->ic_nport = config->ic_nport;
for (i = 0; i < config->ic_nport; i++) {
u_int32_t start, end, size, align;
+
+ size = config->ic_port[i].ir_size;
+
+ /* the PnP device may have a null resource as filler */
+ if (size == 0) {
+ result->ic_port[i].ir_start = 0;
+ result->ic_port[i].ir_end = 0;
+ result->ic_port[i].ir_size = 0;
+ result->ic_port[i].ir_align = 0;
+ continue;
+ }
+
for (start = config->ic_port[i].ir_start,
end = config->ic_port[i].ir_end,
- size = config->ic_port[i].ir_size,
align = config->ic_port[i].ir_align;
start + size - 1 <= end;
start += align) {
@@ -285,6 +307,13 @@ isa_find_irq(device_t child,
for (i = 0; i < config->ic_nirq; i++) {
u_int32_t mask = config->ic_irqmask[i];
int irq;
+
+ /* the PnP device may have a null resource as filler */
+ if (mask == 0) {
+ result->ic_irqmask[i] = 0;
+ continue;
+ }
+
for (irq = find_first_bit(mask);
irq != -1;
irq = find_next_bit(mask, irq)) {
@@ -344,6 +373,13 @@ isa_find_drq(device_t child,
for (i = 0; i < config->ic_ndrq; i++) {
u_int32_t mask = config->ic_drqmask[i];
int drq;
+
+ /* the PnP device may have a null resource as filler */
+ if (mask == 0) {
+ result->ic_drqmask[i] = 0;
+ continue;
+ }
+
for (drq = find_first_bit(mask);
drq != -1;
drq = find_next_bit(mask, drq)) {
@@ -430,6 +466,55 @@ isa_assign_resources(device_t child)
}
/*
+ * Return non-zero if the device has a single configuration, that is,
+ * a fixed set of resoruces.
+ */
+static int
+isa_has_single_config(device_t dev)
+{
+ struct isa_device *idev = DEVTOISA(dev);
+ struct isa_config_entry *ice;
+ u_int32_t mask;
+ int i;
+
+ ice = TAILQ_FIRST(&idev->id_configs);
+ if (TAILQ_NEXT(ice, ice_link))
+ return 0;
+
+ for (i = 0; i < ice->ice_config.ic_nmem; ++i) {
+ if (ice->ice_config.ic_mem[i].ir_size == 0)
+ continue;
+ if (ice->ice_config.ic_mem[i].ir_end !=
+ ice->ice_config.ic_mem[i].ir_start +
+ ice->ice_config.ic_mem[i].ir_size - 1)
+ return 0;
+ }
+ for (i = 0; i < ice->ice_config.ic_nport; ++i) {
+ if (ice->ice_config.ic_port[i].ir_size == 0)
+ continue;
+ if (ice->ice_config.ic_port[i].ir_end !=
+ ice->ice_config.ic_port[i].ir_start +
+ ice->ice_config.ic_port[i].ir_size - 1)
+ return 0;
+ }
+ for (i = 0; i < ice->ice_config.ic_nirq; ++i) {
+ mask = ice->ice_config.ic_irqmask[i];
+ if (mask == 0)
+ continue;
+ if (find_next_bit(mask, find_first_bit(mask)) != -1)
+ return 0;
+ }
+ for (i = 0; i < ice->ice_config.ic_ndrq; ++i) {
+ mask = ice->ice_config.ic_drqmask[i];
+ if (mask == 0)
+ continue;
+ if (find_next_bit(mask, find_first_bit(mask)) != -1)
+ return 0;
+ }
+ return 1;
+}
+
+/*
* Called after other devices have initialised to probe for isa devices.
*/
void
@@ -962,6 +1047,11 @@ isa_add_config(device_t dev, device_t child,
else
TAILQ_INSERT_TAIL(&idev->id_configs, newice, ice_link);
+ if (isa_has_single_config(child))
+ idev->id_config_attr &= ~ISACFGATTR_MULTI;
+ else
+ idev->id_config_attr |= ISACFGATTR_MULTI;
+
return 0;
}
diff --git a/sys/isa/isa_common.h b/sys/isa/isa_common.h
index b940d14..a2f39f8 100644
--- a/sys/isa/isa_common.h
+++ b/sys/isa/isa_common.h
@@ -58,8 +58,6 @@ struct isa_device {
isa_config_cb *id_config_cb; /* callback function */
void *id_config_arg; /* callback argument */
int id_config_attr; /* pnp config attributes */
-#define ISACFGATTR_CANDISABLE (1 << 0) /* can be disabled */
-#define ISACFGATTR_DYNAMIC (1 << 1) /* dynamic configuration */
};
#define DEVTOISA(dev) ((struct isa_device *) device_get_ivars(dev))
diff --git a/sys/isa/isavar.h b/sys/isa/isavar.h
index d81685c..fa27c3c 100644
--- a/sys/isa/isavar.h
+++ b/sys/isa/isavar.h
@@ -128,6 +128,13 @@ enum isa_device_ivars {
};
/*
+ * ISA_IVAR_CONFIGATTR bits
+ */
+#define ISACFGATTR_CANDISABLE (1 << 0) /* can be disabled */
+#define ISACFGATTR_DYNAMIC (1 << 1) /* dynamic configuration */
+#define ISACFGATTR_MULTI (1 << 2) /* multiple configurations */
+
+/*
* Simplified accessors for isa devices
*/
#define ISA_ACCESSOR(A, B, T) \
diff --git a/sys/isa/pnp.c b/sys/isa/pnp.c
index 6a58ddf..de37620 100644
--- a/sys/isa/pnp.c
+++ b/sys/isa/pnp.c
@@ -340,14 +340,25 @@ pnp_set_config(void *arg, struct isa_config *config, int enable)
* Now program the resources.
*/
for (i = 0; i < config->ic_nmem; i++) {
- u_int32_t start = config->ic_mem[i].ir_start;
- u_int32_t size = config->ic_mem[i].ir_size;
- if (start & 0xff)
- panic("pnp_set_config: bogus memory assignment");
- pnp_write(PNP_MEM_BASE_HIGH(i), (start >> 16) & 0xff);
- pnp_write(PNP_MEM_BASE_LOW(i), (start >> 8) & 0xff);
- pnp_write(PNP_MEM_RANGE_HIGH(i), (size >> 16) & 0xff);
- pnp_write(PNP_MEM_RANGE_LOW(i), (size >> 8) & 0xff);
+ u_int32_t start;
+ u_int32_t size;
+
+ /* XXX: should handle memory control register, 32 bit memory */
+ if (config->ic_mem[i].ir_size == 0) {
+ pnp_write(PNP_MEM_BASE_HIGH(i), 0);
+ pnp_write(PNP_MEM_BASE_LOW(i), 0);
+ pnp_write(PNP_MEM_RANGE_HIGH(i), 0);
+ pnp_write(PNP_MEM_RANGE_LOW(i), 0);
+ } else {
+ start = config->ic_mem[i].ir_start;
+ size = config->ic_mem[i].ir_size;
+ if (start & 0xff)
+ panic("pnp_set_config: bogus memory assignment");
+ pnp_write(PNP_MEM_BASE_HIGH(i), (start >> 16) & 0xff);
+ pnp_write(PNP_MEM_BASE_LOW(i), (start >> 8) & 0xff);
+ pnp_write(PNP_MEM_RANGE_HIGH(i), (size >> 16) & 0xff);
+ pnp_write(PNP_MEM_RANGE_LOW(i), (size >> 8) & 0xff);
+ }
}
for (; i < ISA_PNP_NMEM; i++) {
pnp_write(PNP_MEM_BASE_HIGH(i), 0);
@@ -357,9 +368,16 @@ pnp_set_config(void *arg, struct isa_config *config, int enable)
}
for (i = 0; i < config->ic_nport; i++) {
- u_int32_t start = config->ic_port[i].ir_start;
- pnp_write(PNP_IO_BASE_HIGH(i), (start >> 8) & 0xff);
- pnp_write(PNP_IO_BASE_LOW(i), (start >> 0) & 0xff);
+ u_int32_t start;
+
+ if (config->ic_port[i].ir_size == 0) {
+ pnp_write(PNP_IO_BASE_HIGH(i), 0);
+ pnp_write(PNP_IO_BASE_LOW(i), 0);
+ } else {
+ start = config->ic_port[i].ir_start;
+ pnp_write(PNP_IO_BASE_HIGH(i), (start >> 8) & 0xff);
+ pnp_write(PNP_IO_BASE_LOW(i), (start >> 0) & 0xff);
+ }
}
for (; i < ISA_PNP_NPORT; i++) {
pnp_write(PNP_IO_BASE_HIGH(i), 0);
@@ -367,9 +385,17 @@ pnp_set_config(void *arg, struct isa_config *config, int enable)
}
for (i = 0; i < config->ic_nirq; i++) {
- int irq = ffs(config->ic_irqmask[i]) - 1;
- pnp_write(PNP_IRQ_LEVEL(i), irq);
- pnp_write(PNP_IRQ_TYPE(i), 2); /* XXX */
+ int irq;
+
+ /* XXX: interrupt type */
+ if (config->ic_irqmask[i] == 0) {
+ pnp_write(PNP_IRQ_LEVEL(i), 0);
+ pnp_write(PNP_IRQ_TYPE(i), 2);
+ } else {
+ irq = ffs(config->ic_irqmask[i]) - 1;
+ pnp_write(PNP_IRQ_LEVEL(i), irq);
+ pnp_write(PNP_IRQ_TYPE(i), 2); /* XXX */
+ }
}
for (; i < ISA_PNP_NIRQ; i++) {
/*
@@ -377,11 +403,18 @@ pnp_set_config(void *arg, struct isa_config *config, int enable)
* represents no interrupt selection.
*/
pnp_write(PNP_IRQ_LEVEL(i), 0);
+ pnp_write(PNP_IRQ_TYPE(i), 2);
}
for (i = 0; i < config->ic_ndrq; i++) {
- int drq = ffs(config->ic_drqmask[i]) - 1;
- pnp_write(PNP_DMA_CHANNEL(i), drq);
+ int drq;
+
+ if (config->ic_drqmask[i] == 0) {
+ pnp_write(PNP_DMA_CHANNEL(i), 4);
+ } else {
+ drq = ffs(config->ic_drqmask[i]) - 1;
+ pnp_write(PNP_DMA_CHANNEL(i), drq);
+ }
}
for (; i < ISA_PNP_NDRQ; i++) {
/*
@@ -517,7 +550,7 @@ pnp_create_devices(device_t parent, pnp_id *p, int csn,
if (startres) {
pnp_parse_resources(dev, startres,
resinfo - startres - 1,
- p->vendor_id, logical_id, ldn);
+ ldn);
dev = 0;
startres = 0;
}
@@ -535,6 +568,9 @@ pnp_create_devices(device_t parent, pnp_id *p, int csn,
isa_set_vendorid(dev, p->vendor_id);
isa_set_serial(dev, p->serial);
isa_set_logicalid(dev, logical_id);
+ isa_set_configattr(dev,
+ ISACFGATTR_CANDISABLE |
+ ISACFGATTR_DYNAMIC);
csnldn = malloc(sizeof *csnldn, M_DEVBUF, M_NOWAIT);
if (!csnldn) {
device_printf(parent,
@@ -558,8 +594,7 @@ pnp_create_devices(device_t parent, pnp_id *p, int csn,
break;
}
pnp_parse_resources(dev, startres,
- resinfo - startres - 1,
- p->vendor_id, logical_id, ldn);
+ resinfo - startres - 1, ldn);
dev = 0;
startres = 0;
scanning = 0;
diff --git a/sys/isa/pnpparse.c b/sys/isa/pnpparse.c
index 976b0eb..9efc2fe 100644
--- a/sys/isa/pnpparse.c
+++ b/sys/isa/pnpparse.c
@@ -31,6 +31,9 @@
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/bus.h>
+
+#include <machine/stdarg.h>
+
#include <isa/isavar.h>
#include <isa/pnpreg.h>
#include <isa/pnpvar.h>
@@ -40,394 +43,562 @@
#define I16(p) ((p)[0] + ((p)[1] << 8))
#define I32(p) (I16(p) + (I16(p+2) << 16))
+void
+pnp_printf(u_int32_t id, char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ printf("%s: ", pnp_eisaformat(id));
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+/* parse a single descriptor */
+
+static int
+pnp_parse_desc(device_t dev, u_char tag, u_char *res, int len,
+ struct isa_config *config, int ldn)
+{
+ char buf[100];
+ u_int32_t id;
+ u_int32_t compat_id;
+ int temp;
+
+ id = isa_get_logicalid(dev);
+
+ if (PNP_RES_TYPE(tag) == 0) {
+
+ /* Small resource */
+ switch (PNP_SRES_NUM(tag)) {
+
+ case PNP_TAG_VERSION:
+ case PNP_TAG_VENDOR:
+ /* these descriptors are quietly ignored */
+ break;
+
+ case PNP_TAG_LOGICAL_DEVICE:
+ case PNP_TAG_START_DEPENDANT:
+ case PNP_TAG_END_DEPENDANT:
+ if (bootverbose)
+ pnp_printf(id, "unexpected small tag %d\n",
+ PNP_SRES_NUM(tag));
+ /* shouldn't happen; quit now */
+ return (1);
+
+ case PNP_TAG_COMPAT_DEVICE:
+ /*
+ * Got a compatible device id resource.
+ * Should keep a list of compat ids in the device.
+ */
+ bcopy(res, &compat_id, 4);
+ if (isa_get_compatid(dev) == 0)
+ isa_set_compatid(dev, compat_id);
+ break;
+
+ case PNP_TAG_IRQ_FORMAT:
+ if (config->ic_nirq == ISA_NIRQ) {
+ pnp_printf(id, "too many irqs\n");
+ return (1);
+ }
+ if (I16(res) == 0) {
+ /* a null descriptor */
+ config->ic_irqmask[config->ic_nirq] = 0;
+ config->ic_nirq++;
+ break;
+ }
+ if (bootverbose)
+ pnp_printf(id, "adding irq mask %#02x\n",
+ I16(res));
+ config->ic_irqmask[config->ic_nirq] = I16(res);
+ config->ic_nirq++;
+ break;
+
+ case PNP_TAG_DMA_FORMAT:
+ if (config->ic_ndrq == ISA_NDRQ) {
+ pnp_printf(id, "too many drqs\n");
+ return (1);
+ }
+ if (res[0] == 0) {
+ /* a null descriptor */
+ config->ic_drqmask[config->ic_ndrq] = 0;
+ config->ic_ndrq++;
+ break;
+ }
+ if (bootverbose)
+ pnp_printf(id, "adding dma mask %#02x\n",
+ res[0]);
+ config->ic_drqmask[config->ic_ndrq] = res[0];
+ config->ic_ndrq++;
+ break;
+
+ case PNP_TAG_IO_RANGE:
+ if (config->ic_nport == ISA_NPORT) {
+ pnp_printf(id, "too many ports\n");
+ return (1);
+ }
+ if (res[6] == 0) {
+ /* a null descriptor */
+ config->ic_port[config->ic_nport].ir_start = 0;
+ config->ic_port[config->ic_nport].ir_end = 0;
+ config->ic_port[config->ic_nport].ir_size = 0;
+ config->ic_port[config->ic_nport].ir_align = 0;
+ config->ic_nport++;
+ break;
+ }
+ if (bootverbose) {
+ pnp_printf(id, "adding io range "
+ "%#x-%#x, size=%#x, "
+ "align=%#x\n",
+ I16(res + 1),
+ I16(res + 3) + res[6]-1,
+ res[6], res[5]);
+ }
+ config->ic_port[config->ic_nport].ir_start =
+ I16(res + 1);
+ config->ic_port[config->ic_nport].ir_end =
+ I16(res + 3) + res[6] - 1;
+ config->ic_port[config->ic_nport].ir_size = res[6];
+ if (res[5] == 0) {
+ /* Make sure align is at least one */
+ res[5] = 1;
+ }
+ config->ic_port[config->ic_nport].ir_align = res[5];
+ config->ic_nport++;
+ pnp_check_quirks(isa_get_vendorid(dev),
+ isa_get_logicalid(dev), ldn, config);
+ break;
+
+ case PNP_TAG_IO_FIXED:
+ if (config->ic_nport == ISA_NPORT) {
+ pnp_printf(id, "too many ports\n");
+ return (1);
+ }
+ if (res[2] == 0) {
+ /* a null descriptor */
+ config->ic_port[config->ic_nport].ir_start = 0;
+ config->ic_port[config->ic_nport].ir_end = 0;
+ config->ic_port[config->ic_nport].ir_size = 0;
+ config->ic_port[config->ic_nport].ir_align = 0;
+ config->ic_nport++;
+ break;
+ }
+ if (bootverbose) {
+ pnp_printf(id, "adding fixed io range "
+ "%#x-%#x, size=%#x, "
+ "align=%#x\n",
+ I16(res),
+ I16(res) + res[2] - 1,
+ res[2], 1);
+ }
+ config->ic_port[config->ic_nport].ir_start = I16(res);
+ config->ic_port[config->ic_nport].ir_end =
+ I16(res) + res[2] - 1;
+ config->ic_port[config->ic_nport].ir_size = res[2];
+ config->ic_port[config->ic_nport].ir_align = 1;
+ config->ic_nport++;
+ break;
+
+ case PNP_TAG_END:
+ if (bootverbose)
+ pnp_printf(id, "end config\n");
+ return (1);
+
+ default:
+ /* Skip this resource */
+ pnp_printf(id, "unexpected small tag %d\n",
+ PNP_SRES_NUM(tag));
+ break;
+ }
+ } else {
+ /* Large resource */
+ switch (PNP_LRES_NUM(tag)) {
+
+ case PNP_TAG_ID_UNICODE:
+ case PNP_TAG_LARGE_VENDOR:
+ /* these descriptors are quietly ignored */
+ break;
+
+ case PNP_TAG_ID_ANSI:
+ if (len > sizeof(buf) - 1)
+ len = sizeof(buf) - 1;
+ bcopy(res, buf, len);
+
+ /*
+ * Trim trailing spaces and garbage.
+ */
+ while (len > 0 && buf[len - 1] <= ' ')
+ len--;
+ buf[len] = '\0';
+ device_set_desc_copy(dev, buf);
+ break;
+
+ case PNP_TAG_MEMORY_RANGE:
+ if (config->ic_nmem == ISA_NMEM) {
+ pnp_printf(id, "too many memory ranges\n");
+ return (1);
+ }
+ if (I16(res + 7) == 0) {
+ /* a null descriptor */
+ config->ic_mem[config->ic_nmem].ir_start = 0;
+ config->ic_mem[config->ic_nmem].ir_end = 0;
+ config->ic_mem[config->ic_nmem].ir_size = 0;
+ config->ic_mem[config->ic_nmem].ir_align = 0;
+ config->ic_nmem++;
+ break;
+ }
+ if (bootverbose) {
+ temp = I16(res + 7) << 8;
+ pnp_printf(id, "adding memory range "
+ "%#x-%#x, size=%#x, "
+ "align=%#x\n",
+ I16(res + 1) << 8,
+ (I16(res + 3) << 8) + temp - 1,
+ temp, I16(res + 5));
+ }
+ config->ic_mem[config->ic_nmem].ir_start =
+ I16(res + 1) << 8;
+ config->ic_mem[config->ic_nmem].ir_end =
+ (I16(res + 3) << 8) + (I16(res + 7) << 8) - 1;
+ config->ic_mem[config->ic_nmem].ir_size =
+ I16(res + 7) << 8;
+ config->ic_mem[config->ic_nmem].ir_align = I16(res + 5);
+ if (!config->ic_mem[config->ic_nmem].ir_align)
+ config->ic_mem[config->ic_nmem].ir_align =
+ 0x10000;
+ config->ic_nmem++;
+ break;
+
+ case PNP_TAG_MEMORY32_RANGE:
+ if (config->ic_nmem == ISA_NMEM) {
+ pnp_printf(id, "too many memory ranges\n");
+ return (1);
+ }
+ if (I32(res + 13) == 0) {
+ /* a null descriptor */
+ config->ic_mem[config->ic_nmem].ir_start = 0;
+ config->ic_mem[config->ic_nmem].ir_end = 0;
+ config->ic_mem[config->ic_nmem].ir_size = 0;
+ config->ic_mem[config->ic_nmem].ir_align = 0;
+ config->ic_nmem++;
+ break;
+ }
+ if (bootverbose) {
+ pnp_printf(id, "adding memory32 range "
+ "%#x-%#x, size=%#x, "
+ "align=%#x\n",
+ I32(res + 1),
+ I32(res + 5) + I32(res + 13) - 1,
+ I32(res + 13), I32(res + 9));
+ }
+ config->ic_mem[config->ic_nmem].ir_start = I32(res + 1);
+ config->ic_mem[config->ic_nmem].ir_end =
+ I32(res + 5) + I32(res + 13) - 1;
+ config->ic_mem[config->ic_nmem].ir_size = I32(res + 13);
+ config->ic_mem[config->ic_nmem].ir_align = I32(res + 9);
+ config->ic_nmem++;
+ break;
+
+ case PNP_TAG_MEMORY32_FIXED:
+ if (config->ic_nmem == ISA_NMEM) {
+ pnp_printf(id, "too many memory ranges\n");
+ return (1);
+ }
+ if (I32(res + 5) == 0) {
+ /* a null descriptor */
+ config->ic_mem[config->ic_nmem].ir_start = 0;
+ config->ic_mem[config->ic_nmem].ir_end = 0;
+ config->ic_mem[config->ic_nmem].ir_size = 0;
+ config->ic_mem[config->ic_nmem].ir_align = 0;
+ break;
+ }
+ if (bootverbose) {
+ pnp_printf(id, "adding fixed memory32 range "
+ "%#x-%#x, size=%#x\n",
+ I32(res + 1),
+ I32(res + 1) + I32(res + 5) - 1,
+ I32(res + 5));
+ }
+ config->ic_mem[config->ic_nmem].ir_start = I32(res + 1);
+ config->ic_mem[config->ic_nmem].ir_end =
+ I32(res + 1) + I32(res + 5) - 1;
+ config->ic_mem[config->ic_nmem].ir_size = I32(res + 5);
+ config->ic_mem[config->ic_nmem].ir_align = 1;
+ config->ic_nmem++;
+ break;
+
+ default:
+ /* Skip this resource */
+ pnp_printf(id, "unexpected large tag %d\n",
+ PNP_SRES_NUM(tag));
+ break;
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Parse a single "dependent" resource combination.
+ */
+
+u_char
+*pnp_parse_dependant(device_t dev, u_char *resources, int len,
+ struct isa_config *config, int ldn)
+{
+
+ return pnp_scan_resources(dev, resources, len, config, ldn,
+ pnp_parse_desc);
+}
+
+static void
+pnp_merge_resources(device_t dev, struct isa_config *from,
+ struct isa_config *to)
+{
+ device_t parent;
+ int i;
+
+ parent = device_get_parent(dev);
+ for (i = 0; i < from->ic_nmem; i++) {
+ if (to->ic_nmem == ISA_NMEM) {
+ device_printf(parent, "too many memory ranges\n");
+ return;
+ }
+ to->ic_mem[to->ic_nmem] = from->ic_mem[i];
+ to->ic_nmem++;
+ }
+ for (i = 0; i < from->ic_nport; i++) {
+ if (to->ic_nport == ISA_NPORT) {
+ device_printf(parent, "too many port ranges\n");
+ return;
+ }
+ to->ic_port[to->ic_nport] = from->ic_port[i];
+ to->ic_nport++;
+ }
+ for (i = 0; i < from->ic_nirq; i++) {
+ if (to->ic_nirq == ISA_NIRQ) {
+ device_printf(parent, "too many irq ranges\n");
+ return;
+ }
+ to->ic_irqmask[to->ic_nirq] = from->ic_irqmask[i];
+ to->ic_nirq++;
+ }
+ for (i = 0; i < from->ic_ndrq; i++) {
+ if (to->ic_ndrq == ISA_NDRQ) {
+ device_printf(parent, "too many drq ranges\n");
+ return;
+ }
+ to->ic_drqmask[to->ic_ndrq] = from->ic_drqmask[i];
+ to->ic_ndrq++;
+ }
+}
+
/*
- * Parse resource data for Logical Devices.
+ * Parse resource data for Logical Devices, make a list of available
+ * resource configurations, and add them to the device.
*
* This function exits as soon as it gets an error reading *ANY*
* Resource Data or it reaches the end of Resource Data.
*/
+
void
-pnp_parse_resources(device_t dev, u_char *resources, int len, u_int32_t vendor_id, u_int32_t logical_id, int ldn)
+pnp_parse_resources(device_t dev, u_char *resources, int len, int ldn)
{
- device_t parent = device_get_parent(dev);
- u_char tag, *resp, *resinfo;
- int large_len, scanning = len;
- u_int32_t id, compat_id;
+ struct isa_config *configs;
struct isa_config *config;
- int ncfgs = 1;
+ device_t parent;
int priorities[1 + MAXDEP];
- struct isa_config *configs;
- char buf[100];
+ u_char *start;
+ u_char *p;
+ u_char tag;
+ u_int32_t id;
+ int ncfgs;
+ int l;
int i;
+ parent = device_get_parent(dev);
id = isa_get_logicalid(dev);
- configs = (struct isa_config *)malloc(sizeof(*configs) * (1 + MAXDEP),
- M_DEVBUF, M_NOWAIT | M_ZERO);
+
+ configs = (struct isa_config *)malloc(sizeof(*configs)*(1 + MAXDEP),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
if (configs == NULL) {
- device_printf(dev, "No memory to parse PNP data\n");
+ device_printf(parent, "No memory to parse PNP data\n");
return;
}
config = &configs[0];
priorities[0] = 0;
- resp = resources;
- while (scanning > 0) {
- tag = *resp++;
- scanning--;
+ ncfgs = 1;
+
+ p = resources;
+ start = NULL;
+ while (len > 0) {
+ tag = *p++;
+ len--;
if (PNP_RES_TYPE(tag) == 0) {
/* Small resource */
- if (scanning < PNP_SRES_LEN(tag)) {
- scanning = 0;
+ l = PNP_SRES_LEN(tag);
+ if (len < l) {
+ len = 0;
continue;
}
- resinfo = resp;
- resp += PNP_SRES_LEN(tag);
- scanning -= PNP_SRES_LEN(tag);;
-
- switch (PNP_SRES_NUM(tag)) {
- case PNP_TAG_COMPAT_DEVICE:
- /*
- * Got a compatible device id
- * resource. Should keep a list of
- * compat ids in the device.
- */
- bcopy(resinfo, &compat_id, 4);
- isa_set_compatid(dev, compat_id);
- break;
-
- case PNP_TAG_IRQ_FORMAT:
- if (!I16(resinfo))
- break;
- if (bootverbose) {
- printf("%s: adding irq mask %#02x\n",
- pnp_eisaformat(id),
- I16(resinfo));
- }
- if (config->ic_nirq == ISA_NIRQ) {
- device_printf(parent, "too many irqs\n");
- scanning = 0;
- break;
- }
- config->ic_irqmask[config->ic_nirq] =
- I16(resinfo);
- config->ic_nirq++;
- break;
+ len -= l;
- case PNP_TAG_DMA_FORMAT:
- if (bootverbose) {
- printf("%s: adding dma mask %#02x\n",
- pnp_eisaformat(id),
- resinfo[0]);
- }
- if (config->ic_ndrq == ISA_NDRQ) {
- device_printf(parent, "too many drqs\n");
- scanning = 0;
- break;
- }
- config->ic_drqmask[config->ic_ndrq] =
- resinfo[0];
- config->ic_ndrq++;
- break;
+ switch (PNP_SRES_NUM(tag)) {
case PNP_TAG_START_DEPENDANT:
- if (bootverbose) {
- printf("%s: start dependant\n",
- pnp_eisaformat(id));
+ if (start != NULL) {
+ /*
+ * Copy the common resources first,
+ * then parse the "dependent" resources.
+ */
+ pnp_merge_resources(dev, &configs[0],
+ config);
+ pnp_parse_dependant(dev, start,
+ p - start - 1,
+ config, ldn);
}
+ start = p + l;
if (ncfgs > MAXDEP) {
device_printf(parent, "too many dependant configs (%d)\n", MAXDEP);
- scanning = 0;
+ len = 0;
break;
}
config = &configs[ncfgs];
/*
* If the priority is not specified,
- * then use the default of
- * 'acceptable'
+ * then use the default of 'acceptable'
*/
- if (PNP_SRES_LEN(tag) > 0)
- priorities[ncfgs] = resinfo[0];
+ if (l > 0)
+ priorities[ncfgs] = p[0];
else
priorities[ncfgs] = 1;
+ if (bootverbose)
+ pnp_printf(id, "start dependent (%d)\n",
+ priorities[ncfgs]);
ncfgs++;
break;
case PNP_TAG_END_DEPENDANT:
- if (bootverbose) {
- printf("%s: end dependant\n",
- pnp_eisaformat(id));
- }
- config = &configs[0]; /* back to main config */
- break;
-
- case PNP_TAG_IO_RANGE:
- if (bootverbose) {
- printf("%s: adding io range "
- "%#x-%#x, size=%#x, "
- "align=%#x\n",
- pnp_eisaformat(id),
- I16(resinfo + 1),
- I16(resinfo + 3) + resinfo[6]-1,
- resinfo[6],
- resinfo[5]);
- }
- if (config->ic_nport == ISA_NPORT) {
- device_printf(parent, "too many ports\n");
- scanning = 0;
+ if (start == NULL) {
+ device_printf(parent,
+ "malformed resources\n");
+ len = 0;
break;
}
- config->ic_port[config->ic_nport].ir_start =
- I16(resinfo + 1);
- config->ic_port[config->ic_nport].ir_end =
- I16(resinfo + 3) + resinfo[6] - 1;
- config->ic_port[config->ic_nport].ir_size =
- resinfo[6];
- if (resinfo[5] == 0) {
- /* Make sure align is at least one */
- resinfo[5] = 1;
- }
- config->ic_port[config->ic_nport].ir_align =
- resinfo[5];
- config->ic_nport++;
- pnp_check_quirks(vendor_id,
- logical_id,
- ldn, config);
- break;
-
- case PNP_TAG_IO_FIXED:
- if (bootverbose) {
- printf("%s: adding fixed io range "
- "%#x-%#x, size=%#x, "
- "align=%#x\n",
- pnp_eisaformat(id),
- I16(resinfo),
- I16(resinfo) + resinfo[2] - 1,
- resinfo[2],
- 1);
- }
- if (config->ic_nport == ISA_NPORT) {
- device_printf(parent, "too many ports\n");
- scanning = 0;
- break;
- }
- config->ic_port[config->ic_nport].ir_start =
- I16(resinfo);
- config->ic_port[config->ic_nport].ir_end =
- I16(resinfo) + resinfo[2] - 1;
- config->ic_port[config->ic_nport].ir_size
- = resinfo[2];
- config->ic_port[config->ic_nport].ir_align = 1;
- config->ic_nport++;
+ /*
+ * Copy the common resources first,
+ * then parse the "dependent" resources.
+ */
+ pnp_merge_resources(dev, &configs[0], config);
+ pnp_parse_dependant(dev, start, p - start - 1,
+ config, ldn);
+ start = NULL;
+ if (bootverbose)
+ pnp_printf(id, "end dependent\n");
+ /*
+ * Back to the common part; clear it
+ * as its contents has already been copied
+ * to each dependant.
+ */
+ config = &configs[0];
+ bzero(config, sizeof(*config));
break;
case PNP_TAG_END:
- if (bootverbose) {
- printf("%s: end config\n",
- pnp_eisaformat(id));
+ if (start != NULL) {
+ device_printf(parent,
+ "malformed resources\n");
}
- scanning = 0;
+ len = 0;
break;
default:
- /* Skip this resource */
- device_printf(parent, "unexpected small tag %d\n",
- PNP_SRES_NUM(tag));
+ if (start != NULL)
+ /* defer parsing a dependent section */
+ break;
+ if (pnp_parse_desc(dev, tag, p, l, config, ldn))
+ len = 0;
break;
}
+ p += l;
} else {
/* Large resource */
- if (scanning < 2) {
- scanning = 0;
- continue;
- }
- large_len = I16(resp);
- resp += 2;
- scanning -= 2;
-
- if (scanning < large_len) {
- scanning = 0;
- continue;
- }
- resinfo = resp;
- resp += large_len;
- scanning -= large_len;
-
- switch (PNP_LRES_NUM(tag)) {
- case PNP_TAG_ID_ANSI:
- if (large_len > sizeof(buf) - 1)
- large_len = sizeof(buf) - 1;
- bcopy(resinfo, buf, large_len);
-
- /*
- * Trim trailing spaces and garbage.
- */
- while (large_len > 0 && buf[large_len - 1] <= ' ')
- large_len--;
- buf[large_len] = '\0';
- device_set_desc_copy(dev, buf);
- break;
-
- case PNP_TAG_MEMORY_RANGE:
- if (bootverbose) {
- int temp = I16(resinfo + 7) << 8;
-
- printf("%s: adding memory range "
- "%#x-%#x, size=%#x, "
- "align=%#x\n",
- pnp_eisaformat(id),
- I16(resinfo + 1)<<8,
- (I16(resinfo + 3)<<8) + temp - 1,
- temp,
- I16(resinfo + 5));
- }
-
- if (config->ic_nmem == ISA_NMEM) {
- device_printf(parent, "too many memory ranges\n");
- scanning = 0;
- break;
- }
-
- config->ic_mem[config->ic_nmem].ir_start =
- I16(resinfo + 1)<<8;
- config->ic_mem[config->ic_nmem].ir_end =
- (I16(resinfo + 3)<<8)
- + (I16(resinfo + 7) << 8) - 1;
- config->ic_mem[config->ic_nmem].ir_size =
- I16(resinfo + 7) << 8;
- config->ic_mem[config->ic_nmem].ir_align =
- I16(resinfo + 5);
- if (!config->ic_mem[config->ic_nmem].ir_align)
- config->ic_mem[config->ic_nmem]
- .ir_align = 0x10000;
- config->ic_nmem++;
+ if (len < 2) {
+ len = 0;
break;
-
- case PNP_TAG_MEMORY32_RANGE:
- if (I32(resinfo + 13) == 0) {
- if (bootverbose) {
- printf("%s: skipping empty range\n",
- pnp_eisaformat(id));
- }
- continue;
- }
- if (bootverbose) {
- printf("%s: adding memory32 range "
- "%#x-%#x, size=%#x, "
- "align=%#x\n",
- pnp_eisaformat(id),
- I32(resinfo + 1),
- I32(resinfo + 5)
- + I32(resinfo + 13) - 1,
- I32(resinfo + 13),
- I32(resinfo + 9));
- }
-
- if (config->ic_nmem == ISA_NMEM) {
- device_printf(parent, "too many memory ranges\n");
- scanning = 0;
- break;
- }
-
- config->ic_mem[config->ic_nmem].ir_start =
- I32(resinfo + 1);
- config->ic_mem[config->ic_nmem].ir_end =
- I32(resinfo + 5)
- + I32(resinfo + 13) - 1;
- config->ic_mem[config->ic_nmem].ir_size =
- I32(resinfo + 13);
- config->ic_mem[config->ic_nmem].ir_align =
- I32(resinfo + 9);
- config->ic_nmem++;
+ }
+ l = I16(p);
+ p += 2;
+ len -= 2;
+ if (len < l) {
+ len = 0;
break;
-
- case PNP_TAG_MEMORY32_FIXED:
- if (I32(resinfo + 5) == 0) {
- if (bootverbose) {
- printf("%s: skipping empty range\n",
- pnp_eisaformat(id));
- }
- continue;
- }
- if (bootverbose) {
- printf("%s: adding fixed memory32 range "
- "%#x-%#x, size=%#x\n",
- pnp_eisaformat(id),
- I32(resinfo + 1),
- I32(resinfo + 1)
- + I32(resinfo + 5) - 1,
- I32(resinfo + 5));
- }
-
- if (config->ic_nmem == ISA_NMEM) {
- device_printf(parent, "too many memory ranges\n");
- scanning = 0;
- break;
- }
-
- config->ic_mem[config->ic_nmem].ir_start =
- I32(resinfo + 1);
- config->ic_mem[config->ic_nmem].ir_end =
- I32(resinfo + 1)
- + I32(resinfo + 5) - 1;
- config->ic_mem[config->ic_nmem].ir_size =
- I32(resinfo + 5);
- config->ic_mem[config->ic_nmem].ir_align = 1;
- config->ic_nmem++;
+ }
+ len -= l;
+ if (start == NULL &&
+ pnp_parse_desc(dev, tag, p, l, config, ldn)) {
+ len = 0;
break;
-
- default:
- /* Skip this resource */
- device_printf(parent, "unexpected large tag %d\n",
- PNP_SRES_NUM(tag));
}
+ p += l;
}
}
- if(ncfgs == 1) {
+
+ if (ncfgs == 1) {
/* Single config without dependants */
- (void)ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]);
+ ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]);
free(configs, M_DEVBUF);
return;
}
- /* Cycle through dependant configs merging primary details */
- for(i = 1; i < ncfgs; i++) {
- int j;
- config = &configs[i];
- for(j = 0; j < configs[0].ic_nmem; j++) {
- if (config->ic_nmem == ISA_NMEM) {
- device_printf(parent, "too many memory ranges\n");
- free(configs, M_DEVBUF);
- return;
- }
- config->ic_mem[config->ic_nmem] = configs[0].ic_mem[j];
- config->ic_nmem++;
- }
- for(j = 0; j < configs[0].ic_nport; j++) {
- if (config->ic_nport == ISA_NPORT) {
- device_printf(parent, "too many port ranges\n");
- free(configs, M_DEVBUF);
- return;
- }
- config->ic_port[config->ic_nport] = configs[0].ic_port[j];
- config->ic_nport++;
- }
- for(j = 0; j < configs[0].ic_nirq; j++) {
- if (config->ic_nirq == ISA_NIRQ) {
- device_printf(parent, "too many irq ranges\n");
- free(configs, M_DEVBUF);
- return;
- }
- config->ic_irqmask[config->ic_nirq] = configs[0].ic_irqmask[j];
- config->ic_nirq++;
- }
- for(j = 0; j < configs[0].ic_ndrq; j++) {
- if (config->ic_ndrq == ISA_NDRQ) {
- device_printf(parent, "too many drq ranges\n");
- free(configs, M_DEVBUF);
- return;
- }
- config->ic_drqmask[config->ic_ndrq] = configs[0].ic_drqmask[j];
- config->ic_ndrq++;
- }
- (void)ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]);
+
+ for (i = 1; i < ncfgs; i++) {
+ /*
+ * Merge the remaining part of the common resources,
+ * if any. Strictly speaking, there shouldn't be common/main
+ * resources after the END_DEPENDENT tag.
+ */
+ pnp_merge_resources(dev, &configs[0], &configs[i]);
+ ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]);
}
+
free(configs, M_DEVBUF);
}
+
+u_char
+*pnp_scan_resources(device_t dev, u_char *resources, int len,
+ struct isa_config *config, int ldn, pnp_scan_cb *cb)
+{
+ u_char *p;
+ u_char tag;
+ int l;
+
+ p = resources;
+ while (len > 0) {
+ tag = *p++;
+ len--;
+ if (PNP_RES_TYPE(tag) == 0) {
+ /* small resource */
+ l = PNP_SRES_LEN(tag);
+ if (len < l)
+ break;
+ if ((*cb)(dev, tag, p, l, config, ldn))
+ return (p + l);
+ if (PNP_SRES_NUM(tag) == PNP_TAG_END)
+ return (p + l);
+ } else {
+ /* large resource */
+ if (len < 2)
+ break;
+ l = I16(p);
+ p += 2;
+ len -= 2;
+ if (len < l)
+ break;
+ if ((*cb)(dev, tag, p, l, config, ldn))
+ return (p + l);
+ }
+ p += l;
+ len -= l;
+ }
+ return NULL;
+}
diff --git a/sys/isa/pnpvar.h b/sys/isa/pnpvar.h
index 050adb9..012d3de 100644
--- a/sys/isa/pnpvar.h
+++ b/sys/isa/pnpvar.h
@@ -52,8 +52,16 @@ u_char pnp_read(int d); /* currently unused, but who knows... */
| (PNP_HEXTONUM(s[6]) << 24) \
| (PNP_HEXTONUM(s[5]) << 28))
+typedef int pnp_scan_cb(device_t dev, u_char tag, u_char *res, int len,
+ struct isa_config *config, int ldn);
+
char *pnp_eisaformat(u_int32_t id);
-void pnp_parse_resources(device_t dev, u_char *resources, int len, u_int32_t vendor_id, u_int32_t logical_id, int ldn);
+void pnp_printf(u_int32_t id, char *fmt, ...);
+void pnp_parse_resources(device_t dev, u_char *resources, int len, int ldn);
+u_char *pnp_parse_dependant(device_t dev, u_char *resources, int len,
+ struct isa_config *config, int ldn);
+u_char *pnp_scan_resources(device_t dev, u_char *resources, int len,
+ struct isa_config *config, int ldn, pnp_scan_cb *cb);
void pnp_check_quirks(u_int32_t vendor_id, u_int32_t logical_id, int ldn, struct isa_config *config);
OpenPOWER on IntegriCloud