diff --git a/sys/amd64/amd64/bios.c b/sys/amd64/amd64/bios.c index cd904889171e..6755253b307e 100644 --- a/sys/amd64/amd64/bios.c +++ b/sys/amd64/amd64/bios.c @@ -478,9 +478,10 @@ struct pnp_sysdev #define PNPATTR_BOOTABLE (1<<4) /* can be booted from */ #define PNPATTR_DOCK (1<<5) /* is a docking station */ #define PNPATTR_REMOVEABLE (1<<6) /* device is removeable */ -#define PNPATTR_CONFIG_STATIC 0x00 -#define PNPATTR_CONFIG_DYNAMIC 0x07 -#define PNPATTR_CONFIG_DYNONLY 0x17 +#define PNPATTR_CONFIG_STATIC (0) +#define PNPATTR_CONFIG_DYNAMIC (1) +#define PNPATTR_CONFIG_DYNONLY (3) +#define PNPATTR_CONFIG(a) (((a) >> 7) & 0x3) /* device-specific data comes here */ u_int8_t devdata[0]; } __attribute__ ((packed)); @@ -581,10 +582,25 @@ pnpbios_identify(driver_t *driver, device_t parent) dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1); isa_set_vendorid(dev, pd->devid); isa_set_logicalid(dev, pd->devid); + /* + * It appears that some PnP BIOS doesn't allow us to re-enable + * the embedded system device once it is disabled. We shall + * mark all system device nodes as "cannot be disabled", regardless + * of actual settings in the device attribute byte. + * XXX + isa_set_configattr(dev, + ((pd->attrib & PNPATTR_NODISABLE) ? 0 : ISACFGATTR_CANDISABLE) | + ((!(pd->attrib & PNPATTR_NOCONFIG) && + PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC) + ? ISACFGATTR_DYNAMIC : 0)); + */ + isa_set_configattr(dev, + (!(pd->attrib & PNPATTR_NOCONFIG) && + PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC) + ? ISACFGATTR_DYNAMIC : 0); ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0); pnp_parse_resources(dev, &pd->devdata[0], - pd->size - sizeof(struct pnp_sysdev), - isa_get_vendorid(dev), isa_get_logicalid(dev), 0); + pd->size - sizeof(struct pnp_sysdev), 0); if (!device_get_desc(dev)) device_set_desc_copy(dev, pnp_eisaformat(pd->devid)); diff --git a/sys/i386/i386/bios.c b/sys/i386/i386/bios.c index cd904889171e..6755253b307e 100644 --- a/sys/i386/i386/bios.c +++ b/sys/i386/i386/bios.c @@ -478,9 +478,10 @@ struct pnp_sysdev #define PNPATTR_BOOTABLE (1<<4) /* can be booted from */ #define PNPATTR_DOCK (1<<5) /* is a docking station */ #define PNPATTR_REMOVEABLE (1<<6) /* device is removeable */ -#define PNPATTR_CONFIG_STATIC 0x00 -#define PNPATTR_CONFIG_DYNAMIC 0x07 -#define PNPATTR_CONFIG_DYNONLY 0x17 +#define PNPATTR_CONFIG_STATIC (0) +#define PNPATTR_CONFIG_DYNAMIC (1) +#define PNPATTR_CONFIG_DYNONLY (3) +#define PNPATTR_CONFIG(a) (((a) >> 7) & 0x3) /* device-specific data comes here */ u_int8_t devdata[0]; } __attribute__ ((packed)); @@ -581,10 +582,25 @@ pnpbios_identify(driver_t *driver, device_t parent) dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1); isa_set_vendorid(dev, pd->devid); isa_set_logicalid(dev, pd->devid); + /* + * It appears that some PnP BIOS doesn't allow us to re-enable + * the embedded system device once it is disabled. We shall + * mark all system device nodes as "cannot be disabled", regardless + * of actual settings in the device attribute byte. + * XXX + isa_set_configattr(dev, + ((pd->attrib & PNPATTR_NODISABLE) ? 0 : ISACFGATTR_CANDISABLE) | + ((!(pd->attrib & PNPATTR_NOCONFIG) && + PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC) + ? ISACFGATTR_DYNAMIC : 0)); + */ + isa_set_configattr(dev, + (!(pd->attrib & PNPATTR_NOCONFIG) && + PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC) + ? ISACFGATTR_DYNAMIC : 0); ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0); pnp_parse_resources(dev, &pd->devdata[0], - pd->size - sizeof(struct pnp_sysdev), - isa_get_vendorid(dev), isa_get_logicalid(dev), 0); + pd->size - sizeof(struct pnp_sysdev), 0); if (!device_get_desc(dev)) device_set_desc_copy(dev, pnp_eisaformat(pd->devid)); diff --git a/sys/isa/isa_common.c b/sys/isa/isa_common.c index 22677e3c2416..d5aee38b91fc 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)) { @@ -429,6 +465,55 @@ isa_assign_resources(device_t child) return 0; } +/* + * 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. */ @@ -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 b940d1463f94..a2f39f8363a0 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 d81685ca8531..fa27c3cd7fc5 100644 --- a/sys/isa/isavar.h +++ b/sys/isa/isavar.h @@ -127,6 +127,13 @@ enum isa_device_ivars { ISA_IVAR_CONFIGATTR }; +/* + * 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 */ diff --git a/sys/isa/pnp.c b/sys/isa/pnp.c index 6a58ddf6dd99..de376202250a 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 976b0eb8c0c4..9efc2fef18bd 100644 --- a/sys/isa/pnpparse.c +++ b/sys/isa/pnpparse.c @@ -31,6 +31,9 @@ #include #include #include + +#include + #include #include #include @@ -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 resource data for Logical Devices. + * 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, 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 *config; - int ncfgs = 1; - int priorities[1 + MAXDEP]; struct isa_config *configs; - char buf[100]; + struct isa_config *config; + device_t parent; + int priorities[1 + MAXDEP]; + 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; + if (len < 2) { + len = 0; + break; } - large_len = I16(resp); - resp += 2; - scanning -= 2; - - if (scanning < large_len) { - scanning = 0; - continue; + l = I16(p); + p += 2; + len -= 2; + if (len < l) { + len = 0; + break; } - 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); + len -= l; + if (start == NULL && + pnp_parse_desc(dev, tag, p, l, config, ldn)) { + len = 0; 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++; - 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++; - 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++; - 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 050adb94db9d..012d3de942cc 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);