2003-02-03 01:30:49

by Adam Belay

[permalink] [raw]
Subject: [PATCH][RFC] Resource Management Improvements (1/4)

This patch is the first of 4 experimental pnp patches. It is against a clean
2.5.59 kernel. I would appreciate for those who are interested, if you would
test this patch series and post your results on lkml. These changes appear
to be very stable on my test systems and I feel they will be a dramatic
improvement to the pnp layer.

Patch 1 contains resource management improvements that will allow the pnp
layer to resolve virtually any resource conflict. A new kernel parameter
"pnp_max_moves=" was added to allow the user to describe the maximum number
of device levels to move during conflict resolution. This powerful new
engine also includes many cleanups and more verbose reporting. All devices
are configured when added instead of when activated. This will increase
the odds that the conflict will be possible to resolve because resources
cannot be moved in an active device.


diff -urN a/drivers/pnp/base.h b/drivers/pnp/base.h
--- a/drivers/pnp/base.h Fri Jan 31 16:59:56 2003
+++ b/drivers/pnp/base.h Sat Feb 1 17:06:06 2003
@@ -4,6 +4,17 @@
extern int pnp_interface_attach_device(struct pnp_dev *dev);
extern void pnp_name_device(struct pnp_dev *dev);
extern void pnp_fixup_device(struct pnp_dev *dev);
+extern int pnp_configure_device(struct pnp_dev *dev);
extern void pnp_free_resources(struct pnp_resources *resources);
extern int __pnp_add_device(struct pnp_dev *dev);
extern void __pnp_remove_device(struct pnp_dev *dev);
+
+/* resource conflict types */
+#define CONFLICT_TYPE_NONE 0x0000 /* there are no conflicts, other than those in the link */
+#define CONFLICT_TYPE_RESERVED 0x0001 /* the resource requested was reserved */
+#define CONFLICT_TYPE_IN_USE 0x0002 /* there is a conflict because the resource is in use */
+#define CONFLICT_TYPE_PCI 0x0004 /* there is a conflict with a pci device */
+#define CONFLICT_TYPE_INVALID 0x0008 /* the resource requested is invalid */
+#define CONFLICT_TYPE_INTERNAL 0x0010 /* resources within the device conflict with each ohter */
+#define CONFLICT_TYPE_PNP_WARM 0x0020 /* there is a conflict with a pnp device that is active */
+#define CONFLICT_TYPE_PNP_COLD 0x0040 /* there is a conflict with a pnp device that is disabled */
diff -urN a/drivers/pnp/card.c b/drivers/pnp/card.c
--- a/drivers/pnp/card.c Fri Jan 31 16:59:56 2003
+++ b/drivers/pnp/card.c Sat Feb 1 16:52:08 2003
@@ -22,9 +22,9 @@

LIST_HEAD(pnp_cards);

