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.
This commit is contained in:
Kazutaka YOKOTA 2001-09-05 03:54:33 +00:00
parent 8d44fade0e
commit c395939120
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=83051
8 changed files with 702 additions and 361 deletions

View File

@ -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));

View File

@ -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));

View File

@ -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;
}

View File

@ -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))

View File

@ -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
*/

View File

@ -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;

View File

@ -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 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;
}

View File

@ -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);