-static const struct pnp_card_device_id * match_card(struct pnpc_driver *drv, struct pnp_card *card)
+static const struct pnp_card_id * match_card(struct pnpc_driver *drv, struct pnp_card *card)
{
- const struct pnp_card_device_id *drv_id = drv->id_table;
+ const struct pnp_card_id *drv_id = drv->id_table;
while (*drv_id->id){
if (compare_pnp_id(card->id,drv_id->id))
return drv_id;
@@ -106,7 +106,6 @@
return -EINVAL;
sprintf(card->dev.bus_id, "%02x:%02x", card->protocol->number, card->number);
INIT_LIST_HEAD(&card->rdevs);
- strcpy(card->dev.name,card->name);
card->dev.parent = &card->protocol->dev;
card->dev.bus = &pnpc_bus_type;
card->dev.release = &pnp_release_card;
@@ -221,7 +220,7 @@
cdrv = to_pnpc_driver(card->dev.driver);
if (dev->active == 0) {
if (!(cdrv->flags & PNPC_DRIVER_DO_NOT_ACTIVATE)) {
- if(pnp_activate_dev(dev,NULL)<0) {
+ if(pnp_activate_dev(dev)<0) {
pnp_device_detach(dev);
return NULL;
}
@@ -286,7 +285,7 @@
int error = 0;
struct pnpc_driver *drv = to_pnpc_driver(dev->driver);
struct pnp_card *card = to_pnp_card(dev);
- const struct pnp_card_device_id *card_id = NULL;
+ const struct pnp_card_id *card_id = NULL;

pnp_dbg("pnp: match found with the PnP card '%s' and the driver '%s'", dev->bus_id,drv->name);

diff -urN a/drivers/pnp/core.c b/drivers/pnp/core.c
--- a/drivers/pnp/core.c Fri Jan 31 16:59:56 2003
+++ b/drivers/pnp/core.c Sat Feb 1 16:52:08 2003
@@ -104,31 +104,29 @@
static void pnp_release_device(struct device *dmdev)
{
struct pnp_dev * dev = to_pnp_dev(dmdev);
- if (dev->res)
- pnp_free_resources(dev->res);
+ if (dev->possible)
+ pnp_free_resources(dev->possible);
pnp_free_ids(dev);
kfree(dev);
}

int __pnp_add_device(struct pnp_dev *dev)
{
- int error = 0;
+ int ret;
pnp_name_device(dev);
pnp_fixup_device(dev);
- strncpy(dev->dev.name,dev->name,DEVICE_NAME_SIZE-1);
- dev->dev.name[DEVICE_NAME_SIZE-1] = '\0';
dev->dev.bus = &pnp_bus_type;
dev->dev.release = &pnp_release_device;
dev->status = PNP_READY;
- error = device_register(&dev->dev);
- if (error == 0){
- spin_lock(&pnp_lock);
- list_add_tail(&dev->global_list, &pnp_global);
- list_add_tail(&dev->protocol_list, &dev->protocol->devices);
- spin_unlock(&pnp_lock);
+ spin_lock(&pnp_lock);
+ list_add_tail(&dev->global_list, &pnp_global);
+ list_add_tail(&dev->protocol_list, &dev->protocol->devices);
+ spin_unlock(&pnp_lock);
+ pnp_configure_device(dev);
+ ret = device_register(&dev->dev);
+ if (ret == 0)
pnp_interface_attach_device(dev);
- }
- return error;
+ return ret;
}

/*
@@ -172,7 +170,7 @@

static int __init pnp_init(void)
{
- printk(KERN_INFO "Linux Plug and Play Support v0.94 (c) Adam Belay\n");
+ printk(KERN_INFO "Linux Plug and Play Support v0.95 (c) Adam Belay\n");
return bus_register(&pnp_bus_type);
}

diff -urN a/drivers/pnp/driver.c b/drivers/pnp/driver.c
--- a/drivers/pnp/driver.c Fri Jan 31 16:59:56 2003
+++ b/drivers/pnp/driver.c Sat Feb 1 16:52:08 2003
@@ -103,7 +103,7 @@

if (pnp_dev->active == 0) {
if (!(pnp_drv->flags & PNP_DRIVER_DO_NOT_ACTIVATE)) {
- error = pnp_activate_dev(pnp_dev, NULL);
+ error = pnp_activate_dev(pnp_dev);
if (error < 0)
return error;
}
diff -urN a/drivers/pnp/interface.c b/drivers/pnp/interface.c
--- a/drivers/pnp/interface.c Fri Jan 31 16:59:56 2003
+++ b/drivers/pnp/interface.c Sat Feb 1 16:52:08 2003
@@ -218,7 +218,7 @@
static ssize_t pnp_show_possible_resources(struct device *dmdev, char *buf)
{
struct pnp_dev *dev = to_pnp_dev(dmdev);
- struct pnp_resources * res = dev->res;
+ struct pnp_resources * res = dev->possible;
int dep = 0;
pnp_info_buffer_t *buffer;

@@ -251,7 +251,7 @@
str += sprintf(str,"DISABLED\n");
goto done;
}
- for (i = 0; i < DEVICE_COUNT_IO; i++) {
+ for (i = 0; i < PNP_MAX_PORT; i++) {
if (pnp_port_valid(dev, i)) {
str += sprintf(str,"io");
str += sprintf(str," 0x%lx-0x%lx \n",
@@ -259,7 +259,7 @@
pnp_port_end(dev, i));
}
}
- for (i = 0; i < DEVICE_COUNT_MEM; i++) {
+ for (i = 0; i < PNP_MAX_MEM; i++) {
if (pnp_mem_valid(dev, i)) {
str += sprintf(str,"mem");
str += sprintf(str," 0x%lx-0x%lx \n",
@@ -267,13 +267,13 @@
pnp_mem_end(dev, i));
}
}
- for (i = 0; i < DEVICE_COUNT_IRQ; i++) {
+ for (i = 0; i < PNP_MAX_IRQ; i++) {
if (pnp_irq_valid(dev, i)) {
str += sprintf(str,"irq");
str += sprintf(str," %ld \n", pnp_irq(dev, i));
}
}
- for (i = 0; i < DEVICE_COUNT_DMA; i++) {
+ for (i = 0; i < PNP_MAX_DMA; i++) {
if (pnp_dma_valid(dev, i)) {
str += sprintf(str,"dma");
str += sprintf(str," %ld \n", pnp_dma(dev, i));
@@ -316,13 +316,7 @@
goto done;
}
if (!strnicmp(command,"auto",4)) {
- error = pnp_activate_dev(dev,NULL);
- goto done;
- }
- if (!strnicmp(command,"manual",6)) {
- if (num_args != 2)
- goto done;
- error = pnp_raw_set_dev(dev,depnum,NULL);
+ error = pnp_activate_dev(dev);
goto done;
}
done:
diff -urN a/drivers/pnp/isapnp/core.c b/drivers/pnp/isapnp/core.c
--- a/drivers/pnp/isapnp/core.c Fri Jan 31 16:59:56 2003
+++ b/drivers/pnp/isapnp/core.c Sat Feb 1 18:51:08 2003
@@ -101,7 +101,6 @@

/* some prototypes */

-static int isapnp_config_prepare(struct pnp_dev *dev);
extern struct pnp_protocol isapnp_protocol;

static inline void write_data(unsigned char x)
@@ -650,7 +649,6 @@
switch (type) {
case _STAG_LOGDEVID:
if (size >= 5 && size <= 6) {
- isapnp_config_prepare(dev);
if ((dev = isapnp_parse_device(card, size, number++)) == NULL)
return 1;
pnp_build_resource(dev,0);
@@ -723,7 +721,7 @@
size = 0;
break;
case _LTAG_ANSISTR:
- isapnp_parse_name(dev->name, sizeof(dev->name), &size);
+ isapnp_parse_name(dev->dev.name, sizeof(dev->dev.name), &size);
break;
case _LTAG_UNICODESTR:
/* silently ignore */
@@ -746,7 +744,6 @@
case _STAG_END:
if (size > 0)
isapnp_skip_bytes(size);
- isapnp_config_prepare(dev);
return 1;
default:
printk(KERN_ERR "isapnp: unexpected or unknown tag type 0x%x for logical device %i (device %i), ignored\n", type, dev->number, card->number);
@@ -755,7 +752,6 @@
if (size > 0)
isapnp_skip_bytes(size);
}
- isapnp_config_prepare(dev);
return 0;
}

@@ -790,7 +786,7 @@
case _STAG_VENDOR:
break;
case _LTAG_ANSISTR:
- isapnp_parse_name(card->name, sizeof(card->name), &size);
+ isapnp_parse_name(card->dev.name, sizeof(card->dev.name), &size);
break;
case _LTAG_UNICODESTR:
/* silently ignore */
@@ -948,39 +944,6 @@
return 0;
}

-static int isapnp_config_prepare(struct pnp_dev *dev)
-{
- int idx;
- if (dev == NULL)
- return -EINVAL;
- if (dev->active || dev->lock_resources)
- return -EBUSY;
- for (idx = 0; idx < DEVICE_COUNT_IRQ; idx++) {
- dev->irq_resource[idx].name = NULL;
- dev->irq_resource[idx].start = -1;
- dev->irq_resource[idx].end = -1;
- dev->irq_resource[idx].flags = IORESOURCE_IRQ|IORESOURCE_UNSET;
- }
- for (idx = 0; idx < DEVICE_COUNT_DMA; idx++) {
- dev->dma_resource[idx].name = NULL;
- dev->dma_resource[idx].start = -1;
- dev->dma_resource[idx].end = -1;
- dev->dma_resource[idx].flags = IORESOURCE_DMA|IORESOURCE_UNSET;
- }
- for (idx = 0; idx < DEVICE_COUNT_IO; idx++) {
- dev->io_resource[idx].name = NULL;
- dev->io_resource[idx].start = 0;
- dev->io_resource[idx].end = 0;
- dev->io_resource[idx].flags = IORESOURCE_IO|IORESOURCE_UNSET;
- }
- for (idx = 0; idx < DEVICE_COUNT_MEM; idx++) {
- dev->mem_resource[idx].name = NULL;
- dev->mem_resource[idx].start = 0;
- dev->mem_resource[idx].end = 0;
- dev->mem_resource[idx].flags = IORESOURCE_MEM|IORESOURCE_UNSET;
- }
- return 0;
-}

/*
* Inititialization.
@@ -1012,19 +975,12 @@
return 0;
}

-static int isapnp_set_resources(struct pnp_dev *dev, struct pnp_cfg *cfg)
+static int isapnp_set_resources(struct pnp_dev *dev)
{
int tmp;
- isapnp_cfg_begin(dev->card->number, dev->number);
+
+ isapnp_cfg_begin(dev->card->number, dev->number);
dev->active = 1;
- dev->irq_resource[0] = cfg->request.irq_resource[0];
- dev->irq_resource[1] = cfg->request.irq_resource[1];
- dev->dma_resource[0] = cfg->request.dma_resource[0];
- dev->dma_resource[1] = cfg->request.dma_resource[1];
- for (tmp = 0; tmp < DEVICE_COUNT_IO; tmp++)
- dev->io_resource[tmp] = cfg->request.io_resource[tmp];
- for (tmp = 0; tmp < DEVICE_COUNT_MEM; tmp++)
- dev->mem_resource[tmp] = cfg->request.mem_resource[tmp];
for (tmp = 0; tmp < 8 && pnp_port_valid(dev, tmp); tmp++)
isapnp_write_word(ISAPNP_CFG_PORT+(tmp<<1), pnp_port_start(dev, tmp));
for (tmp = 0; tmp < 2 && pnp_irq_valid(dev, tmp); tmp++) {
@@ -1127,11 +1083,11 @@
protocol_for_each_card(&isapnp_protocol,card) {
cards++;
if (isapnp_verbose) {
- printk(KERN_INFO "isapnp: Card '%s'\n", card->name[0]?card->name:"Unknown");
+ printk(KERN_INFO "isapnp: Card '%s'\n", card->dev.name[0]?card->dev.name:"Unknown");
if (isapnp_verbose < 2)
continue;
- pnp_card_for_each_dev(card,dev) {
- printk(KERN_INFO "isapnp: Device '%s'\n", dev->name[0]?dev->name:"Unknown");
+ card_for_each_dev(card,dev) {
+ printk(KERN_INFO "isapnp: Device '%s'\n", dev->dev.name[0]?dev->dev.name:"Unknown");
}
}
}
diff -urN a/drivers/pnp/names.c b/drivers/pnp/names.c
--- a/drivers/pnp/names.c Fri Jan 31 16:59:56 2003
+++ b/drivers/pnp/names.c Sat Feb 1 16:52:08 2003
@@ -30,7 +30,7 @@
pnp_name_device(struct pnp_dev *dev)
{
int i;
- char *name = dev->name;
+ char *name = dev->dev.name;
for(i=0; i<sizeof(pnp_id_eisaid)/sizeof(pnp_id_eisaid[0]); i++){
if (compare_pnp_id(dev->id,pnp_id_eisaid[i])){
sprintf(name, "%s", pnp_id_names[i]);
diff -urN a/drivers/pnp/pnpbios/core.c b/drivers/pnp/pnpbios/core.c
--- a/drivers/pnp/pnpbios/core.c Fri Jan 31 16:59:57 2003
+++ b/drivers/pnp/pnpbios/core.c Sat Feb 1 16:52:08 2003
@@ -671,11 +671,11 @@
static void add_irqresource(struct pnp_dev *dev, int irq)
{
int i = 0;
- while (pnp_irq_valid(dev, i) && i < DEVICE_COUNT_IRQ) i++;
- if (i < DEVICE_COUNT_IRQ) {
- dev->irq_resource[i].start =
- dev->irq_resource[i].end = (unsigned long) irq;
- dev->irq_resource[i].flags = IORESOURCE_IRQ; // Also clears _UNSET flag
+ while (pnp_irq_valid(dev, i) && i < PNP_MAX_IRQ) i++;
+ if (i < PNP_MAX_IRQ) {
+ dev->resources.irq_resource[i].start =
+ dev->resources.irq_resource[i].end = (unsigned long) irq;
+ dev->resources.irq_resource[i].flags = IORESOURCE_IRQ; // Also clears _UNSET flag
}
}

@@ -683,32 +683,32 @@
{
int i = 0;
while (pnp_dma_valid(dev, i) && i < DEVICE_COUNT_DMA) i++;
- if (i < DEVICE_COUNT_DMA) {
- dev->dma_resource[i].start =
- dev->dma_resource[i].end = (unsigned long) dma;
- dev->dma_resource[i].flags = IORESOURCE_DMA; // Also clears _UNSET flag
+ if (i < PNP_MAX_DMA) {
+ dev->resources.dma_resource[i].start =
+ dev->resources.dma_resource[i].end = (unsigned long) dma;
+ dev->resources.dma_resource[i].flags = IORESOURCE_DMA; // Also clears _UNSET flag
}
}

static void add_ioresource(struct pnp_dev *dev, int io, int len)
{
int i = 0;
- while (pnp_port_valid(dev, i) && i < DEVICE_COUNT_IO) i++;
- if (i < DEVICE_COUNT_RESOURCE) {
- dev->io_resource[i].start = (unsigned long) io;
- dev->io_resource[i].end = (unsigned long)(io + len - 1);
- dev->io_resource[i].flags = IORESOURCE_IO; // Also clears _UNSET flag
+ while (pnp_port_valid(dev, i) && i < PNP_MAX_PORT) i++;
+ if (i < PNP_MAX_PORT) {
+ dev->resources.port_resource[i].start = (unsigned long) io;
+ dev->resources.port_resource[i].end = (unsigned long)(io + len - 1);
+ dev->resources.port_resource[i].flags = IORESOURCE_IO; // Also clears _UNSET flag
}
}

static void add_memresource(struct pnp_dev *dev, int mem, int len)
{
int i = 0;
- while (pnp_mem_valid(dev, i) && i < DEVICE_COUNT_MEM) i++;
- if (i < DEVICE_COUNT_RESOURCE) {
- dev->mem_resource[i].start = (unsigned long) mem;
- dev->mem_resource[i].end = (unsigned long)(mem + len - 1);
- dev->mem_resource[i].flags = IORESOURCE_MEM; // Also clears _UNSET flag
+ while (pnp_mem_valid(dev, i) && i < PNP_MAX_MEM) i++;
+ if (i < PNP_MAX_MEM) {
+ dev->resources.mem_resource[i].start = (unsigned long) mem;
+ dev->resources.mem_resource[i].end = (unsigned long)(mem + len - 1);
+ dev->resources.mem_resource[i].flags = IORESOURCE_MEM; // Also clears _UNSET flag
}
}

@@ -720,25 +720,25 @@
/*
* First, set resource info to default values
*/
- for (i=0;i<DEVICE_COUNT_IO;i++) {
- dev->io_resource[i].start = 0;
- dev->io_resource[i].end = 0;
- dev->io_resource[i].flags = IORESOURCE_IO|IORESOURCE_UNSET;
+ for (i=0;i<PNP_MAX_PORT;i++) {
+ dev->resources.port_resource[i].start = 0;
+ dev->resources.port_resource[i].end = 0;
+ dev->resources.port_resource[i].flags = IORESOURCE_UNSET;
}
- for (i=0;i<DEVICE_COUNT_MEM;i++) {
- dev->mem_resource[i].start = 0;
- dev->mem_resource[i].end = 0;
- dev->mem_resource[i].flags = IORESOURCE_MEM|IORESOURCE_UNSET;
+ for (i=0;i<PNP_MAX_MEM;i++) {
+ dev->resources.mem_resource[i].start = 0;
+ dev->resources.mem_resource[i].end = 0;
+ dev->resources.mem_resource[i].flags = IORESOURCE_UNSET;
}
- for (i=0;i<DEVICE_COUNT_IRQ;i++) {
- dev->irq_resource[i].start = (unsigned long)-1;
- dev->irq_resource[i].end = (unsigned long)-1;
- dev->irq_resource[i].flags = IORESOURCE_IRQ|IORESOURCE_UNSET;
+ for (i=0;i<PNP_MAX_IRQ;i++) {
+ dev->resources.irq_resource[i].start = (unsigned long)-1;
+ dev->resources.irq_resource[i].end = (unsigned long)-1;
+ dev->resources.irq_resource[i].flags = IORESOURCE_UNSET;
}
- for (i=0;i<DEVICE_COUNT_DMA;i++) {
- dev->dma_resource[i].start = (unsigned long)-1;
- dev->dma_resource[i].end = (unsigned long)-1;
- dev->dma_resource[i].flags = IORESOURCE_DMA|IORESOURCE_UNSET;
+ for (i=0;i<PNP_MAX_DMA;i++) {
+ dev->resources.dma_resource[i].start = (unsigned long)-1;
+ dev->resources.dma_resource[i].end = (unsigned long)-1;
+ dev->resources.dma_resource[i].flags = IORESOURCE_UNSET;
}

/*
@@ -759,7 +759,7 @@
case 0x02: // device name
{
int len = *(short *) &p[1];
- memcpy(dev->name, p + 3, len >= 80 ? 79 : len);
+ memcpy(dev->dev.name, p + 3, len >= 80 ? 79 : len);
break;
}
case 0x05: // 32-bit memory
@@ -825,10 +825,10 @@

} /* while */
end:
- if (pnp_port_valid(dev, 0) == 0 &&
- pnp_mem_valid(dev, 0) == 0 &&
- pnp_irq_valid(dev, 0) == 0 &&
- pnp_dma_valid(dev, 0) == 0)
+ if (pnp_port_start(dev, 0) == 0 &&
+ pnp_mem_start(dev, 0) == 0 &&
+ pnp_irq(dev, 0) == -1 &&
+ pnp_dma(dev, 0) == -1)
dev->active = 0;
else
dev->active = 1;
@@ -1169,7 +1169,7 @@
return;
}

-static int node_set_resources(struct pnp_bios_node *node, struct pnp_cfg *config)
+static int node_set_resources(struct pnp_bios_node *node, struct pnp_rule_table *config)
{
int error = 0;
unsigned char *p = (char *)node->data, *lastp = NULL;
@@ -1285,7 +1285,7 @@
return 0;
}

-static int pnpbios_set_resources(struct pnp_dev *dev, struct pnp_cfg *config)
+static int pnpbios_set_resources(struct pnp_dev *dev)
{
struct pnp_dev_node_info node_info;
u8 nodenum = dev->number;
@@ -1301,7 +1301,7 @@
return -1;
if (pnp_bios_get_dev_node(&nodenum, (char )1, node))
return -ENODEV;
- if(node_set_resources(node, config)<0){
+ if(node_set_resources(node, &dev->config->rule)<0){
return -1;
}
kfree(node);
@@ -1310,7 +1310,7 @@

static int pnpbios_disable_resources(struct pnp_dev *dev)
{
- struct pnp_cfg * config = kmalloc(sizeof(struct pnp_cfg), GFP_KERNEL);
+ struct pnp_rule_table * config = kmalloc(sizeof(struct pnp_rule_table), GFP_KERNEL);
/* first we need to set everything to a disabled value */
struct pnp_port port = {
.max = 0,
@@ -1346,7 +1346,7 @@
/* just in case */
if(dev->flags & PNPBIOS_NO_DISABLE || !pnpbios_is_dynamic(dev))
return -EPERM;
- memset(config, 0, sizeof(struct pnp_cfg));
+ memset(config, 0, sizeof(struct pnp_rule_table));
if (!dev || !dev->active)
return -EINVAL;
for (i=0; i < 8; i++)
@@ -1445,7 +1445,6 @@
}
memset(dev_id,0,sizeof(struct pnp_id));
dev->number = thisnodenum;
- strcpy(dev->name,"Unknown Device");
pnpid32_to_pnpid(node->eisa_id,id);
memcpy(dev_id->id,id,7);
pnp_add_id(dev_id, dev);
diff -urN a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c
--- a/drivers/pnp/quirks.c Fri Jan 31 16:59:57 2003
+++ b/drivers/pnp/quirks.c Sat Feb 1 16:52:08 2003
@@ -29,7 +29,7 @@
static void quirk_awe32_resources(struct pnp_dev *dev)
{
struct pnp_port *port, *port2, *port3;
- struct pnp_resources *res = dev->res->dep;
+ struct pnp_resources *res = dev->possible->dep;

/*
* Unfortunately the isapnp_add_port_resource is too tightly bound
@@ -57,7 +57,7 @@

static void quirk_cmi8330_resources(struct pnp_dev *dev)
{
- struct pnp_resources *res = dev->res->dep;
+ struct pnp_resources *res = dev->possible->dep;

for ( ; res ; res = res->dep ) {

@@ -77,7 +77,7 @@
static void quirk_sb16audio_resources(struct pnp_dev *dev)
{
struct pnp_port *port;
- struct pnp_resources *res = dev->res->dep;
+ struct pnp_resources *res = dev->possible->dep;
int changed = 0;

/*
@@ -115,7 +115,7 @@
*/
struct pnp_resources *res;
int max;
- res = dev->res;
+ res = dev->possible;
max = 0;
for (res = res->dep; res; res = res->dep) {
if (res->dma->map > max)
diff -urN a/drivers/pnp/resource.c b/drivers/pnp/resource.c
--- a/drivers/pnp/resource.c Fri Jan 31 16:59:57 2003
+++ b/drivers/pnp/resource.c Sat Feb 1 19:10:09 2003
@@ -18,6 +18,7 @@
#include <linux/ioport.h>
#include <linux/config.h>
#include <linux/init.h>
+#include <linux/slab.h>

#ifdef CONFIG_PNP_DEBUG
#define DEBUG
@@ -30,13 +31,16 @@

int pnp_allow_dma0 = -1; /* allow dma 0 during auto activation: -1=off (:default), 0=off (set by user), 1=on */
int pnp_skip_pci_scan; /* skip PCI resource scanning */
+int pnp_max_moves = 4;
int pnp_reserve_irq[16] = { [0 ... 15] = -1 }; /* reserve (don't use) some IRQ */
int pnp_reserve_dma[8] = { [0 ... 7] = -1 }; /* reserve (don't use) some DMA */
int pnp_reserve_io[16] = { [0 ... 15] = -1 }; /* reserve (don't use) some I/O region */
int pnp_reserve_mem[16] = { [0 ... 15] = -1 }; /* reserve (don't use) some memory region */


-/* resource information adding functions */
+/*
+ * possible resource registration
+ */

struct pnp_resources * pnp_build_resource(struct pnp_dev *dev, int dependent)
{
@@ -45,7 +49,7 @@
res = pnp_alloc(sizeof(struct pnp_resources));
if (!res)
return NULL;
- ptr = dev->res;
+ ptr = dev->possible;
if (ptr && ptr->dependent && dependent) { /* add to another list */
ptra = ptr->dep;
while (ptra && ptra->dep)
@@ -56,7 +60,7 @@
ptra->dep = res;
} else {
if (!ptr){
- dev->res = res;
+ dev->possible = res;
}
else{
kfree(res);
@@ -81,7 +85,7 @@
struct pnp_resources *res;
if (!dev)
return NULL;
- res = dev->res;
+ res = dev->possible;
if (!res)
return NULL;
for (i = 0; i < depnum; i++)
@@ -100,7 +104,7 @@
struct pnp_resources *res;
if (!dev)
return -EINVAL;
- res = dev->res;
+ res = dev->possible;
if (!res)
return -EINVAL;
while (res->dep){
@@ -171,11 +175,10 @@
struct pnp_resources *res;
struct pnp_port *ptr;
res = pnp_find_resources(dev,depnum);
- if (res==NULL)
+ if (!res)
return -EINVAL;
if (!data)
return -EINVAL;
- data->res = res;
ptr = res->port;
while (ptr && ptr->next)
ptr = ptr->next;
@@ -232,9 +235,6 @@
return 0;
}

-
-/* resource removing functions */
-
static void pnp_free_port(struct pnp_port *port)
{
struct pnp_port *next;
@@ -307,469 +307,838 @@
}


-/* resource validity checking functions */
+/*
+ * resource validity checking
+ */

-static int pnp_check_port(int port, int size, int idx, struct pnp_cfg *config)
-{
- int i, tmp, rport, rsize;
- struct pnp_dev *dev;
+#define length(start, end) (*(end) - *(start) + 1)

- if (check_region(port, size))
- return 1;
- for (i = 0; i < 8; i++) {
- rport = pnp_reserve_io[i << 1];
- rsize = pnp_reserve_io[(i << 1) + 1];
- if (port >= rport && port < rport + rsize)
- return 1;
- if (port + size > rport && port + size < (rport + rsize) - 1)
- return 1;
- }
+/* ranged_conflict - used to determine if two resource ranges conflict
+ * condition 1: check if the start of a is within b
+ * condition 2: check if the end of a is within b
+ * condition 3: check if b is engulfed by a */
+
+#define ranged_conflict(starta, enda, startb, endb) \
+((*(starta) >= *(startb) && *(starta) <= *(endb)) || \
+ (*(enda) >= *(startb) && *(enda) <= *(endb)) || \
+ (*(starta) < *(startb) && *(enda) > *(endb)))
+
+#define SEARCH_WARM 1 /* check for conflicts with active devices */
+#define SEARCH_COLD 0 /* check for conflicts with disabled devices */
+
+static struct pnp_dev * pnp_check_port_conflicts(struct pnp_dev * dev, int idx, int mode)
+{
+ int tmp;
+ unsigned long *port, *end, *tport, *tend;
+ struct pnp_dev *tdev;
+ port = &dev->res.port_resource[idx].start;
+ end = &dev->res.port_resource[idx].end;

- pnp_for_each_dev(dev) {
- if (dev->active) {
- for (tmp = 0; tmp < 8; tmp++) {
- if (pnp_port_valid(dev, tmp)) {
- rport = pnp_port_start(dev, tmp);
- rsize = pnp_port_len(dev, tmp);
- if (port >= rport && port < rport + rsize)
- return 1;
- if (port + size > rport && port + size < (rport + rsize) - 1)
- return 1;
- }
+ /* if the resource doesn't exist, don't complain about it */
+ if (dev->res.port_resource[idx].start == 0)
+ return NULL;
+
+ /* check for cold conflicts */
+ pnp_for_each_dev(tdev) {
+ /* Is the device configurable? */
+ if (tdev == dev || mode ? !dev->active : dev->active)
+ continue;
+ for (tmp = 0; tmp < PNP_MAX_PORT; tmp++) {
+ if (tdev->res.port_resource[tmp].flags & IORESOURCE_IO) {
+ tport = &tdev->res.port_resource[tmp].start;
+ tend = &tdev->res.port_resource[tmp].end;
+ if (ranged_conflict(port,end,tport,tend))
+ return tdev;
}
}
}
- for (tmp = 0; tmp < 8 && tmp != idx; tmp++) {
- if (pnp_port_valid(dev, tmp) &&
- pnp_flags_valid(&config->request.io_resource[tmp])) {
- rport = config->request.io_resource[tmp].start;
- rsize = (config->request.io_resource[tmp].end - rport) + 1;
- if (port >= rport && port < rport + rsize)
- return 1;
- if (port + size > rport && port + size < (rport + rsize) - 1)
- return 1;
+ return NULL;
+}
+
+static int pnp_check_port(struct pnp_dev * dev, int idx)
+{
+ int tmp;
+ unsigned long *port, *end, *tport, *tend;
+ port = &dev->res.port_resource[idx].start;
+ end = &dev->res.port_resource[idx].end;
+
+ /* if the resource doesn't exist, don't complain about it */
+ if (dev->res.port_resource[idx].start == 0)
+ return 0;
+
+ /* check if the resource is already in use, skip if the device is active because it itself may be in use */
+ if(!dev->active) {
+ if (check_region(*port, length(port,end)))
+ return CONFLICT_TYPE_IN_USE;
+ }
+
+ /* check if the resource is reserved */
+ for (tmp = 0; tmp < 8; tmp++) {
+ int rport = pnp_reserve_io[tmp << 1];
+ int rend = pnp_reserve_io[(tmp << 1) + 1] + rport - 1;
+ if (ranged_conflict(port,end,&rport,&rend))
+ return CONFLICT_TYPE_RESERVED;
+ }
+
+ /* check for internal conflicts */
+ for (tmp = 0; tmp < PNP_MAX_PORT && tmp != idx; tmp++) {
+ if (dev->res.port_resource[tmp].flags & IORESOURCE_IO) {
+ tport = &dev->res.port_resource[tmp].start;
+ tend = &dev->res.port_resource[tmp].end;
+ if (ranged_conflict(port,end,tport,tend))
+ return CONFLICT_TYPE_INTERNAL;
}
}
+
+ /* check for warm conflicts */
+ if (pnp_check_port_conflicts(dev, idx, SEARCH_WARM))
+ return CONFLICT_TYPE_PNP_WARM;
+
return 0;
}

-static int pnp_check_mem(unsigned int addr, unsigned int size, int idx, struct pnp_cfg *config)
+static struct pnp_dev * pnp_check_mem_conflicts(struct pnp_dev * dev, int idx, int mode)
{
- int i, tmp;
- unsigned int raddr, rsize;
- struct pnp_dev *dev;
-
- for (i = 0; i < 8; i++) {
- raddr = (unsigned int)pnp_reserve_mem[i << 1];
- rsize = (unsigned int)pnp_reserve_mem[(i << 1) + 1];
- if (addr >= raddr && addr < raddr + rsize)
- return 1;
- if (addr + size > raddr && addr + size < (raddr + rsize) - 1)
- return 1;
- if (__check_region(&iomem_resource, addr, size))
- return 1;
- }
- pnp_for_each_dev(dev) {
- if (dev->active) {
- for (tmp = 0; tmp < 4; tmp++) {
- if (pnp_mem_valid(dev, tmp)) {
- raddr = pnp_mem_start(dev, tmp);
- rsize = pnp_mem_len(dev, tmp);
- if (addr >= raddr && addr < raddr + rsize)
- return 1;
- if (addr + size > raddr && addr + size < (raddr + rsize) - 1)
- return 1;
- }
+ int tmp;
+ unsigned long *addr, *end, *taddr, *tend;
+ struct pnp_dev *tdev;
+ addr = &dev->res.mem_resource[idx].start;
+ end = &dev->res.mem_resource[idx].end;
+
+ /* if the resource doesn't exist, don't complain about it */
+ if (dev->res.mem_resource[idx].start == 0)
+ return NULL;
+
+ /* check for cold conflicts */
+ pnp_for_each_dev(tdev) {
+ /* Is the device configurable? */
+ if (tdev == dev || mode ? !dev->active : dev->active)
+ continue;
+ for (tmp = 0; tmp < PNP_MAX_MEM; tmp++) {
+ if (tdev->res.mem_resource[tmp].flags & IORESOURCE_MEM) {
+ taddr = &tdev->res.mem_resource[tmp].start;
+ tend = &tdev->res.mem_resource[tmp].end;
+ if (ranged_conflict(addr,end,taddr,tend))
+ return tdev;
}
}
}
- for (tmp = 0; tmp < 4 && tmp != idx; tmp++) {
- if (pnp_mem_valid(dev, tmp) &&
- pnp_flags_valid(&config->request.mem_resource[tmp])) {
- raddr = config->request.mem_resource[tmp].start;
- rsize = (config->request.mem_resource[tmp].end - raddr) + 1;
- if (addr >= raddr && addr < raddr + rsize)
- return 1;
- if (addr + size > raddr && addr + size < (raddr + rsize) - 1)
- return 1;
+ return NULL;
+}
+
+static int pnp_check_mem(struct pnp_dev * dev, int idx)
+{
+ int tmp;
+ unsigned long *addr, *end, *taddr, *tend;
+ addr = &dev->res.mem_resource[idx].start;
+ end = &dev->res.mem_resource[idx].end;
+
+ /* if the resource doesn't exist, don't complain about it */
+ if (dev->res.mem_resource[idx].start == 0)
+ return 0;
+
+ /* check if the resource is already in use, skip if the device is active because it itself may be in use */
+ if(!dev->active) {
+ if (__check_region(&iomem_resource, *addr, length(addr,end)))
+ return CONFLICT_TYPE_IN_USE;
+ }
+
+ /* check if the resource is reserved */
+ for (tmp = 0; tmp < 8; tmp++) {
+ int raddr = pnp_reserve_mem[tmp << 1];
+ int rend = pnp_reserve_mem[(tmp << 1) + 1] + raddr - 1;
+ if (ranged_conflict(addr,end,&raddr,&rend))
+ return CONFLICT_TYPE_RESERVED;
+ }
+
+ /* check for internal conflicts */
+ for (tmp = 0; tmp < PNP_MAX_MEM && tmp != idx; tmp++) {
+ if (dev->res.mem_resource[tmp].flags & IORESOURCE_MEM) {
+ taddr = &dev->res.mem_resource[tmp].start;
+ tend = &dev->res.mem_resource[tmp].end;
+ if (ranged_conflict(addr,end,taddr,tend))
+ return CONFLICT_TYPE_INTERNAL;
}
}
+
+ /* check for warm conflicts */
+ if (pnp_check_mem_conflicts(dev, idx, SEARCH_WARM))
+ return CONFLICT_TYPE_PNP_WARM;
+
return 0;
}

+static struct pnp_dev * pnp_check_irq_conflicts(struct pnp_dev * dev, int idx, int mode)
+{
+ int tmp;
+ struct pnp_dev * tdev;
+ unsigned long * irq = &dev->res.irq_resource[idx].start;
+
+ /* if the resource doesn't exist, don't complain about it */
+ if (dev->res.irq_resource[idx].start == -1)
+ return NULL;
+
+ /* check for cold conflicts */
+ pnp_for_each_dev(tdev) {
+ /* Is the device configurable? */
+ if (tdev == dev || mode ? !dev->active : dev->active)
+ continue;
+ for (tmp = 0; tmp < PNP_MAX_IRQ; tmp++) {
+ if (tdev->res.irq_resource[tmp].flags & IORESOURCE_IRQ) {
+ if ((tdev->res.irq_resource[tmp].start == *irq))
+ return tdev;
+ }
+ }
+ }
+ return NULL;
+}
+
static void pnp_test_handler(int irq, void *dev_id, struct pt_regs *regs)
{
}

-static int pnp_check_interrupt(int irq, struct pnp_cfg *config)
+static int pnp_check_irq(struct pnp_dev * dev, int idx)
{
- int i;
-#ifdef CONFIG_PCI
- struct pci_dev *pci;
-#endif
- struct pnp_dev *dev;
- if (!config)
- return 1;
+ int tmp;
+ unsigned long * irq = &dev->res.irq_resource[idx].start;

- if (irq < 0 || irq > 15)
- return 1;
- for (i = 0; i < 16; i++) {
- if (pnp_reserve_irq[i] == irq)
- return 1;
+ /* if the resource doesn't exist, don't complain about it */
+ if (dev->res.irq_resource[idx].start == -1)
+ return 0;
+
+ /* check if the resource is valid */
+ if (*irq < 0 || *irq > 15)
+ return CONFLICT_TYPE_INVALID;
+
+ /* check if the resource is reserved */
+ for (tmp = 0; tmp < 16; tmp++) {
+ if (pnp_reserve_irq[tmp] == *irq)
+ return CONFLICT_TYPE_RESERVED;
}
- pnp_for_each_dev(dev) {
- if (dev->active) {
- if ((pnp_irq_valid(dev, 0) && dev->irq_resource[0].start == irq) ||
- (pnp_irq_valid(dev, 1) && dev->irq_resource[1].start == irq))
- return 1;
+
+ /* check for internal conflicts */
+ for (tmp = 0; tmp < PNP_MAX_IRQ && tmp != idx; tmp++) {
+ if (dev->res.irq_resource[tmp].flags & IORESOURCE_IRQ) {
+ if (dev->res.irq_resource[tmp].start == *irq)
+ return CONFLICT_TYPE_INTERNAL;
}
}
- if (pnp_flags_valid(&config->request.irq_resource[0]) &&
- pnp_flags_valid(&config->request.irq_resource[1]) &&
- (config->request.irq_resource[0].start == irq))
- return 1;
+
#ifdef CONFIG_PCI
+ /* check if the resource is being used by a pci device */
if (!pnp_skip_pci_scan) {
+ struct pci_dev * pci;
pci_for_each_dev(pci) {
- if (pci->irq == irq)
- return 1;
+ if (pci->irq == *irq)
+ return CONFLICT_TYPE_PCI;
}
}
#endif
- if (request_irq(irq, pnp_test_handler, SA_INTERRUPT, "pnp", NULL))
- return 1;
- free_irq(irq, NULL);
+
+ /* check if the resource is already in use, skip if the device is active because it itself may be in use */
+ if(!dev->active) {
+ if (request_irq(*irq, pnp_test_handler, SA_INTERRUPT, "pnp", NULL))
+ return CONFLICT_TYPE_IN_USE;
+ free_irq(*irq, NULL);
+ }
+
+ /* check for warm conflicts */
+ if (pnp_check_irq_conflicts(dev, idx, SEARCH_WARM))
+ return CONFLICT_TYPE_PNP_WARM;
+
return 0;
}

-static int pnp_check_dma(int dma, struct pnp_cfg *config)
+
+static struct pnp_dev * pnp_check_dma_conflicts(struct pnp_dev * dev, int idx, int mode)
{
- int i, mindma = 1;
- struct pnp_dev *dev;
- if (!config)
- return 1;
+ int tmp;
+ struct pnp_dev * tdev;
+ unsigned long * dma = &dev->res.dma_resource[idx].start;
+
+ /* if the resource doesn't exist, don't complain about it */
+ if (dev->res.dma_resource[idx].start == -1)
+ return NULL;
+
+ /* check for cold conflicts */
+ pnp_for_each_dev(tdev) {
+ /* Is the device configurable? */
+ if (tdev == dev || mode ? !dev->active : dev->active)
+ continue;
+ for (tmp = 0; tmp < PNP_MAX_DMA; tmp++) {
+ if (tdev->res.dma_resource[tmp].flags & IORESOURCE_DMA) {
+ if ((tdev->res.dma_resource[tmp].start == *dma))
+ return tdev;
+ }
+ }
+ }
+ return NULL;
+}

+static int pnp_check_dma(struct pnp_dev * dev, int idx)
+{
+ int tmp, mindma = 1;
+ unsigned long * dma = &dev->res.dma_resource[idx].start;
+
+ /* if the resource doesn't exist, don't complain about it */
+ if (dev->res.dma_resource[idx].start == -1)
+ return 0;
+
+ /* check if the resource is valid */
if (pnp_allow_dma0 == 1)
mindma = 0;
- if (dma < mindma || dma == 4 || dma > 7)
- return 1;
- for (i = 0; i < 8; i++) {
- if (pnp_reserve_dma[i] == dma)
- return 1;
+ if (*dma < mindma || *dma == 4 || *dma > 7)
+ return CONFLICT_TYPE_INVALID;
+
+ /* check if the resource is reserved */
+ for (tmp = 0; tmp < 8; tmp++) {
+ if (pnp_reserve_dma[tmp] == *dma)
+ return CONFLICT_TYPE_RESERVED;
}
- pnp_for_each_dev(dev) {
- if (dev->active) {
- if ((pnp_dma_valid(dev, 0) && pnp_dma(dev, 0) == dma) ||
- (pnp_dma_valid(dev, 1) && pnp_dma(dev, 1) == dma))
- return 1;
+
+ /* check for internal conflicts */
+ for (tmp = 0; tmp < PNP_MAX_DMA && tmp != idx; tmp++) {
+ if (dev->res.dma_resource[tmp].flags & IORESOURCE_DMA) {
+ if (dev->res.dma_resource[tmp].start == *dma)
+ return CONFLICT_TYPE_INTERNAL;
}
}
- if (pnp_flags_valid(&config->request.dma_resource[0]) &&
- pnp_flags_valid(&config->request.dma_resource[1]) &&
- (config->request.dma_resource[0].start == dma))
- return 1;
- if (request_dma(dma, "pnp"))
- return 1;
- free_dma(dma);
+
+ /* check if the resource is already in use, skip if the device is active because it itself may be in use */
+ if(!dev->active) {
+ if (request_dma(*dma, "pnp"))
+ return CONFLICT_TYPE_IN_USE;
+ free_dma(*dma);
+ }
+
+ /* check for warm conflicts */
+ if (pnp_check_dma_conflicts(dev, idx, SEARCH_WARM))
+ return CONFLICT_TYPE_PNP_WARM;
return 0;
}


-/* config generation functions */
-static int pnp_generate_port(struct pnp_cfg *config, int num)
+/*
+ * configuration
+ */
+
+static int pnp_next_port(struct pnp_dev * dev, int idx)
{
struct pnp_port *port;
unsigned long *value1, *value2, *value3;
- if (!config || num < 0 || num > 7)
- return -EINVAL;
- port = config->port[num];
- if (!port)
+ if (!dev || idx < 0 || idx >= PNP_MAX_PORT)
return 0;
- value1 = &config->request.io_resource[num].start;
- value2 = &config->request.io_resource[num].end;
- value3 = &config->request.io_resource[num].flags;
- *value1 = port->min;
- *value2 = *value1 + port->size - 1;
- *value3 = port->flags | IORESOURCE_IO;
- while (pnp_check_port(*value1, port->size, num, config)) {
+ port = dev->rule->port[idx];
+ if (!port)
+ return 1;
+
+ value1 = &dev->res.port_resource[idx].start;
+ value2 = &dev->res.port_resource[idx].end;
+ value3 = &dev->res.port_resource[idx].flags;
+
+ /* set the initial values if this is the first time */
+ if (*value1 == 0) {
+ *value1 = port->min;
+ *value2 = *value1 + port->size -1;
+ *value3 = port->flags | IORESOURCE_IO;
+ if (!pnp_check_port(dev, idx))
+ return 1;
+ }
+
+ /* run through until pnp_check_port is happy */
+ do {
*value1 += port->align;
*value2 = *value1 + port->size - 1;
if (*value1 > port->max || !port->align)
- return -ENOENT;
- }
- return 0;
+ return 0;
+ } while (pnp_check_port(dev, idx));
+ return 1;
}

-static int pnp_generate_mem(struct pnp_cfg *config, int num)
+static int pnp_next_mem(struct pnp_dev * dev, int idx)
{
struct pnp_mem *mem;
unsigned long *value1, *value2, *value3;
- if (!config || num < 0 || num > 3)
- return -EINVAL;
- mem = config->mem[num];
- if (!mem)
+ if (!dev || idx < 0 || idx >= PNP_MAX_MEM)
return 0;
- value1 = &config->request.mem_resource[num].start;
- value2 = &config->request.mem_resource[num].end;
- value3 = &config->request.mem_resource[num].flags;
- *value1 = mem->min;
- *value2 = *value1 + mem->size - 1;
- *value3 = mem->flags | IORESOURCE_MEM;
- if (!(mem->flags & IORESOURCE_MEM_WRITEABLE))
- *value3 |= IORESOURCE_READONLY;
- if (mem->flags & IORESOURCE_MEM_CACHEABLE)
- *value3 |= IORESOURCE_CACHEABLE;
- if (mem->flags & IORESOURCE_MEM_RANGELENGTH)
- *value3 |= IORESOURCE_RANGELENGTH;
- if (mem->flags & IORESOURCE_MEM_SHADOWABLE)
- *value3 |= IORESOURCE_SHADOWABLE;
- while (pnp_check_mem(*value1, mem->size, num, config)) {
+ mem = dev->rule->mem[idx];
+ if (!mem)
+ return 1;
+
+ value1 = &dev->res.mem_resource[idx].start;
+ value2 = &dev->res.mem_resource[idx].end;
+ value3 = &dev->res.mem_resource[idx].flags;
+
+ /* set the initial values if this is the first time */
+ if (*value1 == 0) {
+ *value1 = mem->min;
+ *value2 = *value1 + mem->size -1;
+ *value3 = mem->flags | IORESOURCE_MEM;
+ if (!(mem->flags & IORESOURCE_MEM_WRITEABLE))
+ *value3 |= IORESOURCE_READONLY;
+ if (mem->flags & IORESOURCE_MEM_CACHEABLE)
+ *value3 |= IORESOURCE_CACHEABLE;
+ if (mem->flags & IORESOURCE_MEM_RANGELENGTH)
+ *value3 |= IORESOURCE_RANGELENGTH;
+ if (mem->flags & IORESOURCE_MEM_SHADOWABLE)
+ *value3 |= IORESOURCE_SHADOWABLE;
+ if (!pnp_check_mem(dev, idx))
+ return 1;
+ }
+
+ /* run through until pnp_check_mem is happy */
+ do {
*value1 += mem->align;
*value2 = *value1 + mem->size - 1;
if (*value1 > mem->max || !mem->align)
- return -ENOENT;
- }
- return 0;
+ return 0;
+ } while (pnp_check_mem(dev, idx));
+ return 1;
}

-static int pnp_generate_irq(struct pnp_cfg *config, int num)
+static int pnp_next_irq(struct pnp_dev * dev, int idx)
{
struct pnp_irq *irq;
unsigned long *value1, *value2, *value3;
- /* IRQ priority: this table is good for i386 */
- static unsigned short xtab[16] = {
- 5, 10, 11, 12, 9, 14, 15, 7, 3, 4, 13, 0, 1, 6, 8, 2
- };
- int i;
- if (!config || num < 0 || num > 1)
- return -EINVAL;
- irq = config->irq[num];
- if (!irq)
+ int i, mask;
+ if (!dev || idx < 0 || idx >= PNP_MAX_IRQ)
return 0;
- value1 = &config->request.irq_resource[num].start;
- value2 = &config->request.irq_resource[num].end;
- value3 = &config->request.irq_resource[num].flags;
- *value3 = irq->flags | IORESOURCE_IRQ;
+ irq = dev->rule->irq[idx];
+ if (!irq)
+ return 1;

- for (i=0; i < 16; i++)
+ value1 = &dev->res.irq_resource[idx].start;
+ value2 = &dev->res.irq_resource[idx].end;
+ value3 = &dev->res.irq_resource[idx].flags;
+
+ /* set the initial values if this is the first time */
+ if (*value1 == -1) {
+ *value1 = *value2 = 0;
+ *value3 = irq->flags | IORESOURCE_IRQ;
+ if (!pnp_check_irq(dev, idx))
+ return 1;
+ }
+
+ mask = irq->map;
+ for (i = *value1; i < 16; i++, mask=mask>>1)
{
- if(irq->map & (1<<xtab[i])) {
- *value1 = *value2 = xtab[i];
- if(pnp_check_interrupt(*value1,config)==0)
- return 0;
+ if(mask & 0x01) {
+ *value1 = *value2 = i;
+ if(!pnp_check_irq(dev, idx))
+ return 1;
}
}
- return -ENOENT;
+ return 0;
}

-static int pnp_generate_dma(struct pnp_cfg *config, int num)
+static int pnp_next_dma(struct pnp_dev * dev, int idx)
{
struct pnp_dma *dma;
+ struct resource backup;
unsigned long *value1, *value2, *value3;
- /* DMA priority: this table is good for i386 */
- static unsigned short xtab[16] = {
- 1, 3, 5, 6, 7, 0, 2, 4
- };
- int i;
- if (!config || num < 0 || num > 1)
+ int i, mask;
+ if (!dev || idx < 0 || idx >= PNP_MAX_DMA)
return -EINVAL;
- dma = config->dma[num];
+ dma = dev->rule->dma[idx];
if (!dma)
- return 0;
- value1 = &config->request.dma_resource[num].start;
- value2 = &config->request.dma_resource[num].end;
- value3 = &config->request.dma_resource[num].flags;
+ return 1;
+
+ value1 = &dev->res.dma_resource[idx].start;
+ value2 = &dev->res.dma_resource[idx].end;
+ value3 = &dev->res.dma_resource[idx].flags;
*value3 = dma->flags | IORESOURCE_DMA;
+ backup = dev->res.dma_resource[idx];

- for (i=0; i < 8; i++)
+ /* set the initial values if this is the first time */
+ if (*value1 == -1) {
+ *value1 = *value2 = 0;
+ *value3 = dma->flags | IORESOURCE_DMA;
+ if (!pnp_check_dma(dev, idx))
+ return 1;
+ }
+
+ mask = dma->map;
+ for (i = *value1; i < 8; i++, mask=mask>>1)
{
- if(dma->map & (1<<xtab[i])) {
- *value1 = *value2 = xtab[i];
- if(pnp_check_dma(*value1,config)==0)
- return 0;
+ if(mask & 0x01) {
+ *value1 = *value2 = i;
+ if(!pnp_check_dma(dev, idx))
+ return 1;
}
}
- return -ENOENT;
+ dev->res.dma_resource[idx] = backup;
+ return 0;
}

-int pnp_init_res_cfg(struct pnp_res_cfg *res_config)
+void pnp_init_resource_table(struct pnp_resource_table *table)
{
int idx;
-
- if (!res_config)
- return -EINVAL;
- for (idx = 0; idx < DEVICE_COUNT_IRQ; idx++) {
- res_config->irq_resource[idx].start = -1;
- res_config->irq_resource[idx].end = -1;
- res_config->irq_resource[idx].flags = IORESOURCE_IRQ|IORESOURCE_UNSET;
+ for (idx = 0; idx < PNP_MAX_IRQ; idx++) {
+ table->irq_resource[idx].name = NULL;
+ table->irq_resource[idx].start = -1;
+ table->irq_resource[idx].end = -1;
+ table->irq_resource[idx].flags = 0;
}
- for (idx = 0; idx < DEVICE_COUNT_DMA; idx++) {
- res_config->dma_resource[idx].name = NULL;
- res_config->dma_resource[idx].start = -1;
- res_config->dma_resource[idx].end = -1;
- res_config->dma_resource[idx].flags = IORESOURCE_DMA|IORESOURCE_UNSET;
+ for (idx = 0; idx < PNP_MAX_DMA; idx++) {
+ table->dma_resource[idx].name = NULL;
+ table->dma_resource[idx].start = -1;
+ table->dma_resource[idx].end = -1;
+ table->dma_resource[idx].flags = 0;
}
- for (idx = 0; idx < DEVICE_COUNT_IO; idx++) {
- res_config->io_resource[idx].name = NULL;
- res_config->io_resource[idx].start = 0;
- res_config->io_resource[idx].end = 0;
- res_config->io_resource[idx].flags = IORESOURCE_IO|IORESOURCE_UNSET;
+ for (idx = 0; idx < PNP_MAX_PORT; idx++) {
+ table->port_resource[idx].name = NULL;
+ table->port_resource[idx].start = 0;
+ table->port_resource[idx].end = 0;
+ table->port_resource[idx].flags = 0;
}
- for (idx = 0; idx < DEVICE_COUNT_MEM; idx++) {
- res_config->mem_resource[idx].name = NULL;
- res_config->mem_resource[idx].start = 0;
- res_config->mem_resource[idx].end = 0;
- res_config->mem_resource[idx].flags = IORESOURCE_MEM|IORESOURCE_UNSET;
+ for (idx = 0; idx < PNP_MAX_MEM; idx++) {
+ table->mem_resource[idx].name = NULL;
+ table->mem_resource[idx].start = 0;
+ table->mem_resource[idx].end = 0;
+ table->mem_resource[idx].flags = 0;
}
- return 0;
}

-static int pnp_prepare_request(struct pnp_dev *dev, struct pnp_cfg *config, struct pnp_res_cfg *template)
+struct pnp_change {
+ struct list_head change_list;
+ struct list_head changes;
+ struct pnp_resource_table res_bak;
+ struct pnp_rule_table rule_bak;
+ struct pnp_dev * dev;
+};
+
+static void pnp_free_changes(struct pnp_change * parent)
{
- int idx, err;
- if (!config)
- return -EINVAL;
- if (dev->lock_resources)
- return -EPERM;
- if (dev->active)
- return -EBUSY;
- err = pnp_init_res_cfg(&config->request);
- if (err < 0)
- return err;
- if (!template)
- return 0;
- for (idx = 0; idx < DEVICE_COUNT_IRQ; idx++)
- if (pnp_flags_valid(&template->irq_resource[idx]))
- config->request.irq_resource[idx] = template->irq_resource[idx];
- for (idx = 0; idx < DEVICE_COUNT_DMA; idx++)
- if (pnp_flags_valid(&template->dma_resource[idx]))
- config->request.dma_resource[idx] = template->dma_resource[idx];
- for (idx = 0; idx < DEVICE_COUNT_IO; idx++)
- if (pnp_flags_valid(&template->io_resource[idx]))
- config->request.io_resource[idx] = template->io_resource[idx];
- for (idx = 0; idx < DEVICE_COUNT_MEM; idx++)
- if (pnp_flags_valid(&template->io_resource[idx]))
- config->request.mem_resource[idx] = template->mem_resource[idx];
- return 0;
+ struct list_head * pos, * temp;
+ list_for_each_safe(pos, temp, &parent->changes) {
+ struct pnp_change * change = list_entry(pos, struct pnp_change, change_list);
+ list_del(&change->change_list);
+ kfree(change);
+ }
}

-static int pnp_generate_request(struct pnp_dev *dev, struct pnp_cfg *config, struct pnp_res_cfg *template)
-{
- int i, err;
- if (!config)
- return -EINVAL;
- if ((err = pnp_prepare_request(dev, config, template))<0)
- return err;
- for (i=0; i<=7; i++)
- {
- if(pnp_generate_port(config,i)<0)
- return -ENOENT;
- }
- for (i=0; i<=3; i++)
- {
- if(pnp_generate_mem(config,i)<0)
- return -ENOENT;
- }
- for (i=0; i<=1; i++)
- {
- if(pnp_generate_irq(config,i)<0)
- return -ENOENT;
- }
- for (i=0; i<=1; i++)
- {
- if(pnp_generate_dma(config,i)<0)
- return -ENOENT;
+static void pnp_undo_changes(struct pnp_change * parent)
+{
+ struct list_head * pos, * temp;
+ list_for_each_safe(pos, temp, &parent->changes) {
+ struct pnp_change * change = list_entry(pos, struct pnp_change, change_list);
+ *change->dev->rule = change->rule_bak;
+ change->dev->res = change->res_bak;
+ list_del(&change->change_list);
+ kfree(change);
}
- return 0;
}

+static struct pnp_change * pnp_add_change(struct pnp_change * parent, struct pnp_dev * dev)
+{
+ struct pnp_change * change = pnp_alloc(sizeof(struct pnp_change));
+ change->res_bak = dev->res;
+ change->rule_bak = *dev->rule;
+ change->dev = dev;
+ INIT_LIST_HEAD(&change->changes);
+ if (parent)
+ list_add(&change->change_list, &parent->changes);
+ return change;
+}

+static void pnp_commit_changes(struct pnp_change * parent, struct pnp_change * change)
+{
+ /* check if it's the root change */
+ if (!parent)
+ return;
+ if (!list_empty(&change->changes))
+ list_splice_init(&change->changes, &parent->changes);
+}

-static struct pnp_cfg * pnp_generate_config(struct pnp_dev *dev, int depnum)
+static int pnp_generate_rule(struct pnp_dev *dev, int depnum)
{
- struct pnp_cfg * config;
int nport = 0, nirq = 0, ndma = 0, nmem = 0;
struct pnp_resources * res;
struct pnp_port * port;
struct pnp_mem * mem;
struct pnp_irq * irq;
struct pnp_dma * dma;
- if (!dev)
- return NULL;
- if (depnum < 0)
- return NULL;
- config = pnp_alloc(sizeof(struct pnp_cfg));
- if (!config)
- return NULL;
+ struct pnp_rule_table * rule = dev->rule;
+
+ if (depnum <= 0 || !rule)
+ return -EINVAL;

/* independent */
res = pnp_find_resources(dev, 0);
if (!res)
- goto fail;
+ return -ENODEV;
port = res->port;
mem = res->mem;
irq = res->irq;
dma = res->dma;
while (port){
- config->port[nport] = port;
+ rule->port[nport] = port;
nport++;
port = port->next;
}
while (mem){
- config->mem[nmem] = mem;
+ rule->mem[nmem] = mem;
nmem++;
mem = mem->next;
}
while (irq){
- config->irq[nirq] = irq;
+ rule->irq[nirq] = irq;
nirq++;
irq = irq->next;
}
while (dma){
- config->dma[ndma] = dma;
+ rule->dma[ndma] = dma;
ndma++;
dma = dma->next;
}

/* dependent */
- if (depnum == 0)
- return config;
res = pnp_find_resources(dev, depnum);
if (!res)
- goto fail;
+ return -ENODEV;
port = res->port;
mem = res->mem;
irq = res->irq;
dma = res->dma;
while (port){
- config->port[nport] = port;
+ rule->port[nport] = port;
nport++;
port = port->next;
}
while (mem){
- config->mem[nmem] = mem;
+ rule->mem[nmem] = mem;
nmem++;
mem = mem->next;
}

while (irq){
- config->irq[nirq] = irq;
+ rule->irq[nirq] = irq;
nirq++;
irq = irq->next;
}
while (dma){
- config->dma[ndma] = dma;
+ rule->dma[ndma] = dma;
ndma++;
dma = dma->next;
}
- return config;

- fail:
- kfree(config);
- return NULL;
+ /* clear the remaining values */
+ for (; nport < PNP_MAX_PORT; nport++)
+ rule->port[nport] = NULL;
+ for (; nmem < PNP_MAX_MEM; nmem++)
+ rule->mem[nmem] = NULL;
+ for (; nirq < PNP_MAX_IRQ; nirq++)
+ rule->irq[nirq] = NULL;
+ for (; ndma < PNP_MAX_DMA; ndma++)
+ rule->dma[ndma] = NULL;
+ return 1;
+}
+
+static int pnp_next_rule(struct pnp_dev *dev)
+{
+ int depnum = dev->rule->depnum;
+ int max = pnp_get_max_depnum(dev);
+ int priority = PNP_RES_PRIORITY_PREFERRED;
+
+ if(depnum > 0) {
+ struct pnp_resources * res = pnp_find_resources(dev, depnum);
+ priority = res->priority;
+ }
+
+ for (; priority <= PNP_RES_PRIORITY_FUNCTIONAL; priority++, depnum=0) {
+ depnum += 1;
+ for (; depnum <= max; depnum++) {
+ struct pnp_resources * res = pnp_find_resources(dev, depnum);
+ if (res->priority == priority) {
+ if(pnp_generate_rule(dev, depnum)) {
+ dev->rule->depnum = depnum;
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
}

-/* PnP Device Resource Management */
+static int pnp_next_config(struct pnp_dev * dev, int move, struct pnp_change * parent);
+
+static int pnp_next_request(struct pnp_dev * dev, int move, struct pnp_change * parent, struct pnp_change * change)
+{
+ int i;
+ struct pnp_dev * cdev;
+
+ for (i = 0; i < PNP_MAX_PORT; i++) {
+ if (dev->res.port_resource[i].start == 0) {
+ if (!pnp_next_port(dev,i))
+ return 0;
+ }
+ do {
+ cdev = pnp_check_port_conflicts(dev,i,SEARCH_COLD);
+ if (cdev && (!move || !pnp_next_config(cdev,move,change))) {
+ pnp_undo_changes(change);
+ if (!pnp_next_port(dev,i))
+ return 0;
+ }
+ } while (cdev);
+ pnp_commit_changes(parent, change);
+ }
+ for (i = 0; i < PNP_MAX_MEM; i++) {
+ if (dev->res.mem_resource[i].start == 0) {
+ if (!pnp_next_mem(dev,i))
+ return 0;
+ }
+ do {
+ cdev = pnp_check_mem_conflicts(dev,i,SEARCH_COLD);
+ if (cdev && (!move || !pnp_next_config(cdev,move,change))) {
+ pnp_undo_changes(change);
+ if (!pnp_next_mem(dev,i))
+ return 0;
+ }
+ } while (cdev);
+ pnp_commit_changes(parent, change);
+ }
+ for (i = 0; i < PNP_MAX_IRQ; i++) {
+ if (dev->res.irq_resource[i].start == -1) {
+ if (!pnp_next_irq(dev,i))
+ return 0;
+ }
+ do {
+ cdev = pnp_check_irq_conflicts(dev,i,SEARCH_COLD);
+ if (cdev && (!move || !pnp_next_config(cdev,move,change))) {
+ pnp_undo_changes(change);
+ if (!pnp_next_irq(dev,i))
+ return 0;
+ }
+ } while (cdev);
+ pnp_commit_changes(parent, change);
+ }
+ for (i = 0; i < PNP_MAX_DMA; i++) {
+ if (dev->res.dma_resource[i].start == -1) {
+ if (!pnp_next_dma(dev,i))
+ return 0;
+ }
+ do {
+ cdev = pnp_check_dma_conflicts(dev,i,SEARCH_COLD);
+ if (cdev && (!move || !pnp_next_config(cdev,move,change))) {
+ pnp_undo_changes(change);
+ if (!pnp_next_dma(dev,i))
+ return 0;
+ }
+ } while (cdev);
+ pnp_commit_changes(parent, change);
+ }
+ return 1;
+}
+
+static int pnp_next_config(struct pnp_dev * dev, int move, struct pnp_change * parent)
+{
+ struct pnp_change * change = pnp_add_change(parent,dev);
+ move--;
+ if (!pnp_can_configure(dev))
+ goto fail;
+ if (!dev->rule->depnum) {
+ if (!pnp_next_rule(dev))
+ goto fail;
+ }
+ while (!pnp_next_request(dev, move, parent, change)) {
+ if(!pnp_next_rule(dev))
+ goto fail;
+ pnp_init_resource_table(&dev->res);
+ }
+ if (!parent) {
+ pnp_free_changes(change);
+ kfree(change);
+ }
+ return 1;
+
+fail:
+ if (!parent)
+ kfree(change);
+ return 0;
+}
+
+static int pnp_generate_config(struct pnp_dev * dev)
+{
+ int move;
+ if (!dev->rule) {
+ dev->rule = pnp_alloc(sizeof(struct pnp_rule_table));
+ if (!dev->rule)
+ return -ENOMEM;
+ }
+ for (move = 1; move <= pnp_max_moves; move++) {
+ dev->rule->depnum = 0;
+ pnp_init_resource_table(&dev->res);
+ if (pnp_next_config(dev,move,NULL))
+ return 1;
+ }
+
+ pnp_init_resource_table(&dev->res);
+ dev->rule->depnum = 0;
+ printk (KERN_ERR "pnp: Unable to resolve resource conflicts for the device '%s', this device will not be usable.\n", dev->dev.bus_id);
+ return 0;
+}
+
+static int pnp_process_active(struct pnp_dev *dev)
+{
+ int i;
+ struct pnp_dev * cdev;
+
+ for (i = 0; i < PNP_MAX_PORT; i++)
+ {
+ do {
+ cdev = pnp_check_port_conflicts(dev,i,SEARCH_COLD);
+ if (cdev)
+ pnp_generate_config(cdev);
+ } while (cdev);
+ }
+ for (i = 0; i < PNP_MAX_MEM; i++)
+ {
+ do {
+ cdev = pnp_check_mem_conflicts(dev,i,SEARCH_COLD);
+ if (cdev)
+ pnp_generate_config(cdev);
+ } while (cdev);
+ }
+ for (i = 0; i < PNP_MAX_IRQ; i++)
+ {
+ do {
+ cdev = pnp_check_irq_conflicts(dev,i,SEARCH_COLD);
+ if (cdev)
+ pnp_generate_config(cdev);
+ } while (cdev);
+ }
+ for (i = 0; i < PNP_MAX_DMA; i++)
+ {
+ do {
+ cdev = pnp_check_dma_conflicts(dev,i,SEARCH_COLD);
+ if (cdev)
+ pnp_generate_config(cdev);
+ } while (cdev);
+ }
+ return 1;
+}
+
+/**
+ * pnp_configure_device - determines the best possible resource configuration based on available information
+ * @dev: pointer to the desired device
+ */
+
+int pnp_configure_device(struct pnp_dev *dev)
+{
+ int error;
+ if(!dev)
+ return -EINVAL;
+ if(dev->active)
+ error = pnp_process_active(dev);
+ else
+ error = pnp_generate_config(dev);
+ return error;
+}
+
+
+/*
+ * PnP Device Resource Management
+ */

/**
* pnp_activate_dev - activates a PnP device for use
@@ -778,41 +1147,24 @@
* finds the best resource configuration and then informs the correct pnp protocol
*/

-int pnp_activate_dev(struct pnp_dev *dev, struct pnp_res_cfg *template)
+int pnp_activate_dev(struct pnp_dev *dev)
{
- int depnum, max;
- struct pnp_cfg *config;
if (!dev)
return -EINVAL;
- max = pnp_get_max_depnum(dev);
if (!pnp_can_configure(dev))
return -EBUSY;
if (dev->status != PNP_READY && dev->status != PNP_ATTACHED){
- printk(KERN_INFO "pnp: Automatic configuration failed because the PnP device '%s' is busy\n", dev->dev.bus_id);
+ printk(KERN_INFO "pnp: Activation failed because the PnP device '%s' is busy\n", dev->dev.bus_id);
return -EINVAL;
}
if (!pnp_can_write(dev))
return -EINVAL;
- if (max == 0)
- return 0;
- for (depnum=1; depnum <= max; depnum++)
- {
- config = pnp_generate_config(dev,depnum);
- if (!config)
- return -EINVAL;
- if (pnp_generate_request(dev,config,template)==0)
- goto done;
- kfree(config);
- }
- printk(KERN_ERR "pnp: Automatic configuration failed for device '%s' due to resource conflicts\n", dev->dev.bus_id);
- return -ENOENT;

- done:
pnp_dbg("the device '%s' has been activated", dev->dev.bus_id);
- dev->protocol->set(dev,config);
+ dev->protocol->set(dev);
if (pnp_can_read(dev))
dev->protocol->get(dev);
- kfree(config);
+ kfree(dev->rule);
return 0;
}

@@ -840,54 +1192,18 @@
}

/**
- * pnp_raw_set_dev - same as pnp_activate_dev except the resource config can be specified
- * @dev: pointer to the desired device
- * @depnum: resource dependent function
- * @mode: static or dynamic
- *
- */
-
-int pnp_raw_set_dev(struct pnp_dev *dev, int depnum, struct pnp_res_cfg *template)
-{
- struct pnp_cfg *config;
- if (!dev)
- return -EINVAL;
- if (dev->status != PNP_READY){
- printk(KERN_INFO "pnp: Unable to set resources because the PnP device '%s' is busy\n", dev->dev.bus_id);
- return -EINVAL;
- }
- if (!pnp_can_write(dev) || !pnp_can_configure(dev))
- return -EINVAL;
- config = pnp_generate_config(dev,depnum);
- if (!config)
- return -EINVAL;
- if (pnp_generate_request(dev,config,template)==0)
- goto done;
- kfree(config);
- printk(KERN_ERR "pnp: Manual configuration failed for device '%s' due to resource conflicts\n", dev->dev.bus_id);
- return -ENOENT;
-
- done:
- dev->protocol->set(dev,config);
- if (pnp_can_read(dev))
- dev->protocol->get(dev);
- kfree(config);
- return 0;
-}
-
-/**
* pnp_resource_change - change one resource
* @resource: pointer to resource to be changed
* @start: start of region
* @size: size of region
*
*/
-
+
void pnp_resource_change(struct resource *resource, unsigned long start, unsigned long size)
{
if (resource == NULL)
return;
- resource->flags &= ~(IORESOURCE_AUTO|IORESOURCE_UNSET);
+ resource->flags &= ~IORESOURCE_AUTO;
resource->start = start;
resource->end = start + size - 1;
}
@@ -900,10 +1216,9 @@
EXPORT_SYMBOL(pnp_add_port_resource);
EXPORT_SYMBOL(pnp_add_mem_resource);
EXPORT_SYMBOL(pnp_add_mem32_resource);
-EXPORT_SYMBOL(pnp_init_res_cfg);
+EXPORT_SYMBOL(pnp_init_resource_table);
EXPORT_SYMBOL(pnp_activate_dev);
EXPORT_SYMBOL(pnp_disable_dev);
-EXPORT_SYMBOL(pnp_raw_set_dev);
EXPORT_SYMBOL(pnp_resource_change);

/* format is: allowdma0 */
@@ -915,6 +1230,16 @@
}

__setup("allowdma0", pnp_allowdma0);
+
+/* format is: pnp_max_moves=num */
+
+static int __init pnp_setup_max_moves(char *str)
+{
+ get_option(&str,&pnp_max_moves);
+ return 1;
+}
+
+__setup("pnp_max_moves=", pnp_setup_max_moves);

/* format is: pnp_reserve_irq=irq1[,irq2] .... */

diff -urN a/drivers/pnp/system.c b/drivers/pnp/system.c
--- a/drivers/pnp/system.c Fri Jan 31 16:59:57 2003
+++ b/drivers/pnp/system.c Sat Feb 1 16:52:08 2003
@@ -53,7 +53,7 @@
{
int i;

- for (i=0;i<DEVICE_COUNT_IO;i++) {
+ for (i=0;i<PNP_MAX_PORT;i++) {
if (pnp_port_valid(dev, i))
/* end of resources */
continue;
diff -urN a/drivers/serial/8250_pnp.c b/drivers/serial/8250_pnp.c
--- a/drivers/serial/8250_pnp.c Fri Jan 31 16:59:57 2003
+++ b/drivers/serial/8250_pnp.c Sat Feb 1 16:52:08 2003
@@ -317,7 +317,7 @@
{
unsigned int map = 0x1FF8;
struct pnp_irq *irq;
- struct pnp_resources *res = dev->res;
+ struct pnp_resources *res = dev->possible;

serial8250_get_irq_map(&map);

@@ -357,10 +357,10 @@
*/
static int serial_pnp_guess_board(struct pnp_dev *dev, int *flags)
{
- struct pnp_resources *res = dev->res;
+ struct pnp_resources *res = dev->possible;
struct pnp_resources *resa;

- if (!(check_name(dev->name) || (dev->card && check_name(dev->card->name))))
+ if (!(check_name(dev->dev.name) || (dev->card && check_name(dev->card->dev.name))))
return -ENODEV;

if (!res)
diff -urN a/include/linux/pnp.h b/include/linux/pnp.h
--- a/include/linux/pnp.h Fri Jan 31 16:59:57 2003
+++ b/include/linux/pnp.h Sat Feb 1 17:13:30 2003
@@ -13,166 +13,16 @@
#include <linux/list.h>
#include <linux/errno.h>

+#define PNP_MAX_PORT 8
+#define PNP_MAX_MEM 4
+#define PNP_MAX_IRQ 2
+#define PNP_MAX_DMA 2
+#define PNP_MAX_DEVICES 8
+#define PNP_ID_LEN 8

-/*
- * Device Managemnt
- */
-
-#define DEVICE_COUNT_IRQ 2
-#define DEVICE_COUNT_DMA 2
-#define DEVICE_COUNT_IO 8
-#define DEVICE_COUNT_MEM 4
-#define MAX_DEVICES 8
-
-struct pnp_resource;
struct pnp_protocol;
-struct pnp_id;
-struct pnp_cfg;
-
-struct pnp_card {
- char name[80];
- int status; /* status of the card */
- unsigned char number; /* card number */
- struct list_head global_list; /* node in global list of cards */
- struct list_head protocol_list; /* node in protocol's list of cards */
- struct list_head devices; /* devices attached to the card */
- struct pnp_protocol * protocol;
- struct pnp_id * id; /* contains supported EISA IDs*/
-
- unsigned char pnpver; /* Plug & Play version */
- unsigned char productver; /* product version */
- unsigned int serial; /* serial number */
- unsigned char checksum; /* if zero - checksum passed */
- void * protocol_data; /* Used to store protocol specific data */
-
- struct pnpc_driver * driver; /* pointer to the driver bound to this device */
- struct list_head rdevs; /* a list of devices requested by the card driver */
- struct proc_dir_entry *procdir; /* directory entry in /proc/bus/isapnp */
- struct device dev; /* Driver Model device interface */
-};
-
-#define global_to_pnp_card(n) list_entry(n, struct pnp_card, global_list)
-#define protocol_to_pnp_card(n) list_entry(n, struct pnp_card, protocol_list)
-#define to_pnp_card(n) container_of(n, struct pnp_card, dev)
-#define pnp_for_each_card(card) \
- for((card) = global_to_pnp_card(pnp_cards.next); \
- (card) != global_to_pnp_card(&pnp_cards); \
- (card) = global_to_pnp_card((card)->global_list.next))
-#define pnp_card_for_each_dev(card,dev) \
- for((dev) = card_to_pnp_dev((card)->devices.next); \
- (dev) != card_to_pnp_dev(&(card)->devices); \
- (dev) = card_to_pnp_dev((dev)->card_list.next))
-
-static inline void *pnpc_get_drvdata (struct pnp_card *pcard)
-{
- return dev_get_drvdata(&pcard->dev);
-}
-
-static inline void pnpc_set_drvdata (struct pnp_card *pcard, void *data)
-{
- dev_set_drvdata(&pcard->dev, data);
-}
-
-static inline void *pnpc_get_protodata (struct pnp_card *pcard)
-{
- return pcard->protocol_data;
-}
-
-static inline void pnpc_set_protodata (struct pnp_card *pcard, void *data)
-{
- pcard->protocol_data = data;
-}
-
-struct pnp_dev {
- char name[80]; /* device name */
- int active; /* status of the device */
- int capabilities;
- int status;
-
- struct list_head global_list; /* node in global list of devices */
- struct list_head protocol_list; /* node in list of device's protocol */
- struct list_head card_list; /* node in card's list of devices */
- struct list_head rdev_list; /* node in cards list of requested devices */
- struct pnp_protocol * protocol;
- struct pnp_card * card;
- struct pnp_id * id; /* contains supported EISA IDs*/
-
- void * protocol_data; /* Used to store protocol specific data */
- unsigned char number; /* must be unique */
- unsigned short regs; /* ISAPnP: supported registers */
-
- struct pnp_resources *res; /* possible resource information */
- int lock_resources; /* resources are locked */
- struct resource io_resource[DEVICE_COUNT_IO]; /* port regions */
- struct resource mem_resource[DEVICE_COUNT_MEM]; /* memory regions + expansion ROMs */
- struct resource dma_resource[DEVICE_COUNT_DMA];
- struct resource irq_resource[DEVICE_COUNT_IRQ];
-
- struct pnp_driver * driver; /* pointer to the driver bound to this device */
- struct device dev; /* Driver Model device interface */
- int flags; /* used by protocols */
- struct proc_dir_entry *procent; /* device entry in /proc/bus/isapnp */
-};
-
-#define global_to_pnp_dev(n) list_entry(n, struct pnp_dev, global_list)
-#define card_to_pnp_dev(n) list_entry(n, struct pnp_dev, card_list)
-#define protocol_to_pnp_dev(n) list_entry(n, struct pnp_dev, protocol_list)
-#define to_pnp_dev(n) container_of(n, struct pnp_dev, dev)
-#define pnp_for_each_dev(dev) \
- for(dev = global_to_pnp_dev(pnp_global.next); \
- dev != global_to_pnp_dev(&pnp_global); \
- dev = global_to_pnp_dev(dev->global_list.next))
-#define card_for_each_dev(card,dev) \
- for((dev) = card_to_pnp_dev((card)->devices.next); \
- (dev) != card_to_pnp_dev(&(card)->devices); \
- (dev) = card_to_pnp_dev((dev)->card_list.next))
-
-static inline void *pnp_get_drvdata (struct pnp_dev *pdev)
-{
- return dev_get_drvdata(&pdev->dev);
-}
-
-static inline void pnp_set_drvdata (struct pnp_dev *pdev, void *data)
-{
- dev_set_drvdata(&pdev->dev, data);
-}
-
-static inline void *pnp_get_protodata (struct pnp_dev *pdev)
-{
- return pdev->protocol_data;
-}
-
-static inline void pnp_set_protodata (struct pnp_dev *pdev, void *data)
-{
- pdev->protocol_data = data;
-}
-
-struct pnp_fixup {
- char id[7];
- void (*quirk_function)(struct pnp_dev *dev); /* fixup function */
-};
-
-/* capabilities */
-#define PNP_READ 0x0001
-#define PNP_WRITE 0x0002
-#define PNP_DISABLE 0x0004
-#define PNP_CONFIGURABLE 0x0008
-#define PNP_REMOVABLE 0x0010
-
-#define pnp_can_read(dev) (((dev)->protocol) && ((dev)->protocol->get) && \
- ((dev)->capabilities & PNP_READ))
-#define pnp_can_write(dev) (((dev)->protocol) && ((dev)->protocol->set) && \
- ((dev)->capabilities & PNP_WRITE))
-#define pnp_can_disable(dev) (((dev)->protocol) && ((dev)->protocol->disable) && \
- ((dev)->capabilities & PNP_DISABLE))
-#define pnp_can_configure(dev) ((!(dev)->active) && ((dev)->capabilities & PNP_CONFIGURABLE))
-
-/* status */
-#define PNP_INIT 0x0000
-#define PNP_READY 0x0001
-#define PNP_ATTACHED 0x0002
-#define PNP_BUSY 0x0004
-#define PNP_FAULTY 0x0008
+struct pnp_dev;
+struct pnp_card;


/*
@@ -180,21 +30,21 @@
*/

struct pnp_id {
- char id[7];
+ char id[PNP_ID_LEN];
struct pnp_id * next;
};

struct pnp_device_id {
- char id[7];
+ char id[PNP_ID_LEN];
unsigned long driver_data; /* data private to the driver */
};

-struct pnp_card_device_id {
- char id[7];
+struct pnp_card_id {
+ char id[PNP_ID_LEN];
unsigned long driver_data; /* data private to the driver */
struct {
- char id[7];
- } devs[MAX_DEVICES]; /* logical devices */
+ char id[PNP_ID_LEN];
+ } devs[PNP_MAX_DEVICES]; /* logical devices */
};

#define PNP_DRIVER_DO_NOT_ACTIVATE (1<<0)
@@ -216,9 +66,9 @@
struct pnpc_driver {
struct list_head node;
char *name;
- const struct pnp_card_device_id *id_table;
+ const struct pnp_card_id *id_table;
unsigned int flags;
- int (*probe) (struct pnp_card *card, const struct pnp_card_device_id *card_id);
+ int (*probe) (struct pnp_card *card, const struct pnp_card_id *card_id);
void (*remove) (struct pnp_card *card);
struct device_driver driver;
};
@@ -230,14 +80,11 @@
* Resource Management
*/

-#define pnp_flags_valid(resrc) (((resrc)->flags & IORESOURCE_UNSET) == 0 && \
- ((resrc)->flags & (IORESOURCE_IO|IORESOURCE_MEM|IORESOURCE_IRQ|IORESOURCE_DMA)) != 0)
-
/* Use these instead of directly reading pnp_dev to get resource information */
-#define pnp_port_start(dev,bar) ((dev)->io_resource[(bar)].start)
-#define pnp_port_end(dev,bar) ((dev)->io_resource[(bar)].end)
-#define pnp_port_flags(dev,bar) ((dev)->io_resource[(bar)].flags)
-#define pnp_port_valid(dev,bar) pnp_flags_valid(&(dev)->io_resource[(bar)])
+#define pnp_port_start(dev,bar) ((dev)->res.port_resource[(bar)].start)
+#define pnp_port_end(dev,bar) ((dev)->res.port_resource[(bar)].end)
+#define pnp_port_flags(dev,bar) ((dev)->res.port_resource[(bar)].flags)
+#define pnp_port_valid(dev,bar) (pnp_port_flags((dev),(bar)) & IORESOURCE_IO)
#define pnp_port_len(dev,bar) \
((pnp_port_start((dev),(bar)) == 0 && \
pnp_port_end((dev),(bar)) == \
@@ -246,10 +93,10 @@
(pnp_port_end((dev),(bar)) - \
pnp_port_start((dev),(bar)) + 1))

-#define pnp_mem_start(dev,bar) ((dev)->mem_resource[(bar)].start)
-#define pnp_mem_end(dev,bar) ((dev)->mem_resource[(bar)].end)
-#define pnp_mem_flags(dev,bar) ((dev)->mem_resource[(bar)].flags)
-#define pnp_mem_valid(dev,bar) pnp_flags_valid(&(dev)->mem_resource[(bar)])
+#define pnp_mem_start(dev,bar) ((dev)->res.mem_resource[(bar)].start)
+#define pnp_mem_end(dev,bar) ((dev)->res.mem_resource[(bar)].end)
+#define pnp_mem_flags(dev,bar) ((dev)->res.mem_resource[(bar)].flags)
+#define pnp_mem_valid(dev,bar) (pnp_mem_flags((dev),(bar)) & IORESOURCE_MEM)
#define pnp_mem_len(dev,bar) \
((pnp_mem_start((dev),(bar)) == 0 && \
pnp_mem_end((dev),(bar)) == \
@@ -258,13 +105,13 @@
(pnp_mem_end((dev),(bar)) - \
pnp_mem_start((dev),(bar)) + 1))

-#define pnp_irq(dev,bar) ((dev)->irq_resource[(bar)].start)
-#define pnp_irq_flags(dev,bar) ((dev)->irq_resource[(bar)].flags)
-#define pnp_irq_valid(dev,bar) pnp_flags_valid(&(dev)->irq_resource[(bar)])
-
-#define pnp_dma(dev,bar) ((dev)->dma_resource[(bar)].start)
-#define pnp_dma_flags(dev,bar) ((dev)->dma_resource[(bar)].flags)
-#define pnp_dma_valid(dev,bar) pnp_flags_valid(&(dev)->dma_resource[(bar)])
+#define pnp_irq(dev,bar) ((dev)->res.irq_resource[(bar)].start)
+#define pnp_irq_flags(dev,bar) ((dev)->res.irq_resource[(bar)].flags)
+#define pnp_irq_valid(dev,bar) (pnp_irq_flags((dev),(bar)) & IORESOURCE_IRQ)
+
+#define pnp_dma(dev,bar) ((dev)->res.dma_resource[(bar)].start)
+#define pnp_dma_flags(dev,bar) ((dev)->res.dma_resource[(bar)].flags)
+#define pnp_dma_valid(dev,bar) (pnp_dma_flags((dev),(bar)) & IORESOURCE_DMA)

#define PNP_PORT_FLAG_16BITADDR (1<<0)
#define PNP_PORT_FLAG_FIXED (1<<1)
@@ -276,7 +123,6 @@
unsigned char size; /* size of range */
unsigned char flags; /* port flags */
unsigned char pad; /* pad */
- struct pnp_resources *res; /* parent */
struct pnp_port *next; /* next port */
};

@@ -284,14 +130,12 @@
unsigned short map; /* bitmaks for IRQ lines */
unsigned char flags; /* IRQ flags */
unsigned char pad; /* pad */
- struct pnp_resources *res; /* parent */
struct pnp_irq *next; /* next IRQ */
};

struct pnp_dma {
unsigned char map; /* bitmask for DMA channels */
unsigned char flags; /* DMA flags */
- struct pnp_resources *res; /* parent */
struct pnp_dma *next; /* next port */
};

@@ -302,13 +146,11 @@
unsigned int size; /* size of range */
unsigned char flags; /* memory flags */
unsigned char pad; /* pad */
- struct pnp_resources *res; /* parent */
struct pnp_mem *next; /* next memory resource */
};

struct pnp_mem32 {
unsigned char data[17];
- struct pnp_resources *res; /* parent */
struct pnp_mem32 *next; /* next 32-bit memory resource */
};

@@ -329,33 +171,176 @@
struct pnp_resources *dep; /* dependent resources */
};

-struct pnp_res_cfg {
- struct resource io_resource[DEVICE_COUNT_IO]; /* I/O ports */
- struct resource mem_resource[DEVICE_COUNT_MEM]; /* memory regions + expansion ROMs */
- struct resource dma_resource[DEVICE_COUNT_DMA];
- struct resource irq_resource[DEVICE_COUNT_IRQ];
+struct pnp_rule_table {
+ int depnum;
+ struct pnp_port *port[PNP_MAX_PORT];
+ struct pnp_irq *irq[PNP_MAX_IRQ];
+ struct pnp_dma *dma[PNP_MAX_DMA];
+ struct pnp_mem *mem[PNP_MAX_MEM];
+};
+
+struct pnp_resource_table {
+ struct resource port_resource[PNP_MAX_PORT];
+ struct resource mem_resource[PNP_MAX_MEM];
+ struct resource dma_resource[PNP_MAX_DMA];
+ struct resource irq_resource[PNP_MAX_IRQ];
+};
+
+
+/*
+ * Device Managemnt
+ */
+
+struct pnp_card {
+ struct device dev; /* Driver Model device interface */
+ unsigned char number; /* used as an index, must be unique */
+ struct list_head global_list; /* node in global list of cards */
+ struct list_head protocol_list; /* node in protocol's list of cards */
+ struct list_head devices; /* devices attached to the card */
+ struct list_head rdevs; /* a list of devices requested by the card driver */
+ int status;
+
+ struct pnp_protocol * protocol;
+ struct pnpc_driver * driver;
+ struct pnp_id * id; /* contains supported EISA IDs*/
+
+ void * protocol_data; /* Used to store protocol specific data */
+ unsigned char pnpver; /* Plug & Play version */
+ unsigned char productver; /* product version */
+ unsigned int serial; /* serial number */
+ unsigned char checksum; /* if zero - checksum passed */
+ struct proc_dir_entry *procdir; /* directory entry in /proc/bus/isapnp */
+};
+
+#define global_to_pnp_card(n) list_entry(n, struct pnp_card, global_list)
+#define protocol_to_pnp_card(n) list_entry(n, struct pnp_card, protocol_list)
+#define to_pnp_card(n) container_of(n, struct pnp_card, dev)
+#define pnp_for_each_card(card) \
+ for((card) = global_to_pnp_card(pnp_cards.next); \
+ (card) != global_to_pnp_card(&pnp_cards); \
+ (card) = global_to_pnp_card((card)->global_list.next))
+
+static inline void *pnpc_get_drvdata (struct pnp_card *pcard)
+{
+ return dev_get_drvdata(&pcard->dev);
+}
+
+static inline void pnpc_set_drvdata (struct pnp_card *pcard, void *data)
+{
+ dev_set_drvdata(&pcard->dev, data);
+}
+
+static inline void *pnpc_get_protodata (struct pnp_card *pcard)
+{
+ return pcard->protocol_data;
+}
+
+static inline void pnpc_set_protodata (struct pnp_card *pcard, void *data)
+{
+ pcard->protocol_data = data;
+}
+
+struct pnp_dev {
+ struct device dev; /* Driver Model device interface */
+ unsigned char number; /* used as an index, must be unique */
+ int active;
+ int capabilities;
+ int status;
+
+ struct list_head global_list; /* node in global list of devices */
+ struct list_head protocol_list; /* node in list of device's protocol */
+ struct list_head card_list; /* node in card's list of devices */
+ struct list_head rdev_list; /* node in cards list of requested devices */
+
+ struct pnp_protocol * protocol;
+ struct pnp_card * card; /* card the device is attached to, none if NULL */
+ struct pnp_driver * driver;
+
+ struct pnp_id * id; /* supported EISA IDs*/
+ struct pnp_resource_table res; /* contains the currently chosen resources */
+ struct pnp_resources * possible; /* a list of possible resources */
+ struct pnp_rule_table * rule; /* the current possible resource set */
+ int lock_resources; /* resources are locked */
+
+ void * protocol_data; /* Used to store protocol specific data */
+ unsigned short regs; /* ISAPnP: supported registers */
+ int flags; /* used by protocols */
+ struct proc_dir_entry *procent; /* device entry in /proc/bus/isapnp */
};

-struct pnp_cfg {
- struct pnp_port *port[8];
- struct pnp_irq *irq[2];
- struct pnp_dma *dma[2];
- struct pnp_mem *mem[4];
- struct pnp_res_cfg request;
+#define global_to_pnp_dev(n) list_entry(n, struct pnp_dev, global_list)
+#define card_to_pnp_dev(n) list_entry(n, struct pnp_dev, card_list)
+#define protocol_to_pnp_dev(n) list_entry(n, struct pnp_dev, protocol_list)
+#define to_pnp_dev(n) container_of(n, struct pnp_dev, dev)
+#define pnp_for_each_dev(dev) \
+ for((dev) = global_to_pnp_dev(pnp_global.next); \
+ (dev) != global_to_pnp_dev(&pnp_global); \
+ (dev) = global_to_pnp_dev((dev)->global_list.next))
+#define card_for_each_dev(card,dev) \
+ for((dev) = card_to_pnp_dev((card)->devices.next); \
+ (dev) != card_to_pnp_dev(&(card)->devices); \
+ (dev) = card_to_pnp_dev((dev)->card_list.next))
+#define pnp_dev_name(dev) (dev)->dev.name
+
+static inline void *pnp_get_drvdata (struct pnp_dev *pdev)
+{
+ return dev_get_drvdata(&pdev->dev);
+}
+
+static inline void pnp_set_drvdata (struct pnp_dev *pdev, void *data)
+{
+ dev_set_drvdata(&pdev->dev, data);
+}
+
+static inline void *pnp_get_protodata (struct pnp_dev *pdev)
+{
+ return pdev->protocol_data;
+}
+
+static inline void pnp_set_protodata (struct pnp_dev *pdev, void *data)
+{
+ pdev->protocol_data = data;
+}
+
+struct pnp_fixup {
+ char id[7];
+ void (*quirk_function)(struct pnp_dev *dev); /* fixup function */
};

+/* capabilities */
+#define PNP_READ 0x0001
+#define PNP_WRITE 0x0002
+#define PNP_DISABLE 0x0004
+#define PNP_CONFIGURABLE 0x0008
+#define PNP_REMOVABLE 0x0010
+
+#define pnp_can_read(dev) (((dev)->protocol) && ((dev)->protocol->get) && \
+ ((dev)->capabilities & PNP_READ))
+#define pnp_can_write(dev) (((dev)->protocol) && ((dev)->protocol->set) && \
+ ((dev)->capabilities & PNP_WRITE))
+#define pnp_can_disable(dev) (((dev)->protocol) && ((dev)->protocol->disable) && \
+ ((dev)->capabilities & PNP_DISABLE))
+#define pnp_can_configure(dev) ((!(dev)->active) && (!(dev)->lock_resources) && \
+ ((dev)->capabilities & PNP_CONFIGURABLE))

-/*
+/* status */
+#define PNP_READY 0x0000
+#define PNP_ATTACHED 0x0001
+#define PNP_BUSY 0x0002
+#define PNP_FAULTY 0x0004
+
+
+/*
* Protocol Management
*/

struct pnp_protocol {
struct list_head protocol_list;
- char name[DEVICE_NAME_SIZE];
+ char * name;

- /* functions */
+ /* resource control functions */
int (*get)(struct pnp_dev *dev);
- int (*set)(struct pnp_dev *dev, struct pnp_cfg *config);
+ int (*set)(struct pnp_dev *dev);
int (*disable)(struct pnp_dev *dev);

/* used by pnp layer only (look but don't touch) */
@@ -384,6 +369,8 @@
int pnp_add_device(struct pnp_dev *dev);
void pnp_remove_device(struct pnp_dev *dev);
extern struct list_head pnp_global;
+int pnp_device_attach(struct pnp_dev *pnp_dev);
+void pnp_device_detach(struct pnp_dev *pnp_dev);

/* resource */
struct pnp_resources * pnp_build_resource(struct pnp_dev *dev, int dependent);
@@ -394,10 +381,9 @@
int pnp_add_port_resource(struct pnp_dev *dev, int depnum, struct pnp_port *data);
int pnp_add_mem_resource(struct pnp_dev *dev, int depnum, struct pnp_mem *data);
int pnp_add_mem32_resource(struct pnp_dev *dev, int depnum, struct pnp_mem32 *data);
-int pnp_init_res_cfg(struct pnp_res_cfg *template);
-int pnp_activate_dev(struct pnp_dev *dev, struct pnp_res_cfg *template);
+void pnp_init_resource_table(struct pnp_resource_table *table);
+int pnp_activate_dev(struct pnp_dev *dev);
int pnp_disable_dev(struct pnp_dev *dev);
-int pnp_raw_set_dev(struct pnp_dev *dev, int depnum, struct pnp_res_cfg *template);
void pnp_resource_change(struct resource *resource, unsigned long start, unsigned long size);

/* driver */
@@ -405,17 +391,21 @@
int pnp_add_id(struct pnp_id *id, struct pnp_dev *dev);
int pnp_register_driver(struct pnp_driver *drv);
void pnp_unregister_driver(struct pnp_driver *drv);
-int pnp_device_attach(struct pnp_dev *pnp_dev);
-void pnp_device_detach(struct pnp_dev *pnp_dev);

#else

/* just in case anyone decides to call these without PnP Support Enabled */
+
+/* core */
static inline int pnp_register_protocol(struct pnp_protocol *protocol) { return -ENODEV; }
static inline void pnp_unregister_protocol(struct pnp_protocol *protocol) { }
static inline int pnp_init_device(struct pnp_dev *dev) { return -ENODEV; }
static inline int pnp_add_device(struct pnp_dev *dev) { return -ENODEV; }
static inline void pnp_remove_device(struct pnp_dev *dev) { }
+static inline int pnp_device_attach(struct pnp_dev *pnp_dev) { return -ENODEV; }
+static inline void pnp_device_detach(struct pnp_dev *pnp_dev) { ; }
+
+/* resource */
static inline struct pnp_resources * pnp_build_resource(struct pnp_dev *dev, int dependent) { return NULL; }
static inline struct pnp_resources * pnp_find_resources(struct pnp_dev *dev, int depnum) { return NULL; }
static inline int pnp_get_max_depnum(struct pnp_dev *dev) { return -ENODEV; }
@@ -424,19 +414,17 @@
static inline int pnp_add_port_resource(struct pnp_dev *dev, int depnum, struct pnp_irq *data) { return -ENODEV; }
static inline int pnp_add_mem_resource(struct pnp_dev *dev, int depnum, struct pnp_irq *data) { return -ENODEV; }
static inline int pnp_add_mem32_resource(struct pnp_dev *dev, int depnum, struct pnp_irq *data) { return -ENODEV; }
-static inline int pnp_init_res_cfg(struct pnp_res_cfg *template) { return -ENODEV; }
-static inline int pnp_activate_dev(struct pnp_dev *dev, struct pnp_res_cfg *template) { return -ENODEV; }
+static void pnp_init_resource_table(struct pnp_resource_table *table) { ; }
+static inline int pnp_activate_dev(struct pnp_dev *dev) { return -ENODEV; }
static inline int pnp_disable_dev(struct pnp_dev *dev) { return -ENODEV; }
-static inline int pnp_raw_set_dev(struct pnp_dev *dev, int depnum, struct pnp_res_cfg *template) { return -ENODEV; }
static inline void pnp_resource_change(struct resource *resource, unsigned long start, unsigned long size) { ; }
+
+/* driver */
static inline int compare_pnp_id(struct list_head * id_list, const char * id) { return -ENODEV; }
static inline int pnp_add_id(struct pnp_id *id, struct pnp_dev *dev) { return -ENODEV; }
static inline int pnp_register_driver(struct pnp_driver *drv) { return -ENODEV; }
static inline void pnp_unregister_driver(struct pnp_driver *drv) { ; }

-static inline int pnp_device_attach(struct pnp_dev *pnp_dev) { return -ENODEV; }
-static inline void pnp_device_detach(struct pnp_dev *pnp_dev) { ; }
-
#endif /* CONFIG_PNP */


@@ -458,6 +446,7 @@

#else

+/* card */
static inline int pnpc_add_card(struct pnp_card *card) { return -ENODEV; }
static inline void pnpc_remove_card(struct pnp_card *card) { ; }
static inline int pnpc_add_device(struct pnp_card *card, struct pnp_dev *dev) { return -ENODEV; }
@@ -467,11 +456,12 @@
static inline int pnpc_register_driver(struct pnpc_driver *drv) { return -ENODEV; }
static inline void pnpc_unregister_driver(struct pnpc_driver *drv) { ; }
static inline int pnpc_add_id(struct pnp_id *id, struct pnp_card *card) { return -ENODEV; }
-static inline int pnpc_attach(struct pnp_card *card) { return -ENODEV; }
-static inline void pnpc_detach(struct pnp_card *card) { ; }

#endif /* CONFIG_PNP_CARD */

+#define pnp_err(format, arg...) printk(KERN_ERR "pnp: " format "\n" , ## arg)
+#define pnp_info(format, arg...) printk(KERN_INFO "pnp: " format "\n" , ## arg)
+#define pnp_warn(format, arg...) printk(KERN_WARNING "pnp: " format "\n" , ## arg)

#ifdef DEBUG
#define pnp_dbg(format, arg...) printk(KERN_DEBUG "pnp: " format "\n" , ## arg)


2003-02-03 09:24:27

by Jaroslav Kysela

[permalink] [raw]
Subject: Re: [PATCH][RFC] Resource Management Improvements (1/4)

On Sun, 2 Feb 2003, Adam Belay wrote:

> @@ -825,10 +825,10 @@
>
> } /* while */
> end:
> - if (pnp_port_valid(dev, 0) == 0 &&
> - pnp_mem_valid(dev, 0) == 0 &&
> - pnp_irq_valid(dev, 0) == 0 &&
> - pnp_dma_valid(dev, 0) == 0)
> + if (pnp_port_start(dev, 0) == 0 &&
> + pnp_mem_start(dev, 0) == 0 &&
> + pnp_irq(dev, 0) == -1 &&
> + pnp_dma(dev, 0) == -1)
> dev->active = 0;
> else
> dev->active = 1;

Again, why this?

Jaroslav

-----
Jaroslav Kysela <[email protected]>
Linux Kernel Sound Maintainer
ALSA Project, SuSE Labs

2003-02-03 09:35:03

by Jaroslav Kysela

[permalink] [raw]
Subject: Re: [PATCH][RFC] Resource Management Improvements (1/4)

On Sun, 2 Feb 2003, Adam Belay wrote:

> This patch is the first of 4 experimental pnp patches. It is against a clean
> 2.5.59 kernel. I would appreciate for those who are interested, if you would
> test this patch series and post your results on lkml. These changes appear
> to be very stable on my test systems and I feel they will be a dramatic
> improvement to the pnp layer.
>
> Patch 1 contains resource management improvements that will allow the pnp
> layer to resolve virtually any resource conflict. A new kernel parameter
> "pnp_max_moves=" was added to allow the user to describe the maximum number
> of device levels to move during conflict resolution. This powerful new
> engine also includes many cleanups and more verbose reporting. All devices
> are configured when added instead of when activated. This will increase
> the odds that the conflict will be possible to resolve because resources
> cannot be moved in an active device.

I don't agree with this. Simply, why to activate devices which will not be
ever used? You'll occupy resources which can be assigned to another
device. Also, you simply removed my configuration templates, so the driver
cannot tell explicitly which resources PnP layer should assign. Also
activation (and resource allocation) should be on request allowing driver
to do things manually. Bad bad bad. We're again in state a month ago when
nothing worked and ALSA ISA PnP drivers are still broken, because your
model doesn't have all features like good old ISA PnP code. Also, with all
respects, I think that you're trying to write very clever code. It's also
bad. It will end with numerous problems and with undeterministic behaviour
like unnamed commercial OS. Let users to pass their own configurations.

Jaroslav

-----
Jaroslav Kysela <[email protected]>
Linux Kernel Sound Maintainer
ALSA Project, SuSE Labs

2003-02-03 13:46:39

by Jaroslav Kysela

[permalink] [raw]
Subject: PnP model

Hi all,

I think that we need to discuss deeply the right PnP model. The
actual changes proposed by Adam are going to be more and more complex
without allowing the user interactions inside the "auto" steps. The
auto-configuration might be good and bad as we all know, but having an
method to skip it is necessary.

I strongly vote to follow the same behaviour as PCI code does:
It means call the activation / enabling / setting functions from the
probe() callbacks. Only the driver knows what's the best. Including
the manual assignment of resources.

Jaroslav

-----
Jaroslav Kysela <[email protected]>
Linux Kernel Sound Maintainer
ALSA Project, SuSE Labs

2003-02-03 14:39:26

by Alan

[permalink] [raw]
Subject: Re: PnP model

On Mon, 2003-02-03 at 13:55, Jaroslav Kysela wrote:
> I strongly vote to follow the same behaviour as PCI code does:
> It means call the activation / enabling / setting functions from the
> probe() callbacks. Only the driver knows what's the best. Including
> the manual assignment of resources.

I agree. A lot of drivers should be able to use one model for everything
including "enable_device" stuff. Right now its all a bit too detailed.

Also the locking model seems very unclear and there are hot swappable ISA
bays

2003-02-04 01:33:24

by Adam Belay

[permalink] [raw]
Subject: Re: PnP model

On Mon, Feb 03, 2003 at 02:55:37PM +0100, Jaroslav Kysela wrote:
> Hi all,
>
> I think that we need to discuss deeply the right PnP model. The

I'm confident this is the right pnp model.

> actual changes proposed by Adam are going to be more and more complex
> without allowing the user interactions inside the "auto" steps. The
> auto-configuration might be good and bad as we all know, but having an
> method to skip it is necessary.

In many cases, Auto configuration can be better then manual configuration.
1.) The auto configuration engine in my patch is able to resolve almost any
resource conflict and provides the greatest chance for all devices to have
resources allocated.
2.) Certainly some driver developers would like to manually set resources
but many may prefer the option to auto config.
3.) Drivers under the existing system are not aware of many forms of
resource conflicts and could set resources incorrectly.
4.) Some users do not want to worry about manual configuration and would
welcome an auto configuration system that makes intelligent choices
without user input. This autoconfiguration system monitors many variables
that a user would have a hard time keeping track of and never overlooks any
potential conflicts in its analysis.

The above stated reasons are why I introduced these auto configuration
(Resource Management) improvements.

I feel that one solution is to support both manual and auto configuration so
the user can use what he or she prefers, however, I am confident that in most
cases the auto configuration will be the best option.

Perhaps I didn't make this clear earlier, it was not my intent to remove
support for manual resource configurations. This was only temporary and
intended for the first release. Here is a patch against my other 4
patches that updates the pnp code to support manual resource
configurations. It also merges the pnpbios GPF fix and addresses a few
other issues.

-Adam

P.S.: The new manual config API is pnp_manual_config_dev.

P.S.: I'm currently working on an improved user interface that will allow
the user to manually set resources through the sysfs interface. It will
include reports on where the conflicts are in order to assist the user in
making decisions.

P.S.: I intend to resolve all of these issues but my top priority is to
convert pnp drivers. For example, I recently submitted pnp driver
conversions and am currently working on pnp card service revisions.



This patch needs to be tested. I appreciate any and all feedback.

diff -urN a/drivers/pnp/base.h b/drivers/pnp/base.h
--- a/drivers/pnp/base.h Sun Feb 2 11:40:10 2003
+++ b/drivers/pnp/base.h Mon Feb 3 18:11:16 2003
@@ -4,7 +4,6 @@
extern int pnp_interface_attach_device(struct pnp_dev *dev);
extern void pnp_name_device(struct pnp_dev *dev);
extern void pnp_fixup_device(struct pnp_dev *dev);
-extern int pnp_configure_device(struct pnp_dev *dev);
extern void pnp_free_resources(struct pnp_resources *resources);
extern int __pnp_add_device(struct pnp_dev *dev);
extern void __pnp_remove_device(struct pnp_dev *dev);
diff -urN a/drivers/pnp/core.c b/drivers/pnp/core.c
--- a/drivers/pnp/core.c Sun Feb 2 11:40:10 2003
+++ b/drivers/pnp/core.c Mon Feb 3 18:06:33 2003
@@ -122,7 +122,7 @@
list_add_tail(&dev->global_list, &pnp_global);
list_add_tail(&dev->protocol_list, &dev->protocol->devices);
spin_unlock(&pnp_lock);
- pnp_configure_device(dev);
+ pnp_auto_config_dev(dev);
ret = device_register(&dev->dev);
if (ret == 0)
pnp_interface_attach_device(dev);
diff -urN a/drivers/pnp/interface.c b/drivers/pnp/interface.c
--- a/drivers/pnp/interface.c Sun Feb 2 12:26:25 2003
+++ b/drivers/pnp/interface.c Mon Feb 3 17:37:32 2003
@@ -232,10 +232,11 @@
char *str = buf;
int i;

- if (!dev->active){
- str += sprintf(str,"DISABLED\n");
- goto done;
- }
+ str += sprintf(str,"state = ");
+ if (dev->active)
+ str += sprintf(str,"active\n");
+ else
+ str += sprintf(str,"disabled\n");
for (i = 0; i < PNP_MAX_PORT; i++) {
if (pnp_port_valid(dev, i)) {
str += sprintf(str,"io");
@@ -280,22 +281,6 @@
num_args = sscanf(buf,"%10s %i",command,&depnum);
if (!num_args)
goto done;
- if (!strnicmp(command,"lock",4)) {
- if (dev->active) {
- dev->lock_resources = 1;
- } else {
- error = -EINVAL;
- }
- goto done;
- }
- if (!strnicmp(command,"unlock",6)) {
- if (dev->lock_resources) {
- dev->lock_resources = 0;
- } else {
- error = -EINVAL;
- }
- goto done;
- }
if (!strnicmp(command,"disable",7)) {
error = pnp_disable_dev(dev);
goto done;
diff -urN a/drivers/pnp/pnpbios/core.c b/drivers/pnp/pnpbios/core.c
--- a/drivers/pnp/pnpbios/core.c Sun Feb 2 19:25:15 2003
+++ b/drivers/pnp/pnpbios/core.c Mon Feb 3 15:43:06 2003
@@ -142,11 +142,13 @@
set_limit(cpu_gdt_table[cpu][(selname) >> 3], size); \
} while(0)

+static struct desc_struct bad_bios_desc = { 0, 0x00409200 };
+
/*
* At some point we want to use this stack frame pointer to unwind
- * after PnP BIOS oopses.
+ * after PnP BIOS oopses.
*/
-
+
u32 pnp_bios_fault_esp;
u32 pnp_bios_fault_eip;
u32 pnp_bios_is_utter_crap = 0;
@@ -160,6 +162,8 @@
{
unsigned long flags;
u16 status;
+ struct desc_struct save_desc_40;
+ int cpu;

/*
* PnP BIOSes are generally not terribly re-entrant.
@@ -168,6 +172,10 @@
if(pnp_bios_is_utter_crap)
return PNP_FUNCTION_NOT_SUPPORTED;

+ cpu = get_cpu();
+ save_desc_40 = cpu_gdt_table[cpu][0x40 / 8];
+ cpu_gdt_table[cpu][0x40 / 8] = bad_bios_desc;
+
/* On some boxes IRQ's during PnP BIOS calls are deadly. */
spin_lock_irqsave(&pnp_bios_lock, flags);

@@ -207,6 +215,9 @@
: "memory"
);
spin_unlock_irqrestore(&pnp_bios_lock, flags);
+
+ cpu_gdt_table[cpu][0x40 / 8] = save_desc_40;
+ put_cpu();

/* If we get here and this is set then the PnP BIOS faulted on us. */
if(pnp_bios_is_utter_crap)
@@ -236,7 +247,8 @@
void *p = kmalloc( size, f );
if ( p == NULL )
printk(KERN_ERR "PnPBIOS: kmalloc() failed\n");
- memset(p, 0, size);
+ else
+ memset(p, 0, size);
return p;
}

@@ -886,14 +898,7 @@

for(nodenum=0; nodenum<0xff; ) {
u8 thisnodenum = nodenum;
- /* We build the list from the "boot" config because
- * we know that the resources couldn't have changed
- * at this stage. Furthermore some buggy PnP BIOSes
- * will crash if we request the "current" config
- * from devices that are can only be static such as
- * those controlled by the "system" driver.
- */
- if (pnp_bios_get_dev_node(&nodenum, (char )1, node))
+ if (pnp_bios_get_dev_node(&nodenum, (char )0, node))
break;
nodes_got++;
dev = pnpbios_kmalloc(sizeof (struct pnp_dev), GFP_KERNEL);
@@ -996,6 +1001,8 @@
pnp_bios_callpoint.segment = PNP_CS16;
pnp_bios_hdr = check;

+ set_base(bad_bios_desc, __va((unsigned long)0x40 << 4));
+ _set_limit((char *)&bad_bios_desc, 4095 - (0x40 << 4));
for(i=0; i < NR_CPUS; i++)
{
Q2_SET_SEL(i, PNP_CS32, &pnp_bios_callfunc, 64 * 1024);
diff -urN a/drivers/pnp/resource.c b/drivers/pnp/resource.c
--- a/drivers/pnp/resource.c Sun Feb 2 18:43:34 2003
+++ b/drivers/pnp/resource.c Mon Feb 3 19:29:12 2003
@@ -989,7 +989,8 @@
{
struct pnp_change * change = pnp_add_change(parent,dev);
move--;
- spin_lock(&pnp_lock);
+ if (!dev->rule)
+ goto fail;
if (!pnp_can_configure(dev))
goto fail;
if (!dev->rule->depnum) {
@@ -1001,7 +1002,6 @@
goto fail;
pnp_init_resource_table(&dev->res);
}
- spin_unlock(&pnp_lock);
if (!parent) {
pnp_free_changes(change);
kfree(change);
@@ -1009,7 +1009,6 @@
return 1;

fail:
- spin_unlock(&pnp_lock);
if (!parent)
kfree(change);
return 0;
@@ -1027,19 +1026,25 @@
return -ENOMEM;
}
for (move = 1; move <= pnp_max_moves; move++) {
+ spin_lock(&pnp_lock);
dev->rule->depnum = 0;
pnp_init_resource_table(&dev->res);
- if (pnp_next_config(dev,move,NULL))
+ if (pnp_next_config(dev,move,NULL)) {
+ spin_unlock(&pnp_lock);
return 1;
+ }
+ spin_unlock(&pnp_lock);
}

+ spin_lock(&pnp_lock);
pnp_init_resource_table(&dev->res);
dev->rule->depnum = 0;
- pnp_err("res: Unable to resolve resource conflicts for the device '%s', this device will not be usable.", dev->dev.bus_id);
+ spin_unlock(&pnp_lock);
+ pnp_err("res: Unable to resolve resource conflicts for the device '%s', some devices may not be usable.", dev->dev.bus_id);
return 0;
}

-static int pnp_process_active(struct pnp_dev *dev)
+static int pnp_resolve_conflicts(struct pnp_dev *dev)
{
int i;
struct pnp_dev * cdev;
@@ -1079,24 +1084,6 @@
return 1;
}

-/**
- * pnp_configure_device - determines the best possible resource configuration based on available information
- * @dev: pointer to the desired device
- */
-
-int pnp_configure_device(struct pnp_dev *dev)
-{
- int error;
- if(!dev)
- return -EINVAL;
- if(dev->active) {
- error = pnp_process_active(dev);
- } else {
- error = pnp_generate_config(dev);
- }
- return error;
-}
-
static int pnp_compare_resources(struct pnp_resource_table * resa, struct pnp_resource_table * resb)
{
int idx;
@@ -1128,6 +1115,92 @@
* PnP Device Resource Management
*/

+
+/**
+ * pnp_resource_change - change one resource
+ * @resource: pointer to resource to be changed
+ * @start: start of region
+ * @size: size of region
+ *
+ */
+
+void pnp_resource_change(struct resource *resource, unsigned long start, unsigned long size)
+{
+ if (resource == NULL)
+ return;
+ resource->flags &= ~IORESOURCE_AUTO;
+ resource->start = start;
+ resource->end = start + size - 1;
+}
+
+/**
+ * pnp_auto_config_dev - determines the best possible resource configuration based on available information
+ * @dev: pointer to the desired device
+ */
+
+int pnp_auto_config_dev(struct pnp_dev *dev)
+{
+ int error;
+ if(!dev)
+ return -EINVAL;
+
+ dev->config_mode = PNP_CONFIG_AUTO;
+
+ if(dev->active)
+ error = pnp_resolve_conflicts(dev);
+ else
+ error = pnp_generate_config(dev);
+ return error;
+}
+
+/**
+ * pnp_manual_config_dev - Disables Auto Config and Manually sets the resource table
+ * @dev: pointer to the desired device
+ * @res: pointer to the new resource config
+ */
+
+int pnp_manual_config_dev(struct pnp_dev *dev, struct pnp_resource_table * res, int mode)
+{
+ int i;
+ struct pnp_resource_table bak = dev->res;
+ if (!dev || !res)
+ return -EINVAL;
+ if (dev->active)
+ return -EBUSY;
+ spin_lock(&pnp_lock);
+ dev->res = *res;
+
+ if (!(mode & PNP_CONFIG_FORCE)) {
+ for (i = 0; i < PNP_MAX_PORT; i++) {
+ if(pnp_check_port(dev,i))
+ goto fail;
+ }
+ for (i = 0; i < PNP_MAX_MEM; i++) {
+ if(pnp_check_mem(dev,i))
+ goto fail;
+ }
+ for (i = 0; i < PNP_MAX_IRQ; i++) {
+ if(pnp_check_irq(dev,i))
+ goto fail;
+ }
+ for (i = 0; i < PNP_MAX_DMA; i++) {
+ if(pnp_check_dma(dev,i))
+ goto fail;
+ }
+ }
+ spin_unlock(&pnp_lock);
+
+ if (mode & PNP_CONFIG_RESOLVE)
+ pnp_resolve_conflicts(dev);
+
+ dev->config_mode = PNP_CONFIG_MANUAL;
+
+fail:
+ dev->res = bak;
+ spin_unlock(&pnp_lock);
+ return -EINVAL;
+}
+
/**
* pnp_activate_dev - activates a PnP device for use
* @dev: pointer to the desired device
@@ -1141,11 +1214,27 @@
return -EINVAL;
if (dev->active) {
pnp_info("res: The PnP device '%s' is already active.", dev->dev.bus_id);
+ return -EBUSY;
+ }
+ /* if the auto config failed, try again now, because this device was requested before others it's best to find a config at all costs */
+ if (!pnp_is_active(dev)) {
+ spin_lock(&pnp_lock);
+ if (!pnp_next_config(dev,1,NULL)) {
+ pnp_init_resource_table(&dev->res);
+ dev->rule->depnum = 0;
+ pnp_err("res: Unable to resolve resource conflicts for the device '%s'", dev->dev.bus_id);
+ spin_unlock(&pnp_lock);
+ return -EBUSY;
+ }
+ spin_unlock(&pnp_lock);
+ }
+ if (dev->config_mode & PNP_CONFIG_INVALID) {
+ pnp_info("res: Unable to activate the PnP device '%s' because its resource configuration is invalid", dev->dev.bus_id);
return -EINVAL;
}
if (dev->status != PNP_READY && dev->status != PNP_ATTACHED){
pnp_err("res: Activation failed because the PnP device '%s' is busy", dev->dev.bus_id);
- return -EINVAL;
+ return -EBUSY;
}
if (!pnp_can_write(dev)) {
pnp_info("res: Unable to activate the PnP device '%s' because this feature is not supported", dev->dev.bus_id);
@@ -1165,7 +1254,10 @@
} else
dev->active = pnp_is_active(dev);
pnp_dbg("res: the device '%s' has been activated", dev->dev.bus_id);
- kfree(dev->rule);
+ if (dev->rule) {
+ kfree(dev->rule);
+ dev->rule = NULL;
+ }
return 0;
}

@@ -1200,22 +1292,6 @@
return 0;
}

-/**
- * pnp_resource_change - change one resource
- * @resource: pointer to resource to be changed
- * @start: start of region
- * @size: size of region
- *
- */
-
-void pnp_resource_change(struct resource *resource, unsigned long start, unsigned long size)
-{
- if (resource == NULL)
- return;
- resource->flags &= ~IORESOURCE_AUTO;
- resource->start = start;
- resource->end = start + size - 1;
-}

EXPORT_SYMBOL(pnp_build_resource);
EXPORT_SYMBOL(pnp_find_resources);
diff -urN a/drivers/pnp/support.c b/drivers/pnp/support.c
--- a/drivers/pnp/support.c Sun Feb 2 18:43:34 2003
+++ b/drivers/pnp/support.c Mon Feb 3 15:49:22 2003
@@ -168,7 +168,7 @@
break;
}
} /* switch */
- p += len + 3;
+ p += len + 3;
continue;
} /* end large tag */

@@ -410,8 +410,8 @@
possible_dma(p,len,depnum,dev);
break;
}
- case SMALL_TAG_STARTDEP:
- {
+ case SMALL_TAG_STARTDEP:
+ {
if (len > 1)
goto sm_err;
dependent = 0x100 | PNP_RES_PRIORITY_ACCEPTABLE;
@@ -419,15 +419,15 @@
dependent = 0x100 | p[1];
pnp_build_resource(dev,dependent);
depnum = pnp_get_max_depnum(dev);
- break;
- }
- case SMALL_TAG_ENDDEP:
- {
+ break;
+ }
+ case SMALL_TAG_ENDDEP:
+ {
if (len != 0)
goto sm_err;
depnum = 0;
- break;
- }
+ break;
+ }
case SMALL_TAG_PORT:
{
if (len != 7)
diff -urN a/include/linux/pnp.h b/include/linux/pnp.h
--- a/include/linux/pnp.h Sun Feb 2 18:43:34 2003
+++ b/include/linux/pnp.h Mon Feb 3 19:07:00 2003
@@ -253,7 +253,7 @@
struct pnp_resource_table res; /* contains the currently chosen resources */
struct pnp_resources * possible; /* a list of possible resources */
struct pnp_rule_table * rule; /* the current possible resource set */
- int lock_resources; /* resources are locked */
+ int config_mode; /* flags that determine how the device's resources should be configured */

void * protocol_data; /* Used to store protocol specific data */
unsigned short regs; /* ISAPnP: supported registers */
@@ -300,6 +300,13 @@
void (*quirk_function)(struct pnp_dev *dev); /* fixup function */
};

+/* config modes */
+#define PNP_CONFIG_AUTO 0x0001 /* Use the Resource Configuration Engine to determine resource settings */
+#define PNP_CONFIG_MANUAL 0x0002 /* the config has been manually specified */
+#define PNP_CONFIG_FORCE 0x0003 /* disables validity checking */
+#define PNP_CONFIG_RESOLVE 0x0008 /* moves other configs out of the way, use only when absolutely necessary */
+#define PNP_CONFIG_INVALID 0x0010 /* If this flag is set, the pnp layer will refuse to activate the device */
+
/* capabilities */
#define PNP_READ 0x0001
#define PNP_WRITE 0x0002
@@ -313,7 +320,7 @@
((dev)->capabilities & PNP_WRITE))
#define pnp_can_disable(dev) (((dev)->protocol) && ((dev)->protocol->disable) && \
((dev)->capabilities & PNP_DISABLE))
-#define pnp_can_configure(dev) ((!(dev)->active) && (!(dev)->lock_resources) && \
+#define pnp_can_configure(dev) ((!(dev)->active) && ((dev)->config_mode & PNP_CONFIG_AUTO) && \
((dev)->capabilities & PNP_CONFIGURABLE))

/* status */
@@ -377,6 +384,8 @@
int pnp_activate_dev(struct pnp_dev *dev);
int pnp_disable_dev(struct pnp_dev *dev);
void pnp_resource_change(struct resource *resource, unsigned long start, unsigned long size);
+int pnp_manual_config_dev(struct pnp_dev *dev, struct pnp_resource_table *res, int mode);
+int pnp_auto_config_dev(struct pnp_dev *dev);

/* driver */
int compare_pnp_id(struct pnp_id * pos, const char * id);
@@ -415,6 +424,8 @@
static inline int pnp_activate_dev(struct pnp_dev *dev) { return -ENODEV; }
static inline int pnp_disable_dev(struct pnp_dev *dev) { return -ENODEV; }
static inline void pnp_resource_change(struct resource *resource, unsigned long start, unsigned long size) { ; }
+static inline int pnp_manual_config_dev(struct pnp_dev *dev, struct pnp_resource_table *res, int mode) { return -ENODEV; }
+static inline int pnp_auto_config_dev(struct pnp_dev *dev) { return -ENODEV; }

/* driver */
static inline int compare_pnp_id(struct list_head * id_list, const char * id) { return -ENODEV; }

2003-02-04 09:37:08

by Russell King

[permalink] [raw]
Subject: Re: PnP model

On Mon, Feb 03, 2003 at 08:43:25PM +0000, Adam Belay wrote:
> In many cases, Auto configuration can be better then manual configuration.
> 1.) The auto configuration engine in my patch is able to resolve almost any
> resource conflict and provides the greatest chance for all devices to have
> resources allocated.

There is a nice problem with ISA PNP serial ports generated by this
type of thing in 2.5.59.

On boot, we probe for the usual serial ports, and discover two at
0x3f8 and 0x2f8, and we request these resources. Please note that
one port could be in use as a serial console from earlier in the
bootup.

Then, we move on to the PNP probes. The PNP layer gives us two
serial ports, and we initialise them. The PNP layer notices that
the resources for 0x3f8 and 0x2f8, and re-assigns the ports to
0x3e8 and 0x2e8. The serial layer finds two extra ports at
0x3e8 and 0x2e8.

However, the ports at 0x3f8 and 0x2f8 are now gone, along with the
serial console, and the boot messages claim that we have four serial
ports at ttyS0, ttyS1, ttyS2, and ttyS3, when the machine in fact
only has two serial ports. The serial layer likewise believes we
have four serial ports.

I look forward to your thoughts on getting around this problem.

--
Russell King ([email protected]) The developer of ARM Linux
http://www.arm.linux.org.uk/personal/aboutme.html

2003-02-04 10:09:35

by Jaroslav Kysela

[permalink] [raw]
Subject: Re: PnP model

On Tue, 4 Feb 2003, Russell King wrote:

> On Mon, Feb 03, 2003 at 08:43:25PM +0000, Adam Belay wrote:
> > In many cases, Auto configuration can be better then manual configuration.
> > 1.) The auto configuration engine in my patch is able to resolve almost any
> > resource conflict and provides the greatest chance for all devices to have
> > resources allocated.
>
> There is a nice problem with ISA PNP serial ports generated by this
> type of thing in 2.5.59.
>
> On boot, we probe for the usual serial ports, and discover two at
> 0x3f8 and 0x2f8, and we request these resources. Please note that
> one port could be in use as a serial console from earlier in the
> bootup.
>
> Then, we move on to the PNP probes. The PNP layer gives us two
> serial ports, and we initialise them. The PNP layer notices that
> the resources for 0x3f8 and 0x2f8, and re-assigns the ports to
> 0x3e8 and 0x2e8. The serial layer finds two extra ports at
> 0x3e8 and 0x2e8.
>
> However, the ports at 0x3f8 and 0x2f8 are now gone, along with the
> serial console, and the boot messages claim that we have four serial
> ports at ttyS0, ttyS1, ttyS2, and ttyS3, when the machine in fact
> only has two serial ports. The serial layer likewise believes we
> have four serial ports.
>
> I look forward to your thoughts on getting around this problem.

I think that legacy devices must be probed after PnP ones, otherwise
you'll get these duplications.

Jaroslav

-----
Jaroslav Kysela <[email protected]>
Linux Kernel Sound Maintainer
ALSA Project, SuSE Labs

2003-02-04 10:31:43

by Jaroslav Kysela

[permalink] [raw]
Subject: Re: PnP model

On Mon, 3 Feb 2003, Adam Belay wrote:

> On Mon, Feb 03, 2003 at 02:55:37PM +0100, Jaroslav Kysela wrote:
> > Hi all,
> >
> > I think that we need to discuss deeply the right PnP model. The
>
> I'm confident this is the right pnp model.
>
> > actual changes proposed by Adam are going to be more and more complex
> > without allowing the user interactions inside the "auto" steps. The
> > auto-configuration might be good and bad as we all know, but having an
> > method to skip it is necessary.
>
> In many cases, Auto configuration can be better then manual configuration.

Autoconfiguration is better in perfect world... There are always troubles
with some hardware incompatibilities.

> 1.) The auto configuration engine in my patch is able to resolve almost any
> resource conflict and provides the greatest chance for all devices to have
> resources allocated.
> 2.) Certainly some driver developers would like to manually set resources
> but many may prefer the option to auto config.
> 3.) Drivers under the existing system are not aware of many forms of
> resource conflicts and could set resources incorrectly.
> 4.) Some users do not want to worry about manual configuration and would
> welcome an auto configuration system that makes intelligent choices
> without user input. This autoconfiguration system monitors many variables
> that a user would have a hard time keeping track of and never overlooks any
> potential conflicts in its analysis.
>
> The above stated reasons are why I introduced these auto configuration
> (Resource Management) improvements.
>
> I feel that one solution is to support both manual and auto configuration so
> the user can use what he or she prefers, however, I am confident that in most
> cases the auto configuration will be the best option.

But do we have to enable the auto configuration at boot implicitly?
I don't think so. Again, it's better if driver decides if auto or manual
configuration will be used. The step by step configuration makes the
implemenation more rebust (user will know which driver fails). Also,
having legacy devices in the system, you cannot determine which resources
are required for them before appropriate driver detects and initializes
the device. I don't think that this algorithm will be worse than your
proposed one. There are usually no resource conflicts for PnP devices so
it will work at least on the same number of machines like implicit
autoconfiguration at boot.

Also, imagine that PnP driver is build into the kernel: you'll no chance
select the right configuration over sysfs, but there are __setup() macros
allowing to pass the right resources to a driver.

Jaroslav

-----
Jaroslav Kysela <[email protected]>
Linux Kernel Sound Maintainer
ALSA Project, SuSE Labs


2003-02-04 10:40:08

by Russell King

[permalink] [raw]
Subject: Re: PnP model

On Tue, Feb 04, 2003 at 11:18:36AM +0100, Jaroslav Kysela wrote:
> I think that legacy devices must be probed after PnP ones, otherwise
> you'll get these duplications.

Unfortunately, this isn't easy to do, while keeping stuff like serial
consoles working from early at bootup. Alan Cox definitely does not
want to see the serial console initialised any later than it is today,
and he's not the only one.

In addition, the "legacy" devices are part of the 8250.c module - they
have to be for serial console. The PNP devices are probed as part of
the 8250_pnp.c module, and since 8250_pnp.c depends on 8250.c, the
serial PNP ports will _always_ be initialised after the ISA probes.

--
Russell King ([email protected]) The developer of ARM Linux
http://www.arm.linux.org.uk/personal/aboutme.html

2003-02-04 19:16:39

by Andrew Grover

[permalink] [raw]
Subject: RE: PnP model

> From: Adam Belay [mailto:[email protected]]
> In many cases, Auto configuration can be better then manual
> configuration.
> 1.) The auto configuration engine in my patch is able to
> resolve almost any
> resource conflict and provides the greatest chance for all
> devices to have
> resources allocated.
> 2.) Certainly some driver developers would like to manually
> set resources
> but many may prefer the option to auto config.

I think the people who want to manually configure their device's
resources need to step up and justify why this is really necessary.

If someone is manually configuring something, that means the automatic
config *failed*. Why did it fail? It should never fail. Manual config is
only giving the user to opportunity to get something wrong.

Regards -- Andy

2003-02-04 19:32:49

by John Bradford

[permalink] [raw]
Subject: Re: PnP model

> > In many cases, Auto configuration can be better then manual
> > configuration.
> > 1.) The auto configuration engine in my patch is able to
> > resolve almost any
> > resource conflict and provides the greatest chance for all
> > devices to have
> > resources allocated.
> > 2.) Certainly some driver developers would like to manually
> > set resources
> > but many may prefer the option to auto config.
>
> I think the people who want to manually configure their device's
> resources need to step up and justify why this is really necessary.

Prototyping an embedded system, maybe, where you have devices in the
test box that won't be in the production machine. You would want them
to use resources other than those that you want the hardware which
will be present to use.

> If someone is manually configuring something, that means the automatic
> config *failed*.

Not necessarily.

> Why did it fail? It should never fail. Manual config is only giving
> the user to opportunity to get something wrong.

Agreed, auto configuration should never fail, but that doesn't mean
that you shouldn't have manual configuration as an option.

John.

2003-02-04 19:44:19

by Andrew Grover

[permalink] [raw]
Subject: RE: PnP model

> From: John Bradford [mailto:[email protected]]
> > I think the people who want to manually configure their device's
> > resources need to step up and justify why this is really necessary.
>
> Prototyping an embedded system, maybe, where you have devices in the
> test box that won't be in the production machine. You would want them
> to use resources other than those that you want the hardware which
> will be present to use.

Ok fair enough. But I think the drivers should always think things are
handled in a PnP manner, even if they really aren't. ;-) For example,
between the stages where PnP enumerates the devices and the stage where
drivers get device_add notifications as a result of that, we will be
assigning the system resources to each device, but we could also
implement a way at this stage for people to manually alter things. I
think this is the right place to do this, as opposed to having all the
drivers implement code to probe for themselves.

Thoughts?

Regards -- Andy

2003-02-04 21:50:21

by Alan

[permalink] [raw]
Subject: RE: PnP model

On Tue, 2003-02-04 at 19:53, Grover, Andrew wrote:
> Ok fair enough. But I think the drivers should always think things are
> handled in a PnP manner, even if they really aren't. ;-) For example,

If this was a post 2.6 argument I would agree. The notion of removing all the
horribly resource code and having ISA legacy drivers probe code do

my_dev = isa_create_device(&resourceinfo, "soundblaster16");
my_dev->set_irq = foo;

etc type stuff is nice

2003-02-06 02:25:39

by Adam Belay

[permalink] [raw]
Subject: Re: PnP model

On Tue, Feb 04, 2003 at 10:49:37AM +0000, Russell King wrote:
> On Tue, Feb 04, 2003 at 11:18:36AM +0100, Jaroslav Kysela wrote:
> > I think that legacy devices must be probed after PnP ones, otherwise
> > you'll get these duplications.
>
> Unfortunately, this isn't easy to do, while keeping stuff like serial
> consoles working from early at bootup. Alan Cox definitely does not
> want to see the serial console initialised any later than it is today,
> and he's not the only one.
>
> In addition, the "legacy" devices are part of the 8250.c module - they
> have to be for serial console. The PNP devices are probed as part of
> the 8250_pnp.c module, and since 8250_pnp.c depends on 8250.c, the
> serial PNP ports will _always_ be initialised after the ISA probes.
>

I think I have an alternative solution. If we add support for current
resource configs and don't reset the cards, we can determine what the
active configuration was when the serial driver detects the isapnp card.
Because the device will remain active the resources will not be changed.
Below is a patch against my previous 5 patches. This patch also
contains a few fixes and cleanups. Could you please test this and let
me know if it solves the problem.

Also I noticed that the serial driver legacy probe, independent of my
changes, sometimes detects the incorrect irq, is proper irq detection
needed by the serial driver or will it still work properly even if
that information is detected incorrectly?

Thanks,
Adam


P.S.: ISAPnP MEM32 writing and reading still needs a little work.
P.S.: PNPBIOS MEM32s are fully supported.

diff -urN a/drivers/pnp/interface.c b/drivers/pnp/interface.c
--- a/drivers/pnp/interface.c Mon Feb 3 19:44:01 2003
+++ b/drivers/pnp/interface.c Tue Feb 4 20:58:01 2003
@@ -265,7 +265,6 @@
str += sprintf(str," %ld \n", pnp_dma(dev, i));
}
}
- done:
return (str - buf);
}

diff -urN a/drivers/pnp/isapnp/core.c b/drivers/pnp/isapnp/core.c
--- a/drivers/pnp/isapnp/core.c Sun Feb 2 18:43:34 2003
+++ b/drivers/pnp/isapnp/core.c Wed Feb 5 21:06:09 2003
@@ -53,7 +53,7 @@

int isapnp_disable; /* Disable ISA PnP */
int isapnp_rdp; /* Read Data Port */
-int isapnp_reset = 1; /* reset all PnP cards (deactivate) */
+int isapnp_reset = 0; /* reset all PnP cards (deactivate) */
int isapnp_skip_pci_scan; /* skip PCI resource scanning */
int isapnp_verbose = 1; /* verbose mode */

@@ -259,7 +259,7 @@
* We cannot use NE2000 probe spaces for ISAPnP or we
* will lock up machines.
*/
- if ((rdp < 0x280 || rdp > 0x380) && !check_region(rdp, 1))
+ if ((rdp < 0x280 || rdp > 0x380) && !check_region(rdp, 1))
{
isapnp_rdp = rdp;
return 0;
@@ -435,6 +435,7 @@
/*
* Parse logical device tag.
*/
+static int isapnp_get_resources(struct pnp_dev *dev, struct pnp_resource_table * res);

static struct pnp_dev * __init isapnp_parse_device(struct pnp_card *card, int size, int number)
{
@@ -765,7 +766,7 @@
/*
* Parse resource map for ISA PnP card.
*/
-
+
static void __init isapnp_parse_resource_map(struct pnp_card *card)
{
unsigned char type, tmp[17];
@@ -822,7 +823,7 @@
{
int i, j;
unsigned char checksum = 0x6a, bit, b;
-
+
for (i = 0; i < 8; i++) {
b = data[i];
for (j = 0; j < 8; j++) {
@@ -855,6 +856,63 @@
pnpc_add_id(id,card);
}

+
+static int isapnp_parse_current_resources(struct pnp_dev *dev, struct pnp_resource_table * res)
+{
+ int tmp, ret;
+ struct pnp_rule_table rule;
+ if (dev->rule)
+ rule = *dev->rule;
+ else {
+ if (!pnp_generate_rule(dev,1,&rule))
+ return -EINVAL;
+ }
+
+ dev->active = isapnp_read_byte(ISAPNP_CFG_ACTIVATE);
+ if (dev->active) {
+ for (tmp = 0; tmp < PNP_MAX_PORT; tmp++) {
+ ret = isapnp_read_word(ISAPNP_CFG_PORT + (tmp << 1));
+ if (!ret)
+ continue;
+ res->port_resource[tmp].start = ret;
+ if (rule.port[tmp])
+ res->port_resource[tmp].end = ret + rule.port[tmp]->size - 1;
+ else
+ res->port_resource[tmp].end = ret + 1; /* all we can do is assume 1 :-( */
+ res->port_resource[tmp].flags = IORESOURCE_IO;
+ }
+ for (tmp = 0; tmp < PNP_MAX_MEM; tmp++) {
+ ret = isapnp_read_dword(ISAPNP_CFG_MEM + (tmp << 3));
+ if (!ret)
+ continue;
+ res->mem_resource[tmp].start = ret;
+ if (rule.mem[tmp])
+ res->mem_resource[tmp].end = ret + rule.mem[tmp]->size - 1;
+ else
+ res->mem_resource[tmp].end = ret + 1; /* all we can do is assume 1 :-( */
+ res->mem_resource[tmp].flags = IORESOURCE_MEM;
+ }
+ for (tmp = 0; tmp < PNP_MAX_IRQ; tmp++) {
+ ret = (isapnp_read_word(ISAPNP_CFG_IRQ + (tmp << 1)) >> 8);
+ if (!ret)
+ continue;
+ res->irq_resource[tmp].start = res->irq_resource[tmp].end = ret;
+ res->irq_resource[tmp].flags = IORESOURCE_IRQ;
+ }
+ for (tmp = 0; tmp < PNP_MAX_DMA; tmp++) {
+ ret = isapnp_read_byte(ISAPNP_CFG_DMA + tmp);
+ if (ret == 4)
+ continue;
+ if (rule.dma[tmp]) { /* some isapnp systems forget to set this to 4 so we have to check */
+ res->dma_resource[tmp].start = res->dma_resource[tmp].end = ret;
+ res->dma_resource[tmp].flags = IORESOURCE_DMA;
+ }
+ }
+ }
+ return 0;
+}
+
+
/*
* Build device list for all present ISA PnP devices.
*/
@@ -864,6 +922,7 @@
int csn;
unsigned char header[9], checksum;
struct pnp_card *card;
+ struct pnp_dev *dev;

isapnp_wait();
isapnp_key();
@@ -896,8 +955,17 @@
printk(KERN_ERR "isapnp: checksum for device %i is not valid (0x%x)\n", csn, isapnp_checksum_value);
card->checksum = isapnp_checksum_value;
card->protocol = &isapnp_protocol;
+
+ /* read the current resource data */
+ card_for_each_dev(card,dev) {
+ isapnp_device(dev->number);
+ pnp_init_resource_table(&dev->res);
+ isapnp_parse_current_resources(dev, &dev->res);
+ }
+
pnpc_add_card(card);
}
+ isapnp_wait();
return 0;
}

@@ -971,16 +1039,12 @@

static int isapnp_get_resources(struct pnp_dev *dev, struct pnp_resource_table * res)
{
- /* We don't need to do anything but this, the rest is taken care of */
- if (pnp_port_valid(dev, 0) == 0 &&
- pnp_mem_valid(dev, 0) == 0 &&
- pnp_irq_valid(dev, 0) == 0 &&
- pnp_dma_valid(dev, 0) == 0)
- dev->active = 0;
- else
- dev->active = 1;
- *res = dev->res;
- return 0;
+ int ret;
+ pnp_init_resource_table(res);
+ isapnp_cfg_begin(dev->card->number, dev->number);
+ ret = isapnp_parse_current_resources(dev, res);
+ isapnp_cfg_end();
+ return ret;
}

static int isapnp_set_resources(struct pnp_dev *dev, struct pnp_resource_table * res)
@@ -989,18 +1053,19 @@

isapnp_cfg_begin(dev->card->number, dev->number);
dev->active = 1;
- for (tmp = 0; tmp < 8 && res->port_resource[tmp].flags & IORESOURCE_IO; tmp++)
+ for (tmp = 0; tmp < PNP_MAX_PORT && res->port_resource[tmp].flags & IORESOURCE_IO; tmp++)
isapnp_write_word(ISAPNP_CFG_PORT+(tmp<<1), res->port_resource[tmp].start);
- for (tmp = 0; tmp < 2 && res->irq_resource[tmp].flags & IORESOURCE_IRQ; tmp++) {
+ for (tmp = 0; tmp < PNP_MAX_IRQ && res->irq_resource[tmp].flags & IORESOURCE_IRQ; tmp++) {
int irq = res->irq_resource[tmp].start;
if (irq == 2)
irq = 9;
isapnp_write_byte(ISAPNP_CFG_IRQ+(tmp<<1), irq);
}
- for (tmp = 0; tmp < 2 && res->dma_resource[tmp].flags & IORESOURCE_DMA; tmp++)
+ for (tmp = 0; tmp < PNP_MAX_DMA && res->dma_resource[tmp].flags & IORESOURCE_DMA; tmp++)
isapnp_write_byte(ISAPNP_CFG_DMA+tmp, res->dma_resource[tmp].start);
- for (tmp = 0; tmp < 4 && res->mem_resource[tmp].flags & IORESOURCE_MEM; tmp++)
+ for (tmp = 0; tmp < PNP_MAX_MEM && res->mem_resource[tmp].flags & IORESOURCE_MEM; tmp++)
isapnp_write_word(ISAPNP_CFG_MEM+(tmp<<2), (res->mem_resource[tmp].start >> 8) & 0xffff);
+ /* FIXME: We aren't handling 32bit mems properly here */
isapnp_activate(dev->number);
isapnp_cfg_end();
return 0;
@@ -1010,7 +1075,7 @@
{
if (!dev || !dev->active)
return -EINVAL;
- isapnp_cfg_begin(dev->card->number, dev->number);
+ isapnp_cfg_begin(dev->card->number, dev->number);
isapnp_deactivate(dev->number);
dev->active = 0;
isapnp_cfg_end();
diff -urN a/drivers/pnp/resource.c b/drivers/pnp/resource.c
--- a/drivers/pnp/resource.c Mon Feb 3 19:44:02 2003
+++ b/drivers/pnp/resource.c Wed Feb 5 20:01:16 2003
@@ -725,6 +725,12 @@
return 0;
}

+/**
+ * pnp_init_resource_table - Resets a resource table to default values.
+ * @table: pointer to the desired resource table
+ *
+ */
+
void pnp_init_resource_table(struct pnp_resource_table *table)
{
int idx;
@@ -787,6 +793,8 @@
static struct pnp_change * pnp_add_change(struct pnp_change * parent, struct pnp_dev * dev)
{
struct pnp_change * change = pnp_alloc(sizeof(struct pnp_change));
+ if (!change)
+ return NULL;
change->res_bak = dev->res;
change->rule_bak = *dev->rule;
change->dev = dev;
@@ -805,7 +813,15 @@
list_splice_init(&change->changes, &parent->changes);
}

-static int pnp_generate_rule(struct pnp_dev *dev, int depnum)
+/**
+ * pnp_generate_rule - Creates a rule table structure based on depnum and device.
+ * @dev: pointer to the desired device
+ * @depnum: dependent function, if not valid will return an error
+ * @rule: pointer to a rule structure to record data to
+ *
+ */
+
+int pnp_generate_rule(struct pnp_dev * dev, int depnum, struct pnp_rule_table * rule)
{
int nport = 0, nirq = 0, ndma = 0, nmem = 0;
struct pnp_resources * res;
@@ -813,7 +829,6 @@
struct pnp_mem * mem;
struct pnp_irq * irq;
struct pnp_dma * dma;
- struct pnp_rule_table * rule = dev->rule;

if (depnum <= 0 || !rule)
return -EINVAL;
@@ -905,7 +920,7 @@
for (; depnum <= max; depnum++) {
struct pnp_resources * res = pnp_find_resources(dev, depnum);
if (res->priority == priority) {
- if(pnp_generate_rule(dev, depnum)) {
+ if(pnp_generate_rule(dev, depnum, dev->rule)) {
dev->rule->depnum = depnum;
return 1;
}
@@ -923,7 +938,7 @@
struct pnp_dev * cdev;

for (i = 0; i < PNP_MAX_PORT; i++) {
- if (dev->res.port_resource[i].start == 0) {
+ if (dev->res.port_resource[i].start == 0 || pnp_check_port_conflicts(dev,i,SEARCH_WARM)) {
if (!pnp_next_port(dev,i))
return 0;
}
@@ -938,7 +953,7 @@
pnp_commit_changes(parent, change);
}
for (i = 0; i < PNP_MAX_MEM; i++) {
- if (dev->res.mem_resource[i].start == 0) {
+ if (dev->res.mem_resource[i].start == 0 || pnp_check_mem_conflicts(dev,i,SEARCH_WARM)) {
if (!pnp_next_mem(dev,i))
return 0;
}
@@ -953,7 +968,7 @@
pnp_commit_changes(parent, change);
}
for (i = 0; i < PNP_MAX_IRQ; i++) {
- if (dev->res.irq_resource[i].start == -1) {
+ if (dev->res.irq_resource[i].start == -1 || pnp_check_irq_conflicts(dev,i,SEARCH_WARM)) {
if (!pnp_next_irq(dev,i))
return 0;
}
@@ -968,7 +983,7 @@
pnp_commit_changes(parent, change);
}
for (i = 0; i < PNP_MAX_DMA; i++) {
- if (dev->res.dma_resource[i].start == -1) {
+ if (dev->res.dma_resource[i].start == -1 || pnp_check_dma_conflicts(dev,i,SEARCH_WARM)) {
if (!pnp_next_dma(dev,i))
return 0;
}
@@ -989,6 +1004,8 @@
{
struct pnp_change * change = pnp_add_change(parent,dev);
move--;
+ if (!change)
+ return 0;
if (!dev->rule)
goto fail;
if (!pnp_can_configure(dev))
@@ -1014,7 +1031,8 @@
return 0;
}

-static int pnp_generate_config(struct pnp_dev * dev)
+/* this advanced algorithm will shuffle other configs to make room and ensure that the most possible devices have configs */
+static int pnp_advanced_config(struct pnp_dev * dev)
{
int move;
/* if the device cannot be configured skip it */
@@ -1036,7 +1054,6 @@
spin_unlock(&pnp_lock);
}

- spin_lock(&pnp_lock);
pnp_init_resource_table(&dev->res);
dev->rule->depnum = 0;
spin_unlock(&pnp_lock);
@@ -1054,7 +1071,7 @@
do {
cdev = pnp_check_port_conflicts(dev,i,SEARCH_COLD);
if (cdev)
- pnp_generate_config(cdev);
+ pnp_advanced_config(cdev);
} while (cdev);
}
for (i = 0; i < PNP_MAX_MEM; i++)
@@ -1062,7 +1079,7 @@
do {
cdev = pnp_check_mem_conflicts(dev,i,SEARCH_COLD);
if (cdev)
- pnp_generate_config(cdev);
+ pnp_advanced_config(cdev);
} while (cdev);
}
for (i = 0; i < PNP_MAX_IRQ; i++)
@@ -1070,7 +1087,7 @@
do {
cdev = pnp_check_irq_conflicts(dev,i,SEARCH_COLD);
if (cdev)
- pnp_generate_config(cdev);
+ pnp_advanced_config(cdev);
} while (cdev);
}
for (i = 0; i < PNP_MAX_DMA; i++)
@@ -1078,12 +1095,59 @@
do {
cdev = pnp_check_dma_conflicts(dev,i,SEARCH_COLD);
if (cdev)
- pnp_generate_config(cdev);
+ pnp_advanced_config(cdev);
} while (cdev);
}
return 1;
}

+/* this is a much faster algorithm but it may not leave resources for other devices to use */
+static int pnp_simple_config(struct pnp_dev * dev)
+{
+ int i;
+ spin_lock(&pnp_lock);
+ if (dev->active) {
+ spin_unlock(&pnp_lock);
+ return 1;
+ }
+ dev->rule->depnum = 0;
+ pnp_init_resource_table(&dev->res);
+ if (!dev->rule) {
+ dev->rule = pnp_alloc(sizeof(struct pnp_rule_table));
+ if (!dev->rule) {
+ spin_unlock(&pnp_lock);
+ return -ENOMEM;
+ }
+ }
+ while (pnp_next_rule(dev)) {
+ for (i = 0; i < PNP_MAX_PORT; i++) {
+ if (!pnp_next_port(dev,i))
+ continue;
+ }
+ for (i = 0; i < PNP_MAX_MEM; i++) {
+ if (!pnp_next_mem(dev,i))
+ continue;
+ }
+ for (i = 0; i < PNP_MAX_IRQ; i++) {
+ if (!pnp_next_irq(dev,i))
+ continue;
+ }
+ for (i = 0; i < PNP_MAX_DMA; i++) {
+ if (!pnp_next_dma(dev,i))
+ continue;
+ }
+ goto done;
+ }
+ pnp_init_resource_table(&dev->res);
+ dev->rule->depnum = 0;
+ spin_unlock(&pnp_lock);
+ return 0;
+
+done:
+ pnp_resolve_conflicts(dev); /* this is required or we will break the advanced configs */
+ return 1;
+}
+
static int pnp_compare_resources(struct pnp_resource_table * resa, struct pnp_resource_table * resb)
{
int idx;
@@ -1136,6 +1200,7 @@
/**
* pnp_auto_config_dev - determines the best possible resource configuration based on available information
* @dev: pointer to the desired device
+ *
*/

int pnp_auto_config_dev(struct pnp_dev *dev)
@@ -1149,7 +1214,7 @@
if(dev->active)
error = pnp_resolve_conflicts(dev);
else
- error = pnp_generate_config(dev);
+ error = pnp_advanced_config(dev);
return error;
}

@@ -1157,6 +1222,8 @@
* pnp_manual_config_dev - Disables Auto Config and Manually sets the resource table
* @dev: pointer to the desired device
* @res: pointer to the new resource config
+ *
+ * This function can be used by drivers that want to manually set thier resources.
*/

int pnp_manual_config_dev(struct pnp_dev *dev, struct pnp_resource_table * res, int mode)
@@ -1190,9 +1257,7 @@
}
spin_unlock(&pnp_lock);

- if (mode & PNP_CONFIG_RESOLVE)
- pnp_resolve_conflicts(dev);
-
+ pnp_resolve_conflicts(dev);
dev->config_mode = PNP_CONFIG_MANUAL;

fail:
@@ -1216,49 +1281,51 @@
pnp_info("res: The PnP device '%s' is already active.", dev->dev.bus_id);
return -EBUSY;
}
- /* if the auto config failed, try again now, because this device was requested before others it's best to find a config at all costs */
+ spin_lock(&pnp_lock); /* we lock just in case the device is being configured during this call */
+ dev->active = 1;
+ spin_unlock(&pnp_lock); /* once the device is claimed active we know it won't be configured so we can unlock */
+
+ /* If this condition is true, advanced configuration failed, we need to get this device up and running
+ * so we use the simple config engine which ignores cold conflicts, this of course may lead to new failures */
if (!pnp_is_active(dev)) {
- spin_lock(&pnp_lock);
- if (!pnp_next_config(dev,1,NULL)) {
- pnp_init_resource_table(&dev->res);
- dev->rule->depnum = 0;
- pnp_err("res: Unable to resolve resource conflicts for the device '%s'", dev->dev.bus_id);
- spin_unlock(&pnp_lock);
- return -EBUSY;
+ if (!pnp_simple_config(dev)) {
+ pnp_err("res: Unable to resolve resource conflicts for the device '%s'.", dev->dev.bus_id);
+ goto fail;
}
- spin_unlock(&pnp_lock);
}
if (dev->config_mode & PNP_CONFIG_INVALID) {
- pnp_info("res: Unable to activate the PnP device '%s' because its resource configuration is invalid", dev->dev.bus_id);
- return -EINVAL;
+ pnp_info("res: Unable to activate the PnP device '%s' because its resource configuration is invalid.", dev->dev.bus_id);
+ goto fail;
}
if (dev->status != PNP_READY && dev->status != PNP_ATTACHED){
- pnp_err("res: Activation failed because the PnP device '%s' is busy", dev->dev.bus_id);
- return -EBUSY;
+ pnp_err("res: Activation failed because the PnP device '%s' is busy.", dev->dev.bus_id);
+ goto fail;
}
if (!pnp_can_write(dev)) {
- pnp_info("res: Unable to activate the PnP device '%s' because this feature is not supported", dev->dev.bus_id);
- return -EINVAL;
+ pnp_info("res: Unable to activate the PnP device '%s' because this feature is not supported.", dev->dev.bus_id);
+ goto fail;
}
if (!dev->protocol->set(dev, &dev->res)<0) {
- pnp_err("res: The protocol '%s' reports that activating the PnP device '%s' has failed", dev->protocol->name, dev->dev.bus_id);
- return -1;
+ pnp_err("res: The protocol '%s' reports that activating the PnP device '%s' has failed.", dev->protocol->name, dev->dev.bus_id);
+ goto fail;
}
if (pnp_can_read(dev)) {
struct pnp_resource_table res;
dev->protocol->get(dev, &res);
- if (pnp_compare_resources(&dev->res, &res)) {
- pnp_err("res: The resources requested do not match those actually set for the PnP device '%s'", dev->dev.bus_id);
- return -1;
- }
+ if (pnp_compare_resources(&dev->res, &res)) /* if this happens we may be in big trouble but it's best just to continue */
+ pnp_err("res: The resources requested do not match those set for the PnP device '%s', please report.", dev->dev.bus_id);
} else
dev->active = pnp_is_active(dev);
- pnp_dbg("res: the device '%s' has been activated", dev->dev.bus_id);
+ pnp_dbg("res: the device '%s' has been activated.", dev->dev.bus_id);
if (dev->rule) {
kfree(dev->rule);
dev->rule = NULL;
}
return 0;
+
+fail:
+ dev->active = 0; /* fixes incorrect active state */
+ return -EINVAL;
}

/**
@@ -1277,18 +1344,19 @@
return -EINVAL;
}
if (dev->status != PNP_READY){
- pnp_info("res: Disable failed becuase the PnP device '%s' is busy", dev->dev.bus_id);
+ pnp_info("res: Disable failed becuase the PnP device '%s' is busy.", dev->dev.bus_id);
return -EINVAL;
}
- if (!pnp_can_disable(dev)<0) {
- pnp_info("res: Unable to disable the PnP device '%s' because this feature is not supported", dev->dev.bus_id);
+ if (!pnp_can_disable(dev)) {
+ pnp_info("res: Unable to disable the PnP device '%s' because this feature is not supported.", dev->dev.bus_id);
return -EINVAL;
}
- if (!dev->protocol->disable(dev)) {
- pnp_err("res: The protocol '%s' reports that disabling the PnP device '%s' has failed", dev->protocol->name, dev->dev.bus_id);
+ if (dev->protocol->disable(dev)<0) {
+ pnp_err("res: The protocol '%s' reports that disabling the PnP device '%s' has failed.", dev->protocol->name, dev->dev.bus_id);
return -1;
}
- pnp_dbg("the device '%s' has been disabled", dev->dev.bus_id);
+ dev->active = 0; /* just in case the protocol doesn't do this */
+ pnp_dbg("the device '%s' has been disabled.", dev->dev.bus_id);
return 0;
}

diff -urN a/include/linux/pnp.h b/include/linux/pnp.h
--- a/include/linux/pnp.h Mon Feb 3 19:44:02 2003
+++ b/include/linux/pnp.h Tue Feb 4 21:41:01 2003
@@ -303,9 +303,8 @@
/* config modes */
#define PNP_CONFIG_AUTO 0x0001 /* Use the Resource Configuration Engine to determine resource settings */
#define PNP_CONFIG_MANUAL 0x0002 /* the config has been manually specified */
-#define PNP_CONFIG_FORCE 0x0003 /* disables validity checking */
-#define PNP_CONFIG_RESOLVE 0x0008 /* moves other configs out of the way, use only when absolutely necessary */
-#define PNP_CONFIG_INVALID 0x0010 /* If this flag is set, the pnp layer will refuse to activate the device */
+#define PNP_CONFIG_FORCE 0x0004 /* disables validity checking */
+#define PNP_CONFIG_INVALID 0x0008 /* If this flag is set, the pnp layer will refuse to activate the device */

/* capabilities */
#define PNP_READ 0x0001
@@ -381,6 +380,7 @@
int pnp_add_port_resource(struct pnp_dev *dev, int depnum, struct pnp_port *data);
int pnp_add_mem_resource(struct pnp_dev *dev, int depnum, struct pnp_mem *data);
void pnp_init_resource_table(struct pnp_resource_table *table);
+int pnp_generate_rule(struct pnp_dev * dev, int depnum, struct pnp_rule_table * rule);
int pnp_activate_dev(struct pnp_dev *dev);
int pnp_disable_dev(struct pnp_dev *dev);
void pnp_resource_change(struct resource *resource, unsigned long start, unsigned long size);
@@ -421,6 +421,7 @@
static inline int pnp_add_port_resource(struct pnp_dev *dev, int depnum, struct pnp_irq *data) { return -ENODEV; }
static inline int pnp_add_mem_resource(struct pnp_dev *dev, int depnum, struct pnp_irq *data) { return -ENODEV; }
static inline void pnp_init_resource_table(struct pnp_resource_table *table) { ; }
+static inline int pnp_generate_rule(struct pnp_dev * dev, int depnum, struct pnp_rule_table * rule) { return -ENODEV; }
static inline int pnp_activate_dev(struct pnp_dev *dev) { return -ENODEV; }
static inline int pnp_disable_dev(struct pnp_dev *dev) { return -ENODEV; }
static inline void pnp_resource_change(struct resource *resource, unsigned long start, unsigned long size) { ; }

2003-02-06 23:39:28

by Adam Belay

[permalink] [raw]
Subject: Re: PnP model

On Tue, Feb 04, 2003 at 11:53:40AM -0800, Grover, Andrew wrote:
> > From: John Bradford [mailto:[email protected]]
> > > I think the people who want to manually configure their device's
> > > resources need to step up and justify why this is really necessary.
> >
> > Prototyping an embedded system, maybe, where you have devices in the
> > test box that won't be in the production machine. You would want them
> > to use resources other than those that you want the hardware which
> > will be present to use.
>
> Ok fair enough. But I think the drivers should always think things are
> handled in a PnP manner, even if they really aren't. ;-) For example,
> between the stages where PnP enumerates the devices and the stage where
> drivers get device_add notifications as a result of that, we will be
> assigning the system resources to each device, but we could also
> implement a way at this stage for people to manually alter things. I
> think this is the right place to do this, as opposed to having all the
> drivers implement code to probe for themselves.
>
> Thoughts?

I agree. Actually the isapnp specifications (see Figure 2. Plug and Play ISA
Configuration Flow for Plug and Play BIOS located in Plug and Play ISA
Specification Version 1.0a) recommend that the operating system configures
and activates all devices before drivers are loaded. For the most part
linux plug and play follows this standard with the exception of manual
configuration support which is included in my latest patches.

Regards,
Adam

2003-02-07 08:47:31

by Jaroslav Kysela

[permalink] [raw]
Subject: Re: PnP model

On Wed, 5 Feb 2003, Adam Belay wrote:

> On Tue, Feb 04, 2003 at 10:49:37AM +0000, Russell King wrote:
> > On Tue, Feb 04, 2003 at 11:18:36AM +0100, Jaroslav Kysela wrote:
> > > I think that legacy devices must be probed after PnP ones, otherwise
> > > you'll get these duplications.
> >
> > Unfortunately, this isn't easy to do, while keeping stuff like serial
> > consoles working from early at bootup. Alan Cox definitely does not
> > want to see the serial console initialised any later than it is today,
> > and he's not the only one.
> >
> > In addition, the "legacy" devices are part of the 8250.c module - they
> > have to be for serial console. The PNP devices are probed as part of
> > the 8250_pnp.c module, and since 8250_pnp.c depends on 8250.c, the
> > serial PNP ports will _always_ be initialised after the ISA probes.
> >
>
> I think I have an alternative solution. If we add support for current
> resource configs and don't reset the cards, we can determine what the
> active configuration was when the serial driver detects the isapnp card.
> Because the device will remain active the resources will not be changed.
> Below is a patch against my previous 5 patches. This patch also
> contains a few fixes and cleanups. Could you please test this and let
> me know if it solves the problem.
>
> Also I noticed that the serial driver legacy probe, independent of my
> changes, sometimes detects the incorrect irq, is proper irq detection
> needed by the serial driver or will it still work properly even if
> that information is detected incorrectly?

> -int isapnp_reset = 1; /* reset all PnP cards (deactivate) */
> +int isapnp_reset = 0; /* reset all PnP cards (deactivate) */

Please, don't do this. The default value is quite ok. I think that it's
more PnP BIOS problem than ISA PnP one (I don't know about any motherboard
where the standard serial ports are ISA PnP). But the parsing of active
configuration when isapnp_reset == 0 is ok...

> + for (tmp = 0; tmp < PNP_MAX_PORT && res->port_resource[tmp].flags & IORESOURCE_IO; tmp++)

Again, pnp_valid_* macros are your fried. You deleted them from your
tree during merge.

Jaroslav

-----
Jaroslav Kysela <[email protected]>
Linux Kernel Sound Maintainer
ALSA Project, SuSE Labs