diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 0f9b0783ed37605e8720c004c720544021b4a1b2..0237a5524e07b68b9cd6566160902e9a08601fe0 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -239,14 +239,13 @@ config BLK_DEV_CMD640_ENHANCED and your BIOS does not already do this for you, then say Y here. Otherwise say N. -config BLK_DEV_ISAPNP - bool "ISA-PNP EIDE support" - depends on BLK_DEV_IDE && ISAPNP +config BLK_DEV_IDEPNP + bool "PNP EIDE support" + depends on BLK_DEV_IDE && PNP help - If you have an ISA EIDE card that is PnP (Plug and Play) and - requires setup first before scanning for devices, say Y here. - - If unsure, say N. + If you have a PnP (Plug and Play) compatible EIDE card and + would like the kernel to automatically detect and activate + it, say Y here. config BLK_DEV_IDEPCI bool "PCI IDE chipset support" if PCI diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index 13bc8113953657c1f1a5d60370f94580b164e3a4..83ee41a73b97b0d8d9aba937ff12eb4a8a65d60a 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -21,7 +21,7 @@ obj-$(CONFIG_BLK_DEV_IDEFLOPPY) += ide-floppy.o obj-$(CONFIG_BLK_DEV_IDEPCI) += setup-pci.o obj-$(CONFIG_BLK_DEV_IDEDMA_PCI) += ide-dma.o obj-$(CONFIG_BLK_DEV_IDE_TCQ) += ide-tcq.o -obj-$(CONFIG_BLK_DEV_ISAPNP) += ide-pnp.o +obj-$(CONFIG_BLK_DEV_IDEPNP) += ide-pnp.o ifeq ($(CONFIG_BLK_DEV_IDE),y) obj-$(CONFIG_PROC_FS) += ide-proc.o diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c index 63085d6d00d1763769bd8a065d3b20c27575f626..00a240f73c090b702cd0ae2c87cde6d6b71ac35e 100644 --- a/drivers/ide/ide-pnp.c +++ b/drivers/ide/ide-pnp.c @@ -19,9 +19,7 @@ #include <linux/ide.h> #include <linux/init.h> -#include <linux/isapnp.h> - -#define DEV_NAME(dev) (dev->name) +#include <linux/pnp.h> #define GENERIC_HD_DATA 0 #define GENERIC_HD_ERROR 1 @@ -32,31 +30,27 @@ #define GENERIC_HD_SELECT 6 #define GENERIC_HD_STATUS 7 -static int generic_ide_offsets[IDE_NR_PORTS] __initdata = { +static int generic_ide_offsets[IDE_NR_PORTS] = { GENERIC_HD_DATA, GENERIC_HD_ERROR, GENERIC_HD_NSECTOR, GENERIC_HD_SECTOR, GENERIC_HD_LCYL, GENERIC_HD_HCYL, GENERIC_HD_SELECT, GENERIC_HD_STATUS, -1, -1 }; -/* ISA PnP device table entry */ -struct pnp_dev_t { - unsigned short card_vendor, card_device, vendor, device; - int (*init_fn)(struct pnp_dev *dev, int enable); +/* Add your devices here :)) */ +struct pnp_device_id idepnp_devices[] = { + /* Generic ESDI/IDE/ATA compatible hard disk controller */ + {.id = "PNP0600", .driver_data = 0}, + {.id = ""} }; -/* Generic initialisation function for ISA PnP IDE interface */ - -static int __init pnpide_generic_init(struct pnp_dev *dev, int enable) +static int idepnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id) { hw_regs_t hw; ide_hwif_t *hwif; int index; - if (!enable) - return 0; - if (!(pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) && pnp_irq_valid(dev, 0))) - return 1; + return -1; ide_setup_ports(&hw, (unsigned long) pnp_port_start(dev, 0), generic_ide_offsets, @@ -68,82 +62,36 @@ static int __init pnpide_generic_init(struct pnp_dev *dev, int enable) index = ide_register_hw(&hw, &hwif); if (index != -1) { - printk(KERN_INFO "ide%d: %s IDE interface\n", index, DEV_NAME(dev)); + printk(KERN_INFO "ide%d: generic PnP IDE interface\n", index); + pnp_set_drvdata(dev,hwif); hwif->pnp_dev = dev; return 0; } - return 1; + return -1; } -/* Add your devices here :)) */ -struct pnp_dev_t idepnp_devices[] __initdata = { - /* Generic ESDI/IDE/ATA compatible hard disk controller */ - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0600), - pnpide_generic_init }, - { 0 } -}; +static void idepnp_remove(struct pnp_dev * dev) +{ + ide_hwif_t *hwif = pnp_get_drvdata(dev); + if (hwif) { + ide_unregister(hwif->index); + } else + printk(KERN_ERR "idepnp: Unable to remove device, please report.\n"); +} -#define NR_PNP_DEVICES 8 -struct pnp_dev_inst { - struct pnp_dev *dev; - struct pnp_dev_t *dev_type; +static struct pnp_driver idepnp_driver = { + .name = "ide", + .id_table = idepnp_devices, + .probe = idepnp_probe, + .remove = idepnp_remove, }; -static struct pnp_dev_inst devices[NR_PNP_DEVICES]; -static int pnp_ide_dev_idx = 0; -/* - * Probe for ISA PnP IDE interfaces. - */ -void __init pnpide_init(int enable) +void pnpide_init(int enable) { - struct pnp_dev *dev = NULL; - struct pnp_dev_t *dev_type; - - if (!isapnp_present()) - return; - - /* Module unload, deactivate all registered devices. */ - if (!enable) { - int i; - for (i = 0; i < pnp_ide_dev_idx; i++) { - dev = devices[i].dev; - devices[i].dev_type->init_fn(dev, 0); - pnp_device_detach(dev); - } - return; - } - - for (dev_type = idepnp_devices; dev_type->vendor; dev_type++) { - while ((dev = pnp_find_dev(NULL, dev_type->vendor, - dev_type->device, dev))) { - - if (pnp_device_attach(dev) < 0) - continue; - - if (pnp_activate_dev(dev, NULL) < 0) { - printk(KERN_ERR"ide: %s activate failed\n", DEV_NAME(dev)); - continue; - } - - /* Call device initialization function */ - if (dev_type->init_fn(dev, 1)) { - pnp_device_detach(dev); - } else { -#ifdef MODULE - /* - * Register device in the array to - * deactivate it on a module unload. - */ - if (pnp_ide_dev_idx >= NR_PNP_DEVICES) - return; - devices[pnp_ide_dev_idx].dev = dev; - devices[pnp_ide_dev_idx].dev_type = dev_type; - pnp_ide_dev_idx++; -#endif - } - } - } + if(enable) + pnp_register_driver(&idepnp_driver); + else + pnp_unregister_driver(&idepnp_driver); } diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 46fa224180d4bfecef59632cc9af23bb3eaa3731..e4df5743e77f10ac01bde1235d7b9fbb8bb07a60 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -818,6 +818,7 @@ void ide_unregister (unsigned int index) EXPORT_SYMBOL(ide_unregister); + /** * ide_setup_ports - set up IDE interface ports * @hw: register descriptions @@ -2145,12 +2146,12 @@ static void __init probe_for_hwifs (void) buddha_init(); } #endif /* CONFIG_BLK_DEV_BUDDHA */ -#if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP) +#if defined(CONFIG_BLK_DEV_IDEPNP) && defined(CONFIG_PNP) { extern void pnpide_init(int enable); pnpide_init(1); } -#endif /* CONFIG_BLK_DEV_ISAPNP */ +#endif /* CONFIG_BLK_DEV_IDEPNP */ } void __init ide_init_builtin_drivers (void) @@ -2321,9 +2322,9 @@ int ide_unregister_subdriver (ide_drive_t *drive) spin_unlock_irqrestore(&ide_lock, flags); return 1; } -#if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP) && defined(MODULE) +#if defined(CONFIG_BLK_DEV_IDEPNP) && defined(CONFIG_PNP) && defined(MODULE) pnpide_init(0); -#endif /* CONFIG_BLK_DEV_ISAPNP */ +#endif /* CONFIG_BLK_DEV_IDEPNP */ #ifdef CONFIG_PROC_FS ide_remove_proc_entries(drive->proc, DRIVER(drive)->proc); ide_remove_proc_entries(drive->proc, generic_subdriver_entries); diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c index 7ac2ad722baf120ea057f71b363fc185bc035586..13f0b59942569a8318fd82fada9290c849ca9302 100644 --- a/drivers/media/radio/radio-cadet.c +++ b/drivers/media/radio/radio-cadet.c @@ -1,4 +1,4 @@ -/* radio-cadet.c - A video4linux driver for the ADS Cadet AM/FM Radio Card +/* radio-cadet.c - A video4linux driver for the ADS Cadet AM/FM Radio Card * * by Fred Gleason <fredg@wava.com> * Version 0.3.3 @@ -20,6 +20,9 @@ * Removed dead CONFIG_RADIO_CADET_PORT code * PnP detection on load is now default (no args necessary) * + * 2002-01-17 Adam Belay <ambx1@neo.rr.com> + * Updated to latest pnp code + * */ #include <linux/module.h> /* Modules */ @@ -30,7 +33,7 @@ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/videodev.h> /* kernel radio structs */ #include <linux/param.h> -#include <linux/isapnp.h> +#include <linux/pnp.h> #define RDS_BUFFER 256 @@ -47,8 +50,6 @@ static unsigned char rdsbuf[RDS_BUFFER]; static int cadet_lock=0; static int cadet_probe(void); -static struct pnp_dev *dev = NULL; -static int isapnp_cadet_probe(void); /* * Signal Strength Threshold Values @@ -152,7 +153,7 @@ static unsigned cadet_gettune(void) */ outb(curvol,io+1); cadet_lock--; - + return fifo; } @@ -541,22 +542,23 @@ static struct video_device cadet_radio= .fops = &cadet_fops, }; -static int isapnp_cadet_probe(void) -{ - dev = pnp_find_dev (NULL, ISAPNP_VENDOR('M','S','M'), - ISAPNP_FUNCTION(0x0c24), NULL); +static struct pnp_device_id cadet_pnp_devices[] = { + /* ADS Cadet AM/FM Radio Card */ + {.id = "MSM0c24", .driver_data = 0}, + {.id = ""} +}; +MODULE_DEVICE_TABLE(pnp, id_table); + +static int cadet_pnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id) +{ if (!dev) return -ENODEV; - if (pnp_device_attach(dev) < 0) - return -EAGAIN; - if (pnp_activate_dev(dev, NULL) < 0) { - printk ("radio-cadet: pnp configure failed (out of resources?)\n"); - pnp_device_detach(dev); - return -EIO; - } + /* only support one device */ + if (io > 0) + return -EBUSY; + if (!pnp_port_valid(dev, 0)) { - pnp_device_detach(dev); return -ENODEV; } @@ -567,6 +569,13 @@ static int isapnp_cadet_probe(void) return io; } +static struct pnp_driver cadet_pnp_driver = { + .name = "radio-cadet", + .id_table = cadet_pnp_devices, + .probe = cadet_pnp_probe, + .remove = NULL, +}; + static int cadet_probe(void) { static int iovals[8]={0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e}; @@ -597,7 +606,7 @@ static int __init cadet_init(void) * If a probe was requested then probe ISAPnP first (safest) */ if (io < 0) - io = isapnp_cadet_probe(); + pnp_register_driver(&cadet_pnp_driver); /* * If that fails then probe unsafely if probe is requested */ @@ -612,16 +621,19 @@ static int __init cadet_init(void) #ifdef MODULE printk(KERN_ERR "You must set an I/O address with io=0x???\n"); #endif - return -EINVAL; + goto fail; } if (!request_region(io,2,"cadet")) - return -EBUSY; + goto fail; if(video_register_device(&cadet_radio,VFL_TYPE_RADIO,radio_nr)==-1) { release_region(io,2); - return -EINVAL; + goto fail; } printk(KERN_INFO "ADS Cadet Radio Card at 0x%x\n",io); return 0; +fail: + pnp_unregister_driver(&cadet_pnp_driver); + return -1; } @@ -634,21 +646,11 @@ MODULE_PARM(io, "i"); MODULE_PARM_DESC(io, "I/O address of Cadet card (0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e)"); MODULE_PARM(radio_nr, "i"); -static struct isapnp_device_id id_table[] __devinitdata = { - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('M','S','M'), ISAPNP_FUNCTION(0x0c24), 0 }, - {0} -}; - -MODULE_DEVICE_TABLE(isapnp, id_table); - static void __exit cadet_cleanup_module(void) { video_unregister_device(&cadet_radio); release_region(io,2); - - if (dev) - pnp_device_detach(dev); + pnp_unregister_driver(&cadet_pnp_driver); } module_init(cadet_init); diff --git a/drivers/pnp/Makefile b/drivers/pnp/Makefile index 9d42052d9472a0c5450c8fc947297314bee68e4b..a6a6ee3bba627579f9e4767640d7762fce3a6342 100644 --- a/drivers/pnp/Makefile +++ b/drivers/pnp/Makefile @@ -4,8 +4,7 @@ pnp-card-$(CONFIG_PNP_CARD) = card.o -obj-y := core.o driver.o resource.o interface.o quirks.o names.o system.o $(pnp-card-y) +obj-y := core.o driver.o resource.o manager.o support.o interface.o quirks.o names.o system.o $(pnp-card-y) obj-$(CONFIG_PNPBIOS) += pnpbios/ obj-$(CONFIG_ISAPNP) += isapnp/ - diff --git a/drivers/pnp/base.h b/drivers/pnp/base.h index 4a022d3d8ee955965212c8e40ec05ac590f4f9a0..e991aa70220ec14140cb57c11cfdbd0fb53478d8 100644 --- a/drivers/pnp/base.h +++ b/drivers/pnp/base.h @@ -1,9 +1,32 @@ extern struct bus_type pnp_bus_type; extern spinlock_t pnp_lock; -extern void *pnp_alloc(long size); -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 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); +void *pnp_alloc(long size); +int pnp_interface_attach_device(struct pnp_dev *dev); +void pnp_name_device(struct pnp_dev *dev); +void pnp_fixup_device(struct pnp_dev *dev); +void pnp_free_resources(struct pnp_resources *resources); +int __pnp_add_device(struct pnp_dev *dev); +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 */ + +/* conflict search modes */ +#define SEARCH_WARM 1 /* check for conflicts with active devices */ +#define SEARCH_COLD 0 /* check for conflicts with disabled devices */ + +struct pnp_dev * pnp_check_port_conflicts(struct pnp_dev * dev, int idx, int mode); +int pnp_check_port(struct pnp_dev * dev, int idx); +struct pnp_dev * pnp_check_mem_conflicts(struct pnp_dev * dev, int idx, int mode); +int pnp_check_mem(struct pnp_dev * dev, int idx); +struct pnp_dev * pnp_check_irq_conflicts(struct pnp_dev * dev, int idx, int mode); +int pnp_check_irq(struct pnp_dev * dev, int idx); +struct pnp_dev * pnp_check_dma_conflicts(struct pnp_dev * dev, int idx, int mode); +int pnp_check_dma(struct pnp_dev * dev, int idx); diff --git a/drivers/pnp/card.c b/drivers/pnp/card.c index 19318f322d542af77e674b989648a7729de22d89..fb628c3dfbc6abcb0249db47f9a6e20639bd6472 100644 --- a/drivers/pnp/card.c +++ b/drivers/pnp/card.c @@ -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; @@ -43,8 +43,8 @@ static int card_bus_match(struct device *dev, struct device_driver *drv) } struct bus_type pnpc_bus_type = { - name: "pnp_card", - match: card_bus_match, + .name = "pnp_card", + .match = card_bus_match, }; @@ -106,7 +106,6 @@ int pnpc_add_card(struct pnp_card *card) 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; @@ -144,7 +143,6 @@ void pnpc_remove_card(struct pnp_card *card) list_for_each_safe(pos,temp,&card->devices){ struct pnp_dev *dev = card_to_pnp_dev(pos); pnpc_remove_device(dev); - __pnp_remove_device(dev); } } @@ -221,7 +219,7 @@ struct pnp_dev * pnp_request_card_device(struct pnp_card *card, const char *id, 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 +284,7 @@ static int pnpc_card_probe(struct device *dev) 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 --git a/drivers/pnp/core.c b/drivers/pnp/core.c index 6e4552115cbd9dce8bc2cd32f56158c21bc9b9ec..07ece270ba65ff7f8ddf7ca3140b3db5a60db737 100644 --- a/drivers/pnp/core.c +++ b/drivers/pnp/core.c @@ -104,31 +104,29 @@ static void pnp_free_ids(struct pnp_dev *dev) 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_auto_config_dev(dev); + ret = device_register(&dev->dev); + if (ret == 0) pnp_interface_attach_device(dev); - } - return error; + return ret; } /* @@ -172,7 +170,7 @@ void pnp_remove_device(struct pnp_dev *dev) 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 --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c index 1c56e16bf49c6ef0899ad0989ac0fab5988a2d03..451585f46bcefe30e9f8ca92dd08d7a1b893a12b 100644 --- a/drivers/pnp/driver.c +++ b/drivers/pnp/driver.c @@ -95,7 +95,7 @@ static int pnp_device_probe(struct device *dev) pnp_dev = to_pnp_dev(dev); pnp_drv = to_pnp_driver(dev->driver); - pnp_dbg("pnp: match found with the PnP device '%s' and the driver '%s'", dev->bus_id,pnp_drv->name); + pnp_dbg("match found with the PnP device '%s' and the driver '%s'", dev->bus_id,pnp_drv->name); error = pnp_device_attach(pnp_dev); if (error < 0) @@ -103,13 +103,10 @@ static int pnp_device_probe(struct device *dev) 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; } - } else { - if ((pnp_drv->flags & PNP_DRIVER_DO_NOT_ACTIVATE)) - pnp_disable_dev(pnp_dev); } error = 0; if (pnp_drv->probe && pnp_dev->active) { diff --git a/drivers/pnp/interface.c b/drivers/pnp/interface.c index 20c9557b7b4e28895b4d324d96b1a380877a1c6f..2520f9ec4ad54e5bfd0d07dfed11025d0fd8d56f 100644 --- a/drivers/pnp/interface.c +++ b/drivers/pnp/interface.c @@ -1,7 +1,7 @@ /* * interface.c - contains everything related to the user interface * - * Some code is based on isapnp_proc.c (c) Jaroslav Kysela <perex@suse.cz> + * Some code, especially possible resource dumping is based on isapnp_proc.c (c) Jaroslav Kysela <perex@suse.cz> * Copyright 2002 Adam Belay <ambx1@neo.rr.com> * */ @@ -12,6 +12,8 @@ #include <linux/list.h> #include <linux/types.h> #include <linux/stat.h> +#include <linux/ctype.h> +#include <asm/uaccess.h> #include "base.h" @@ -158,27 +160,15 @@ static void pnp_print_mem(pnp_info_buffer_t *buffer, char *space, struct pnp_mem case IORESOURCE_MEM_8AND16BIT: s = "8-bit&16-bit"; break; + case IORESOURCE_MEM_32BIT: + s = "32-bit"; + break; default: s = "16-bit"; } pnp_printf(buffer, ", %s\n", s); } -static void pnp_print_mem32(pnp_info_buffer_t *buffer, char *space, struct pnp_mem32 *mem32) -{ - int first = 1, i; - - pnp_printf(buffer, "%s32-bit memory ", space); - for (i = 0; i < 17; i++) { - if (first) { - first = 0; - } else { - pnp_printf(buffer, ":"); - } - pnp_printf(buffer, "%02x", mem32->data[i]); - } -} - static void pnp_print_resources(pnp_info_buffer_t *buffer, char *space, struct pnp_resources *res, int dep) { char *s; @@ -186,7 +176,6 @@ static void pnp_print_resources(pnp_info_buffer_t *buffer, char *space, struct p struct pnp_irq *irq; struct pnp_dma *dma; struct pnp_mem *mem; - struct pnp_mem32 *mem32; switch (res->priority) { case PNP_RES_PRIORITY_PREFERRED: @@ -211,18 +200,15 @@ static void pnp_print_resources(pnp_info_buffer_t *buffer, char *space, struct p pnp_print_dma(buffer, space, dma); for (mem = res->mem; mem; mem = mem->next) pnp_print_mem(buffer, space, mem); - for (mem32 = res->mem32; mem32; mem32 = mem32->next) - pnp_print_mem32(buffer, space, mem32); } 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; - int dep = 0; - pnp_info_buffer_t *buffer; - - buffer = (pnp_info_buffer_t *) pnp_alloc(sizeof(pnp_info_buffer_t)); + struct pnp_resources * res = dev->possible; + int ret, dep = 0; + pnp_info_buffer_t *buffer = (pnp_info_buffer_t *) + pnp_alloc(sizeof(pnp_info_buffer_t)); if (!buffer) return -ENOMEM; buffer->len = PAGE_SIZE; @@ -236,97 +222,301 @@ static ssize_t pnp_show_possible_resources(struct device *dmdev, char *buf) res = res->dep; dep++; } - return (buffer->curr - buf); + ret = (buffer->curr - buf); + kfree(buffer); + return ret; } static DEVICE_ATTR(possible,S_IRUGO,pnp_show_possible_resources,NULL); +static void pnp_print_conflict_node(pnp_info_buffer_t *buffer, struct pnp_dev * dev) +{ + if (!dev) + return; + pnp_printf(buffer, "'%s'.\n", dev->dev.bus_id); +} + +static void pnp_print_conflict_desc(pnp_info_buffer_t *buffer, int conflict) +{ + if (!conflict) + return; + pnp_printf(buffer, " Conflict Detected: %2x - ", conflict); + switch (conflict) { + case CONFLICT_TYPE_RESERVED: + pnp_printf(buffer, "manually reserved.\n"); + break; + + case CONFLICT_TYPE_IN_USE: + pnp_printf(buffer, "currently in use.\n"); + break; + + case CONFLICT_TYPE_PCI: + pnp_printf(buffer, "PCI device.\n"); + break; + + case CONFLICT_TYPE_INVALID: + pnp_printf(buffer, "invalid.\n"); + break; + + case CONFLICT_TYPE_INTERNAL: + pnp_printf(buffer, "another resource on this device.\n"); + break; + + case CONFLICT_TYPE_PNP_WARM: + pnp_printf(buffer, "active PnP device "); + break; + + case CONFLICT_TYPE_PNP_COLD: + pnp_printf(buffer, "disabled PnP device "); + break; + default: + pnp_printf(buffer, "Unknown conflict.\n"); + break; + } +} + +static void pnp_print_conflict(pnp_info_buffer_t *buffer, struct pnp_dev * dev, int idx, int type) +{ + struct pnp_dev * cdev, * wdev = NULL; + int conflict; + switch (type) { + case IORESOURCE_IO: + conflict = pnp_check_port(dev, idx); + if (conflict == CONFLICT_TYPE_PNP_WARM) + wdev = pnp_check_port_conflicts(dev, idx, SEARCH_WARM); + cdev = pnp_check_port_conflicts(dev, idx, SEARCH_COLD); + break; + case IORESOURCE_MEM: + conflict = pnp_check_mem(dev, idx); + if (conflict == CONFLICT_TYPE_PNP_WARM) + wdev = pnp_check_mem_conflicts(dev, idx, SEARCH_WARM); + cdev = pnp_check_mem_conflicts(dev, idx, SEARCH_COLD); + break; + case IORESOURCE_IRQ: + conflict = pnp_check_irq(dev, idx); + if (conflict == CONFLICT_TYPE_PNP_WARM) + wdev = pnp_check_irq_conflicts(dev, idx, SEARCH_WARM); + cdev = pnp_check_irq_conflicts(dev, idx, SEARCH_COLD); + break; + case IORESOURCE_DMA: + conflict = pnp_check_dma(dev, idx); + if (conflict == CONFLICT_TYPE_PNP_WARM) + wdev = pnp_check_dma_conflicts(dev, idx, SEARCH_WARM); + cdev = pnp_check_dma_conflicts(dev, idx, SEARCH_COLD); + break; + default: + return; + } + + pnp_print_conflict_desc(buffer, conflict); + + if (wdev) + pnp_print_conflict_node(buffer, wdev); + + if (cdev) { + pnp_print_conflict_desc(buffer, CONFLICT_TYPE_PNP_COLD); + pnp_print_conflict_node(buffer, cdev); + } +} + static ssize_t pnp_show_current_resources(struct device *dmdev, char *buf) { struct pnp_dev *dev = to_pnp_dev(dmdev); - char *str = buf; - int i; + int i, ret; + pnp_info_buffer_t *buffer = (pnp_info_buffer_t *) + pnp_alloc(sizeof(pnp_info_buffer_t)); + if (!buffer) + return -ENOMEM; + if (!dev) + return -EINVAL; + buffer->len = PAGE_SIZE; + buffer->buffer = buf; + buffer->curr = buffer->buffer; - if (!dev->active){ - str += sprintf(str,"DISABLED\n"); - goto done; - } - for (i = 0; i < DEVICE_COUNT_IO; i++) { + pnp_printf(buffer,"state = "); + if (dev->active) + pnp_printf(buffer,"active\n"); + else + pnp_printf(buffer,"disabled\n"); + 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", + pnp_printf(buffer,"io"); + pnp_printf(buffer," 0x%lx-0x%lx \n", pnp_port_start(dev, i), pnp_port_end(dev, i)); + pnp_print_conflict(buffer, dev, i, IORESOURCE_IO); } } - 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", + pnp_printf(buffer,"mem"); + pnp_printf(buffer," 0x%lx-0x%lx \n", pnp_mem_start(dev, i), pnp_mem_end(dev, i)); + pnp_print_conflict(buffer, dev, i, IORESOURCE_MEM); } } - 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)); + pnp_printf(buffer,"irq"); + pnp_printf(buffer," %ld \n", pnp_irq(dev, i)); + pnp_print_conflict(buffer, dev, i, IORESOURCE_IRQ); } } - 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)); + pnp_printf(buffer,"dma"); + pnp_printf(buffer," %ld \n", pnp_dma(dev, i)); + pnp_print_conflict(buffer, dev, i, IORESOURCE_DMA); } } - done: - return (str - buf); + ret = (buffer->curr - buf); + kfree(buffer); + return ret; } +extern int pnp_resolve_conflicts(struct pnp_dev *dev); + static ssize_t -pnp_set_current_resources(struct device * dmdev, const char * buf, size_t count) +pnp_set_current_resources(struct device * dmdev, const char * ubuf, size_t count) { struct pnp_dev *dev = to_pnp_dev(dmdev); - char command[20]; - int num_args; - int error = 0; - int depnum; + char *buf = (void *)ubuf; + int retval = 0; - num_args = sscanf(buf,"%10s %i",command,&depnum); - if (!num_args) + while (isspace(*buf)) + ++buf; + if (!strnicmp(buf,"disable",7)) { + retval = pnp_disable_dev(dev); goto done; - if (!strnicmp(command,"lock",4)) { - if (dev->active) { - dev->lock_resources = 1; - } else { - error = -EINVAL; - } + } + if (!strnicmp(buf,"activate",8)) { + retval = pnp_activate_dev(dev); goto done; } - if (!strnicmp(command,"unlock",6)) { - if (dev->lock_resources) { - dev->lock_resources = 0; - } else { - error = -EINVAL; - } + if (!strnicmp(buf,"reset",5)) { + if (!dev->active) + goto done; + retval = pnp_disable_dev(dev); + if (retval) + goto done; + retval = pnp_activate_dev(dev); goto done; } - if (!strnicmp(command,"disable",7)) { - error = pnp_disable_dev(dev); + if (!strnicmp(buf,"auto-config",11)) { + if (dev->active) + goto done; + retval = pnp_auto_config_dev(dev); goto done; } - if (!strnicmp(command,"auto",4)) { - error = pnp_activate_dev(dev,NULL); + if (!strnicmp(buf,"clear-config",12)) { + if (dev->active) + goto done; + spin_lock(&pnp_lock); + dev->config_mode = PNP_CONFIG_MANUAL; + pnp_init_resource_table(&dev->res); + if (dev->rule) + dev->rule->depnum = 0; + spin_unlock(&pnp_lock); goto done; } - if (!strnicmp(command,"manual",6)) { - if (num_args != 2) + if (!strnicmp(buf,"resolve",7)) { + retval = pnp_resolve_conflicts(dev); + goto done; + } + if (!strnicmp(buf,"get",3)) { + spin_lock(&pnp_lock); + if (pnp_can_read(dev)) + dev->protocol->get(dev, &dev->res); + spin_unlock(&pnp_lock); + goto done; + } + if (!strnicmp(buf,"set",3)) { + if (dev->active) goto done; - error = pnp_raw_set_dev(dev,depnum,NULL); + buf += 3; + struct pnp_resource_table res; + int nport = 0, nmem = 0, nirq = 0, ndma = 0; + pnp_init_resource_table(&res); + while (1) { + while (isspace(*buf)) + ++buf; + if (!strnicmp(buf,"io",2)) { + buf += 2; + while (isspace(*buf)) + ++buf; + res.port_resource[nport].start = simple_strtoul(buf,&buf,0); + while (isspace(*buf)) + ++buf; + if(*buf == '-') { + buf += 1; + while (isspace(*buf)) + ++buf; + res.port_resource[nport].end = simple_strtoul(buf,&buf,0); + } else + res.port_resource[nport].end = res.port_resource[nport].start; + res.port_resource[nport].flags = IORESOURCE_IO; + nport++; + if (nport >= PNP_MAX_PORT) + break; + continue; + } + if (!strnicmp(buf,"mem",3)) { + buf += 3; + while (isspace(*buf)) + ++buf; + res.mem_resource[nmem].start = simple_strtoul(buf,&buf,0); + while (isspace(*buf)) + ++buf; + if(*buf == '-') { + buf += 1; + while (isspace(*buf)) + ++buf; + res.mem_resource[nmem].end = simple_strtoul(buf,&buf,0); + } else + res.mem_resource[nmem].end = res.mem_resource[nmem].start; + res.mem_resource[nmem].flags = IORESOURCE_MEM; + nmem++; + if (nmem >= PNP_MAX_MEM) + break; + continue; + } + if (!strnicmp(buf,"irq",3)) { + buf += 3; + while (isspace(*buf)) + ++buf; + res.irq_resource[nirq].start = + res.irq_resource[nirq].end = simple_strtoul(buf,&buf,0); + res.irq_resource[nirq].flags = IORESOURCE_IRQ; + nirq++; + if (nirq >= PNP_MAX_IRQ) + break; + continue; + } + if (!strnicmp(buf,"dma",3)) { + buf += 3; + while (isspace(*buf)) + ++buf; + res.dma_resource[ndma].start = + res.dma_resource[ndma].end = simple_strtoul(buf,&buf,0); + res.dma_resource[ndma].flags = IORESOURCE_DMA; + ndma++; + if (ndma >= PNP_MAX_DMA) + break; + continue; + } + break; + } + spin_lock(&pnp_lock); + dev->config_mode = PNP_CONFIG_MANUAL; + dev->res = res; + spin_unlock(&pnp_lock); goto done; } done: - return error < 0 ? error : count; + if (retval) + return retval; + return count; } static DEVICE_ATTR(resources,S_IRUGO | S_IWUSR, diff --git a/drivers/pnp/isapnp/core.c b/drivers/pnp/isapnp/core.c index ec79ed4727c8171cf5825b70d4cc15a793bd4ce5..eb30153aae85338aea4d1dca89cd218f92900024 100644 --- a/drivers/pnp/isapnp/core.c +++ b/drivers/pnp/isapnp/core.c @@ -101,7 +101,6 @@ static int isapnp_detected; /* 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) @@ -260,7 +259,7 @@ static int isapnp_next_rdp(void) * 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; @@ -580,14 +579,18 @@ static void __init isapnp_add_mem32_resource(struct pnp_dev *dev, int depnum, int size) { unsigned char tmp[17]; - struct pnp_mem32 *mem32; + struct pnp_mem *mem; isapnp_peek(tmp, size); - mem32 = isapnp_alloc(sizeof(struct pnp_mem32)); - if (!mem32) + mem = isapnp_alloc(sizeof(struct pnp_mem)); + if (!mem) return; - memcpy(mem32->data, tmp, 17); - pnp_add_mem32_resource(dev,depnum,mem32); + mem->min = (tmp[4] << 24) | (tmp[3] << 16) | (tmp[2] << 8) | tmp[1]; + mem->max = (tmp[8] << 24) | (tmp[7] << 16) | (tmp[6] << 8) | tmp[5]; + mem->align = (tmp[12] << 24) | (tmp[11] << 16) | (tmp[10] << 8) | tmp[9]; + mem->size = (tmp[16] << 24) | (tmp[15] << 16) | (tmp[14] << 8) | tmp[13]; + mem->flags = tmp[0]; + pnp_add_mem_resource(dev,depnum,mem); } /* @@ -597,15 +600,18 @@ static void __init isapnp_add_mem32_resource(struct pnp_dev *dev, static void __init isapnp_add_fixed_mem32_resource(struct pnp_dev *dev, int depnum, int size) { - unsigned char tmp[17]; - struct pnp_mem32 *mem32; + unsigned char tmp[9]; + struct pnp_mem *mem; isapnp_peek(tmp, size); - mem32 = isapnp_alloc(sizeof(struct pnp_mem32)); - if (!mem32) + mem = isapnp_alloc(sizeof(struct pnp_mem)); + if (!mem) return; - memcpy(mem32->data, tmp, 17); - pnp_add_mem32_resource(dev,depnum,mem32); + mem->min = mem->max = (tmp[4] << 24) | (tmp[3] << 16) | (tmp[2] << 8) | tmp[1]; + mem->size = (tmp[8] << 24) | (tmp[7] << 16) | (tmp[6] << 8) | tmp[5]; + mem->align = 0; + mem->flags = tmp[0]; + pnp_add_mem_resource(dev,depnum,mem); } /* @@ -650,7 +656,6 @@ static int __init isapnp_create_device(struct pnp_card *card, 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 +728,7 @@ static int __init isapnp_create_device(struct pnp_card *card, 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 */ @@ -738,7 +743,7 @@ static int __init isapnp_create_device(struct pnp_card *card, size = 0; break; case _LTAG_FIXEDMEM32RANGE: - if (size != 17) + if (size != 9) goto __skip; isapnp_add_fixed_mem32_resource(dev, depnum, size); size = 0; @@ -746,7 +751,6 @@ static int __init isapnp_create_device(struct pnp_card *card, 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,14 +759,13 @@ static int __init isapnp_create_device(struct pnp_card *card, if (size > 0) isapnp_skip_bytes(size); } - isapnp_config_prepare(dev); return 0; } /* * Parse resource map for ISA PnP card. */ - + static void __init isapnp_parse_resource_map(struct pnp_card *card) { unsigned char type, tmp[17]; @@ -790,7 +793,7 @@ static void __init isapnp_parse_resource_map(struct pnp_card *card) 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 */ @@ -819,7 +822,7 @@ static unsigned char __init isapnp_checksum(unsigned char *data) { int i, j; unsigned char checksum = 0x6a, bit, b; - + for (i = 0; i < 8; i++) { b = data[i]; for (j = 0; j < 8; j++) { @@ -852,6 +855,63 @@ static void isapnp_parse_card_id(struct pnp_card * card, unsigned short vendor, 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. */ @@ -861,6 +921,7 @@ static int __init isapnp_build_device_list(void) int csn; unsigned char header[9], checksum; struct pnp_card *card; + struct pnp_dev *dev; isapnp_wait(); isapnp_key(); @@ -893,8 +954,17 @@ static int __init isapnp_build_device_list(void) 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; } @@ -948,39 +1018,6 @@ int isapnp_cfg_end(void) 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. @@ -999,44 +1036,35 @@ EXPORT_SYMBOL(isapnp_write_dword); EXPORT_SYMBOL(isapnp_wake); EXPORT_SYMBOL(isapnp_device); -static int isapnp_get_resources(struct pnp_dev *dev) +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; - 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_cfg *cfg) +static int isapnp_set_resources(struct pnp_dev *dev, struct pnp_resource_table * res) { 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++) { - int irq = pnp_irq(dev, 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 < 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 && pnp_dma_valid(dev, tmp); tmp++) - isapnp_write_byte(ISAPNP_CFG_DMA+tmp, pnp_dma(dev, tmp)); - for (tmp = 0; tmp < 4 && pnp_mem_valid(dev, tmp); tmp++) - isapnp_write_word(ISAPNP_CFG_MEM+(tmp<<2), (pnp_mem_start(dev, tmp) >> 8) & 0xffff); + 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 < 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; @@ -1046,7 +1074,7 @@ static int isapnp_disable_resources(struct pnp_dev *dev) { 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(); @@ -1127,11 +1155,11 @@ int __init isapnp_init(void) 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"); } } } @@ -1145,7 +1173,7 @@ int __init isapnp_init(void) return 0; } -subsys_initcall(isapnp_init); +device_initcall(isapnp_init); /* format is: noisapnp */ diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c new file mode 100644 index 0000000000000000000000000000000000000000..90e70be2a3a2929b0f1cc834c089f104aa854d93 --- /dev/null +++ b/drivers/pnp/manager.c @@ -0,0 +1,711 @@ +/* + * manager.c - Resource Management, Conflict Resolution, Activation and Disabling of Devices + * + * Copyright 2003 Adam Belay <ambx1@neo.rr.com> + * + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> + +#ifdef CONFIG_PNP_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include <linux/pnp.h> +#include "base.h" + + +int pnp_max_moves = 4; + + +static int pnp_next_port(struct pnp_dev * dev, int idx) +{ + struct pnp_port *port; + unsigned long *start, *end, *flags; + if (!dev || idx < 0 || idx >= PNP_MAX_PORT) + return 0; + port = dev->rule->port[idx]; + if (!port) + return 1; + + start = &dev->res.port_resource[idx].start; + end = &dev->res.port_resource[idx].end; + flags = &dev->res.port_resource[idx].flags; + + /* set the initial values if this is the first time */ + if (*start == 0) { + *start = port->min; + *end = *start + port->size - 1; + *flags = port->flags | IORESOURCE_IO; + if (!pnp_check_port(dev, idx)) + return 1; + } + + /* run through until pnp_check_port is happy */ + do { + *start += port->align; + *end = *start + port->size - 1; + if (*start > port->max || !port->align) + return 0; + } while (pnp_check_port(dev, idx)); + return 1; +} + +static int pnp_next_mem(struct pnp_dev * dev, int idx) +{ + struct pnp_mem *mem; + unsigned long *start, *end, *flags; + if (!dev || idx < 0 || idx >= PNP_MAX_MEM) + return 0; + mem = dev->rule->mem[idx]; + if (!mem) + return 1; + + start = &dev->res.mem_resource[idx].start; + end = &dev->res.mem_resource[idx].end; + flags = &dev->res.mem_resource[idx].flags; + + /* set the initial values if this is the first time */ + if (*start == 0) { + *start = mem->min; + *end = *start + mem->size -1; + *flags = mem->flags | IORESOURCE_MEM; + if (!(mem->flags & IORESOURCE_MEM_WRITEABLE)) + *flags |= IORESOURCE_READONLY; + if (mem->flags & IORESOURCE_MEM_CACHEABLE) + *flags |= IORESOURCE_CACHEABLE; + if (mem->flags & IORESOURCE_MEM_RANGELENGTH) + *flags |= IORESOURCE_RANGELENGTH; + if (mem->flags & IORESOURCE_MEM_SHADOWABLE) + *flags |= IORESOURCE_SHADOWABLE; + if (!pnp_check_mem(dev, idx)) + return 1; + } + + /* run through until pnp_check_mem is happy */ + do { + *start += mem->align; + *end = *start + mem->size - 1; + if (*start > mem->max || !mem->align) + return 0; + } while (pnp_check_mem(dev, idx)); + return 1; +} + +static int pnp_next_irq(struct pnp_dev * dev, int idx) +{ + struct pnp_irq *irq; + unsigned long *start, *end, *flags; + int i, mask; + if (!dev || idx < 0 || idx >= PNP_MAX_IRQ) + return 0; + irq = dev->rule->irq[idx]; + if (!irq) + return 1; + + start = &dev->res.irq_resource[idx].start; + end = &dev->res.irq_resource[idx].end; + flags = &dev->res.irq_resource[idx].flags; + + /* set the initial values if this is the first time */ + if (*start == -1) { + *start = *end = 0; + *flags = irq->flags | IORESOURCE_IRQ; + if (!pnp_check_irq(dev, idx)) + return 1; + } + + mask = irq->map; + for (i = *start + 1; i < 16; i++) + { + if(mask>>i & 0x01) { + *start = *end = i; + if(!pnp_check_irq(dev, idx)) + return 1; + } + } + return 0; +} + +static int pnp_next_dma(struct pnp_dev * dev, int idx) +{ + struct pnp_dma *dma; + unsigned long *start, *end, *flags; + int i, mask; + if (!dev || idx < 0 || idx >= PNP_MAX_DMA) + return -EINVAL; + dma = dev->rule->dma[idx]; + if (!dma) + return 1; + + start = &dev->res.dma_resource[idx].start; + end = &dev->res.dma_resource[idx].end; + flags = &dev->res.dma_resource[idx].flags; + + /* set the initial values if this is the first time */ + if (*start == -1) { + *start = *end = 0; + *flags = dma->flags | IORESOURCE_DMA; + if (!pnp_check_dma(dev, idx)) + return 1; + } + + mask = dma->map; + for (i = *start + 1; i < 8; i++) + { + if(mask>>i & 0x01) { + *start = *end = i; + if(!pnp_check_dma(dev, idx)) + return 1; + } + } + return 0; +} + +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) + return 0; + + if (max == 0) { + if (pnp_generate_rule(dev, 0, dev->rule)) { + dev->rule->depnum = -1; + return 1; + } + } + + 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)) { + dev->rule->depnum = depnum; + return 1; + } + } + } + } + return 0; +} + +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) +{ + 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 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); + } +} + +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; + 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 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 + || pnp_check_port_conflicts(dev,i,SEARCH_WARM)) { + 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 + || pnp_check_mem_conflicts(dev,i,SEARCH_WARM)) { + 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 + || pnp_check_irq_conflicts(dev,i,SEARCH_WARM)) { + 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 + || pnp_check_dma_conflicts(dev,i,SEARCH_WARM)) { + 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; + move--; + if (!dev->rule) + return 0; + change = pnp_add_change(parent,dev); + if (!change) + return 0; + 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; +} + +/* 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 */ + if (!pnp_can_configure(dev)) + return 1; + if (!dev->rule) { + dev->rule = pnp_alloc(sizeof(struct pnp_rule_table)); + if (!dev->rule) + return -ENOMEM; + } + + spin_lock(&pnp_lock); + 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)) { + spin_unlock(&pnp_lock); + return 1; + } + } + + pnp_init_resource_table(&dev->res); + dev->rule->depnum = 0; + 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; +} + +int pnp_resolve_conflicts(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_advanced_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_advanced_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_advanced_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_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; + } + if (!dev->rule) { + dev->rule = pnp_alloc(sizeof(struct pnp_rule_table)); + if (!dev->rule) { + spin_unlock(&pnp_lock); + return -ENOMEM; + } + } + dev->rule->depnum = 0; + pnp_init_resource_table(&dev->res); + 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; + for (idx = 0; idx < PNP_MAX_IRQ; idx++) { + if (resa->irq_resource[idx].start != resb->irq_resource[idx].start) + return 1; + } + for (idx = 0; idx < PNP_MAX_DMA; idx++) { + if (resa->dma_resource[idx].start != resb->dma_resource[idx].start) + return 1; + } + for (idx = 0; idx < PNP_MAX_PORT; idx++) { + if (resa->port_resource[idx].start != resb->port_resource[idx].start) + return 1; + if (resa->port_resource[idx].end != resb->port_resource[idx].end) + return 1; + } + for (idx = 0; idx < PNP_MAX_MEM; idx++) { + if (resa->mem_resource[idx].start != resb->mem_resource[idx].start) + return 1; + if (resa->mem_resource[idx].end != resb->mem_resource[idx].end) + return 1; + } + return 0; +} + + +/* + * PnP Device Resource Management + */ + +/** + * 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_advanced_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 + * + * 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) +{ + 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); + + pnp_resolve_conflicts(dev); + dev->config_mode = PNP_CONFIG_MANUAL; + return 0; + +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 + * + * finds the best resource configuration and then informs the correct pnp protocol + */ + +int pnp_activate_dev(struct pnp_dev *dev) +{ + if (!dev) + return -EINVAL; + if (dev->active) { + pnp_info("res: The PnP device '%s' is already active.", dev->dev.bus_id); + return -EBUSY; + } + 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)) { + if (!pnp_simple_config(dev)) { + pnp_err("res: Unable to resolve resource conflicts for the device '%s'.", dev->dev.bus_id); + goto fail; + } + } + 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); + 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); + 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); + 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); + goto fail; + } + if (pnp_can_read(dev)) { + struct pnp_resource_table res; + dev->protocol->get(dev, &res); + 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'.", dev->dev.bus_id); + } else + dev->active = pnp_is_active(dev); + 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; +} + +/** + * pnp_disable_dev - disables device + * @dev: pointer to the desired device + * + * inform the correct pnp protocol so that resources can be used by other devices + */ + +int pnp_disable_dev(struct pnp_dev *dev) +{ + if (!dev) + return -EINVAL; + if (!dev->active) { + pnp_info("res: The PnP device '%s' is already disabled.", dev->dev.bus_id); + return -EINVAL; + } + if (dev->status != PNP_READY){ + pnp_info("res: Disable failed becuase the PnP device '%s' is busy.", dev->dev.bus_id); + return -EINVAL; + } + 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)<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; + } + 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; +} + +/** + * 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_auto_config_dev); +EXPORT_SYMBOL(pnp_manual_config_dev); +EXPORT_SYMBOL(pnp_activate_dev); +EXPORT_SYMBOL(pnp_disable_dev); +EXPORT_SYMBOL(pnp_resource_change); + + +/* 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); diff --git a/drivers/pnp/names.c b/drivers/pnp/names.c index 297f4c30899080d30433a6799fd12e2ebeeefde1..bdef0ea165392f992cb930a6c48fda3f9ed7d86e 100644 --- a/drivers/pnp/names.c +++ b/drivers/pnp/names.c @@ -30,10 +30,10 @@ void 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]); + snprintf(name, DEVICE_NAME_SIZE, "%s", pnp_id_names[i]); return; } } diff --git a/drivers/pnp/pnpbios/core.c b/drivers/pnp/pnpbios/core.c index cde17aa6823bb23394fc46b440e82a68a1c4786b..69176c1ef8751cffb2875baa2ec8fe44c64ce942 100644 --- a/drivers/pnp/pnpbios/core.c +++ b/drivers/pnp/pnpbios/core.c @@ -142,11 +142,13 @@ set_base(cpu_gdt_table[cpu][(selname) >> 3], (u32)(address)); \ 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 @@ static inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3, { 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 @@ static inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3, 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 @@ static inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3, : "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,6 +247,8 @@ void *pnpbios_kmalloc(size_t size, int f) void *p = kmalloc( size, f ); if ( p == NULL ) printk(KERN_ERR "PnPBIOS: kmalloc() failed\n"); + else + memset(p, 0, size); return p; } @@ -664,381 +677,6 @@ static int pnp_dock_thread(void * unused) #endif /* CONFIG_HOTPLUG */ - -/* pnp current resource reading functions */ - - -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 - } -} - -static void add_dmaresource(struct pnp_dev *dev, int dma) -{ - 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 - } -} - -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 - } -} - -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 - } -} - -static unsigned char *node_current_resource_data_to_dev(struct pnp_bios_node *node, struct pnp_dev *dev) -{ - unsigned char *p = node->data, *lastp=NULL; - int i; - - /* - * 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<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<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<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; - } - - /* - * Fill in dev resource info - */ - while ( (char *)p < ((char *)node->data + node->size )) { - if(p==lastp) break; - - if( p[0] & 0x80 ) {// large item - switch (p[0] & 0x7f) { - case 0x01: // memory - { - int io = *(short *) &p[4]; - int len = *(short *) &p[10]; - add_memresource(dev, io, len); - break; - } - case 0x02: // device name - { - int len = *(short *) &p[1]; - memcpy(dev->name, p + 3, len >= 80 ? 79 : len); - break; - } - case 0x05: // 32-bit memory - { - int io = *(int *) &p[4]; - int len = *(int *) &p[16]; - add_memresource(dev, io, len); - break; - } - case 0x06: // fixed location 32-bit memory - { - int io = *(int *) &p[4]; - int len = *(int *) &p[8]; - add_memresource(dev, io, len); - break; - } - } /* switch */ - lastp = p+3; - p = p + p[1] + p[2]*256 + 3; - continue; - } - if ((p[0]>>3) == 0x0f){ // end tag - p = p + 2; - goto end; - break; - } - switch (p[0]>>3) { - case 0x04: // irq - { - int i, mask, irq = -1; - mask= p[1] + p[2]*256; - for (i=0;i<16;i++, mask=mask>>1) - if(mask & 0x01) irq=i; - add_irqresource(dev, irq); - break; - } - case 0x05: // dma - { - int i, mask, dma = -1; - mask = p[1]; - for (i=0;i<8;i++, mask = mask>>1) - if(mask & 0x01) dma=i; - add_dmaresource(dev, dma); - break; - } - case 0x08: // io - { - int io= p[2] + p[3] *256; - int len = p[7]; - add_ioresource(dev, io, len); - break; - } - case 0x09: // fixed location io - { - int io = p[1] + p[2] * 256; - int len = p[3]; - add_ioresource(dev, io, len); - break; - } - } /* switch */ - lastp=p+1; - p = p + (p[0] & 0x07) + 1; - - } /* 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) - dev->active = 0; - else - dev->active = 1; - return (unsigned char *)p; -} - - -/* pnp possible resource reading functions */ - -static void read_lgtag_mem(unsigned char *p, int size, int depnum, struct pnp_dev *dev) -{ - struct pnp_mem * mem; - mem = pnpbios_kmalloc(sizeof(struct pnp_mem),GFP_KERNEL); - if (!mem) - return; - memset(mem,0,sizeof(struct pnp_mem)); - mem->min = ((p[3] << 8) | p[2]) << 8; - mem->max = ((p[5] << 8) | p[4]) << 8; - mem->align = (p[7] << 8) | p[6]; - mem->size = ((p[9] << 8) | p[8]) << 8; - mem->flags = p[1]; - pnp_add_mem_resource(dev,depnum,mem); - return; -} - -static void read_lgtag_mem32(unsigned char *p, int size, int depnum, struct pnp_dev *dev) -{ - struct pnp_mem32 * mem; - mem = pnpbios_kmalloc(sizeof(struct pnp_mem32),GFP_KERNEL); - if (!mem) - return; - memset(mem,0,sizeof(struct pnp_mem32)); - memcpy(mem->data, p, 17); - pnp_add_mem32_resource(dev,depnum,mem); - return; -} - -static void read_lgtag_fmem32(unsigned char *p, int size, int depnum, struct pnp_dev *dev) -{ - struct pnp_mem32 * mem; - mem = pnpbios_kmalloc(sizeof(struct pnp_mem32),GFP_KERNEL); - if (!mem) - return; - memset(mem,0,sizeof(struct pnp_mem32)); - memcpy(mem->data, p, 17); - pnp_add_mem32_resource(dev,depnum,mem); - return; -} - -static void read_smtag_irq(unsigned char *p, int size, int depnum, struct pnp_dev *dev) -{ - struct pnp_irq * irq; - irq = pnpbios_kmalloc(sizeof(struct pnp_irq),GFP_KERNEL); - if (!irq) - return; - memset(irq,0,sizeof(struct pnp_irq)); - irq->map = (p[2] << 8) | p[1]; - if (size > 2) - irq->flags = p[3]; - pnp_add_irq_resource(dev,depnum,irq); - return; -} - -static void read_smtag_dma(unsigned char *p, int size, int depnum, struct pnp_dev *dev) -{ - struct pnp_dma * dma; - dma = pnpbios_kmalloc(sizeof(struct pnp_dma),GFP_KERNEL); - if (!dma) - return; - memset(dma,0,sizeof(struct pnp_dma)); - dma->map = p[1]; - dma->flags = p[2]; - pnp_add_dma_resource(dev,depnum,dma); - return; -} - -static void read_smtag_port(unsigned char *p, int size, int depnum, struct pnp_dev *dev) -{ - struct pnp_port * port; - port = pnpbios_kmalloc(sizeof(struct pnp_port),GFP_KERNEL); - if (!port) - return; - memset(port,0,sizeof(struct pnp_port)); - port->min = (p[3] << 8) | p[2]; - port->max = (p[5] << 8) | p[4]; - port->align = p[6]; - port->size = p[7]; - port->flags = p[1] ? PNP_PORT_FLAG_16BITADDR : 0; - pnp_add_port_resource(dev,depnum,port); - return; -} - -static void read_smtag_fport(unsigned char *p, int size, int depnum, struct pnp_dev *dev) -{ - struct pnp_port * port; - port = pnpbios_kmalloc(sizeof(struct pnp_port),GFP_KERNEL); - if (!port) - return; - memset(port,0,sizeof(struct pnp_port)); - port->min = port->max = (p[2] << 8) | p[1]; - port->size = p[3]; - port->align = 0; - port->flags = PNP_PORT_FLAG_FIXED; - pnp_add_port_resource(dev,depnum,port); - return; -} - -static unsigned char *node_possible_resource_data_to_dev(unsigned char *p, struct pnp_bios_node *node, struct pnp_dev *dev) -{ - int len, depnum, dependent; - - if ((char *)p == NULL) - return NULL; - if (pnp_build_resource(dev, 0) == NULL) - return NULL; - depnum = 0; /*this is the first so it should be 0 */ - dependent = 0; - while ( (char *)p < ((char *)node->data + node->size )) { - - if( p[0] & 0x80 ) {// large item - len = (p[2] << 8) | p[1]; - switch (p[0] & 0x7f) { - case 0x01: // memory - { - if (len != 9) - goto __skip; - read_lgtag_mem(p,len,depnum,dev); - break; - } - case 0x05: // 32-bit memory - { - if (len != 17) - goto __skip; - read_lgtag_mem32(p,len,depnum,dev); - break; - } - case 0x06: // fixed location 32-bit memory - { - if (len != 17) - goto __skip; - read_lgtag_fmem32(p,len,depnum,dev); - break; - } - } /* switch */ - p += len + 3; - continue; - } - len = p[0] & 0x07; - switch ((p[0]>>3) & 0x0f) { - case 0x0f: - { - p = p + 2; - return (unsigned char *)p; - break; - } - case 0x04: // irq - { - if (len < 2 || len > 3) - goto __skip; - read_smtag_irq(p,len,depnum,dev); - break; - } - case 0x05: // dma - { - if (len != 2) - goto __skip; - read_smtag_dma(p,len,depnum,dev); - break; - } - case 0x06: // start dep - { - if (len > 1) - goto __skip; - dependent = 0x100 | PNP_RES_PRIORITY_ACCEPTABLE; - if (len > 0) - dependent = 0x100 | p[1]; - pnp_build_resource(dev,dependent); - depnum = pnp_get_max_depnum(dev); - break; - } - case 0x07: // end dep - { - if (len != 0) - goto __skip; - depnum = 0; - break; - } - case 0x08: // io - { - if (len != 7) - goto __skip; - read_smtag_port(p,len,depnum,dev); - break; - } - case 0x09: // fixed location io - { - if (len != 3) - goto __skip; - read_smtag_fport(p,len,depnum,dev); - break; - } - } /* switch */ - __skip: - p += len + 1; - - } /* while */ - - return NULL; -} - /* pnp EISA ids */ #define HEX(id,a) hex[((id)>>a) & 15] @@ -1075,20 +713,26 @@ static void node_id_data_to_dev(unsigned char *p, struct pnp_bios_node *node, st return; while ( (char *)p < ((char *)node->data + node->size )) { - if( p[0] & 0x80 ) {// large item + if( p[0] & 0x80 ) { len = (p[2] << 8) | p[1]; - p += len + 3; - continue; - } + if ((p[0] & 0x7f) == 0x02) /* human readable name */ + { + int size = *(short *) &p[1]; + memcpy(dev->dev.name, p + 3, len >= 80 ? 79 : size); + break; + } + p += len + 3; + continue; + } len = p[0] & 0x07; - switch ((p[0]>>3) & 0x0f) { - case 0x0f: + switch ((p[0]>>3) & 0x0f) { + case 0x0f: /* end tag */ { return; break; } - case 0x03: // compatible ID - { + case 0x03: /* compatible ID */ + { if (len != 4) goto __skip; dev_id = pnpbios_kmalloc(sizeof (struct pnp_id), GFP_KERNEL); @@ -1099,177 +743,20 @@ static void node_id_data_to_dev(unsigned char *p, struct pnp_bios_node *node, st memcpy(&dev_id->id, id, 7); pnp_add_id(dev_id, dev); break; - } - } /* switch */ - __skip: - p += len + 1; - - } /* while */ -} - -/* pnp resource writing functions */ - -static void write_lgtag_mem(unsigned char *p, int size, struct pnp_mem *mem) -{ - if (!mem) - return; - p[2] = (mem->min >> 8) & 0xff; - p[3] = ((mem->min >> 8) >> 8) & 0xff; - p[4] = (mem->max >> 8) & 0xff; - p[5] = ((mem->max >> 8) >> 8) & 0xff; - p[6] = mem->align & 0xff; - p[7] = (mem->align >> 8) & 0xff; - p[8] = (mem->size >> 8) & 0xff; - p[9] = ((mem->size >> 8) >> 8) & 0xff; - p[1] = mem->flags & 0xff; - return; -} - -static void write_smtag_irq(unsigned char *p, int size, struct pnp_irq *irq) -{ - if (!irq) - return; - p[1] = irq->map & 0xff; - p[2] = (irq->map >> 8) & 0xff; - if (size > 2) - p[3] = irq->flags & 0xff; - return; -} - -static void write_smtag_dma(unsigned char *p, int size, struct pnp_dma *dma) -{ - if (!dma) - return; - p[1] = dma->map & 0xff; - p[2] = dma->flags & 0xff; - return; -} - -static void write_smtag_port(unsigned char *p, int size, struct pnp_port *port) -{ - if (!port) - return; - p[2] = port->min & 0xff; - p[3] = (port->min >> 8) & 0xff; - p[4] = port->max & 0xff; - p[5] = (port->max >> 8) & 0xff; - p[6] = port->align & 0xff; - p[7] = port->size & 0xff; - p[1] = port->flags & 0xff; - return; -} - -static void write_smtag_fport(unsigned char *p, int size, struct pnp_port *port) -{ - if (!port) - return; - p[1] = port->min & 0xff; - p[2] = (port->min >> 8) & 0xff; - p[3] = port->size & 0xff; - return; -} - -static int node_set_resources(struct pnp_bios_node *node, struct pnp_cfg *config) -{ - int error = 0; - unsigned char *p = (char *)node->data, *lastp = NULL; - int len, port = 0, irq = 0, dma = 0, mem = 0; - - if (!node) - return -EINVAL; - if ((char *)p == NULL) - return -EINVAL; - while ( (char *)p < ((char *)node->data + node->size )) { - - if( p[0] & 0x80 ) {// large item - len = (p[2] << 8) | p[1]; - switch (p[0] & 0x7f) { - case 0x01: // memory - { - if (len != 9) - goto __skip; - write_lgtag_mem(p,len,config->mem[mem]); - mem++; - break; - } - case 0x05: // 32-bit memory - { - if (len != 17) - goto __skip; - /* FIXME */ - break; - } - case 0x06: // fixed location 32-bit memory - { - if (len != 17) - goto __skip; - /* FIXME */ - break; - } - } /* switch */ - lastp = p+3; - p = p + p[1] + p[2]*256 + 3; - continue; - } - len = p[0] & 0x07; - switch ((p[0]>>3) & 0x0f) { - case 0x0f: - { - goto done; - break; } - case 0x04: // irq - { - if (len < 2 || len > 3) - goto __skip; - write_smtag_irq(p,len,config->irq[irq]); - irq++; - break; - } - case 0x05: // dma - { - if (len != 2) - goto __skip; - write_smtag_dma(p,len,config->dma[dma]); - dma++; - break; - } - case 0x08: // io - { - if (len != 7) - goto __skip; - write_smtag_port(p,len,config->port[port]); - port++; - break; - } - case 0x09: // fixed location io - { - if (len != 3) - goto __skip; - write_smtag_fport(p,len,config->port[port]); - port++; - break; } - } /* switch */ __skip: - p += len + 1; - - } /* while */ + p += len + 1; - /* we never got an end tag so this data is corrupt or invalid */ - return -EINVAL; - - done: - error = pnp_bios_set_dev_node(node->handle, (char)0, node); - return error; + } } -static int pnpbios_get_resources(struct pnp_dev *dev) +static int pnpbios_get_resources(struct pnp_dev * dev, struct pnp_resource_table * res) { struct pnp_dev_node_info node_info; u8 nodenum = dev->number; struct pnp_bios_node * node; - + /* just in case */ if(!pnpbios_is_dynamic(dev)) return -EPERM; @@ -1278,18 +765,22 @@ static int pnpbios_get_resources(struct pnp_dev *dev) node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL); if (!node) return -1; - if (pnp_bios_get_dev_node(&nodenum, (char )0, node)) + if (pnp_bios_get_dev_node(&nodenum, (char )0, node)) { + kfree(node); return -ENODEV; - node_current_resource_data_to_dev(node,dev); + } + pnp_parse_current_resources((char *)node->data,(char *)node->data + node->size,res); + dev->active = pnp_is_active(dev); kfree(node); 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_resource_table * res) { struct pnp_dev_node_info node_info; u8 nodenum = dev->number; struct pnp_bios_node * node; + int ret; /* just in case */ if (!pnpbios_is_dynamic(dev)) @@ -1301,83 +792,42 @@ static int pnpbios_set_resources(struct pnp_dev *dev, struct pnp_cfg *config) return -1; if (pnp_bios_get_dev_node(&nodenum, (char )1, node)) return -ENODEV; - if(node_set_resources(node, config)<0){ + if(!pnp_write_resources((char *)node->data,(char *)node->data + node->size,res)){ + kfree(node); return -1; } + ret = pnp_bios_set_dev_node(node->handle, (char)0, node); kfree(node); - return 0; + if (ret > 0) + ret = -1; + return ret; } static int pnpbios_disable_resources(struct pnp_dev *dev) { - struct pnp_cfg * config = kmalloc(sizeof(struct pnp_cfg), GFP_KERNEL); - /* first we need to set everything to a disabled value */ - struct pnp_port port = { - .max = 0, - .min = 0, - .align = 0, - .size = 0, - .flags = 0, - .pad = 0, - }; - struct pnp_mem mem = { - .max = 0, - .min = 0, - .align = 0, - .size = 0, - .flags = 0, - .pad = 0, - }; - struct pnp_dma dma = { - .map = 0, - .flags = 0, - }; - struct pnp_irq irq = { - .map = 0, - .flags = 0, - .pad = 0, - }; - int i; struct pnp_dev_node_info node_info; - u8 nodenum = dev->number; struct pnp_bios_node * node; - if (!config) - return -1; + int ret; + /* just in case */ if(dev->flags & PNPBIOS_NO_DISABLE || !pnpbios_is_dynamic(dev)) return -EPERM; - memset(config, 0, sizeof(struct pnp_cfg)); if (!dev || !dev->active) return -EINVAL; - for (i=0; i < 8; i++) - config->port[i] = &port; - for (i=0; i < 4; i++) - config->mem[i] = &mem; - for (i=0; i < 2; i++) - config->irq[i] = &irq; - for (i=0; i < 2; i++) - config->dma[i] = &dma; - dev->active = 0; - if (pnp_bios_dev_node_info(&node_info) != 0) return -ENODEV; + /* the value of this will be zero */ node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL); if (!node) - return -1; - if (pnp_bios_get_dev_node(&nodenum, (char )1, node)) - goto failed; - if(node_set_resources(node, config)<0) - goto failed; - kfree(config); - kfree(node); - return 0; - failed: + return -ENOMEM; + ret = pnp_bios_set_dev_node(dev->number, (char)0, node); + dev->active = 0; kfree(node); - kfree(config); - return -1; + if (ret > 0) + ret = -1; + return ret; } - /* PnP Layer support */ static struct pnp_protocol pnpbios_protocol = { @@ -1387,15 +837,47 @@ static struct pnp_protocol pnpbios_protocol = { .disable = pnpbios_disable_resources, }; -static inline int insert_device(struct pnp_dev *dev) +static int insert_device(struct pnp_dev *dev, struct pnp_bios_node * node) { struct list_head * pos; + unsigned char * p; struct pnp_dev * pnp_dev; + struct pnp_id *dev_id; + char id[8]; + + /* check if the device is already added */ + dev->number = node->handle; list_for_each (pos, &pnpbios_protocol.devices){ pnp_dev = list_entry(pos, struct pnp_dev, protocol_list); if (dev->number == pnp_dev->number) return -1; } + + /* set the initial values for the PnP device */ + dev_id = pnpbios_kmalloc(sizeof(struct pnp_id), GFP_KERNEL); + if (!dev_id) + return -1; + pnpid32_to_pnpid(node->eisa_id,id); + memcpy(dev_id->id,id,7); + pnp_add_id(dev_id, dev); + p = pnp_parse_current_resources((char *)node->data, + (char *)node->data + node->size,&dev->res); + p = pnp_parse_possible_resources((char *)p, + (char *)node->data + node->size,dev); + node_id_data_to_dev(p,node,dev); + dev->active = pnp_is_active(dev); + dev->flags = node->flags; + if (!(dev->flags & PNPBIOS_NO_CONFIG)) + dev->capabilities |= PNP_CONFIGURABLE; + if (!(dev->flags & PNPBIOS_NO_DISABLE)) + dev->capabilities |= PNP_DISABLE; + dev->capabilities |= PNP_READ; + if (pnpbios_is_dynamic(dev)) + dev->capabilities |= PNP_WRITE; + if (dev->flags & PNPBIOS_REMOVABLE) + dev->capabilities |= PNP_REMOVABLE; + dev->protocol = &pnpbios_protocol; + pnp_add_device(dev); return 0; } @@ -1403,14 +885,11 @@ static inline int insert_device(struct pnp_dev *dev) static void __init build_devlist(void) { u8 nodenum; - char id[8]; - unsigned char *pos; unsigned int nodes_got = 0; unsigned int devs = 0; struct pnp_bios_node *node; struct pnp_dev_node_info node_info; struct pnp_dev *dev; - struct pnp_id *dev_id; if (!pnp_bios_present()) return; @@ -1424,51 +903,15 @@ static void __init build_devlist(void) 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); if (!dev) break; - memset(dev,0,sizeof(struct pnp_dev)); - dev_id = pnpbios_kmalloc(sizeof (struct pnp_id), GFP_KERNEL); - if (!dev_id) { - kfree(dev); - break; - } - 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); - pos = node_current_resource_data_to_dev(node,dev); - pos = node_possible_resource_data_to_dev(pos,node,dev); - node_id_data_to_dev(pos,node,dev); - dev->flags = node->flags; - if (!(dev->flags & PNPBIOS_NO_CONFIG)) - dev->capabilities |= PNP_CONFIGURABLE; - if (!(dev->flags & PNPBIOS_NO_DISABLE)) - dev->capabilities |= PNP_DISABLE; - dev->capabilities |= PNP_READ; - if (pnpbios_is_dynamic(dev)) - dev->capabilities |= PNP_WRITE; - if (dev->flags & PNPBIOS_REMOVABLE) - dev->capabilities |= PNP_REMOVABLE; - - dev->protocol = &pnpbios_protocol; - - if(insert_device(dev)<0) { - kfree(dev_id); + if(insert_device(dev,node)<0) kfree(dev); - } else + else devs++; if (nodenum <= thisnodenum) { printk(KERN_ERR "PnPBIOS: build_devlist: Node number 0x%x is out of sequence following node 0x%x. Aborting.\n", (unsigned int)nodenum, (unsigned int)thisnodenum); @@ -1563,6 +1006,8 @@ int __init pnpbios_init(void) 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 --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c index 455b2dcf45728085a6d2a7f81efe6222b29d420c..4ea00c18615324a4f2424d4031908bc920d03693 100644 --- a/drivers/pnp/quirks.c +++ b/drivers/pnp/quirks.c @@ -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_awe32_resources(struct pnp_dev *dev) 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_cmi8330_resources(struct pnp_dev *dev) 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 @@ static void quirk_opl3sax_resources(struct pnp_dev *dev) */ 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 --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c index 6d4b3d6bc18e872851064362e912f93300d7a7a2..1b0ac165450c492eea9ecdf23f3449b0a83a4ff8 100644 --- a/drivers/pnp/resource.c +++ b/drivers/pnp/resource.c @@ -1,8 +1,8 @@ /* - * resource.c - contains resource management algorithms + * resource.c - Contains functions for registering and analyzing resource information * * based on isapnp.c resource management (c) Jaroslav Kysela <perex@suse.cz> - * Copyright 2002 Adam Belay <ambx1@neo.rr.com> + * Copyright 2003 Adam Belay <ambx1@neo.rr.com> * */ @@ -16,15 +16,8 @@ #include <asm/dma.h> #include <asm/irq.h> #include <linux/ioport.h> -#include <linux/config.h> #include <linux/init.h> -#ifdef CONFIG_PNP_DEBUG - #define DEBUG -#else - #undef DEBUG -#endif - #include <linux/pnp.h> #include "base.h" @@ -36,7 +29,9 @@ int pnp_reserve_io[16] = { [0 ... 15] = -1 }; /* reserve (don't use) some I/O re 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,8 +40,8 @@ struct pnp_resources * pnp_build_resource(struct pnp_dev *dev, int dependent) res = pnp_alloc(sizeof(struct pnp_resources)); if (!res) return NULL; - ptr = dev->res; - if (ptr && ptr->dependent && dependent) { /* add to another list */ + ptr = dev->possible; + if (ptr) { /* add to another list */ ptra = ptr->dep; while (ptra && ptra->dep) ptra = ptra->dep; @@ -54,24 +49,14 @@ struct pnp_resources * pnp_build_resource(struct pnp_dev *dev, int dependent) ptr->dep = res; else ptra->dep = res; - } else { - if (!ptr){ - dev->res = res; - } - else{ - kfree(res); - return NULL; - } - } + } else + dev->possible = res; if (dependent) { res->priority = dependent & 0xff; if (res->priority > PNP_RES_PRIORITY_FUNCTIONAL) res->priority = PNP_RES_PRIORITY_INVALID; - res->dependent = 1; - } else { + } else res->priority = PNP_RES_PRIORITY_PREFERRED; - res->dependent = 1; - } return res; } @@ -81,7 +66,7 @@ struct pnp_resources * pnp_find_resources(struct pnp_dev *dev, int depnum) 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 +85,7 @@ int pnp_get_max_depnum(struct pnp_dev *dev) struct pnp_resources *res; if (!dev) return -EINVAL; - res = dev->res; + res = dev->possible; if (!res) return -EINVAL; while (res->dep){ @@ -110,10 +95,6 @@ int pnp_get_max_depnum(struct pnp_dev *dev) return num; } -/* - * Add IRQ resource to resources list. - */ - int pnp_add_irq_resource(struct pnp_dev *dev, int depnum, struct pnp_irq *data) { int i; @@ -139,10 +120,6 @@ int pnp_add_irq_resource(struct pnp_dev *dev, int depnum, struct pnp_irq *data) return 0; } -/* - * Add DMA resource to resources list. - */ - int pnp_add_dma_resource(struct pnp_dev *dev, int depnum, struct pnp_dma *data) { struct pnp_resources *res; @@ -162,20 +139,15 @@ int pnp_add_dma_resource(struct pnp_dev *dev, int depnum, struct pnp_dma *data) return 0; } -/* - * Add port resource to resources list. - */ - int pnp_add_port_resource(struct pnp_dev *dev, int depnum, struct pnp_port *data) { 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; @@ -186,10 +158,6 @@ int pnp_add_port_resource(struct pnp_dev *dev, int depnum, struct pnp_port *data return 0; } -/* - * Add memory resource to resources list. - */ - int pnp_add_mem_resource(struct pnp_dev *dev, int depnum, struct pnp_mem *data) { struct pnp_resources *res; @@ -209,32 +177,6 @@ int pnp_add_mem_resource(struct pnp_dev *dev, int depnum, struct pnp_mem *data) return 0; } -/* - * Add 32-bit memory resource to resources list. - */ - -int pnp_add_mem32_resource(struct pnp_dev *dev, int depnum, struct pnp_mem32 *data) -{ - struct pnp_resources *res; - struct pnp_mem32 *ptr; - res = pnp_find_resources(dev,depnum); - if (!res) - return -EINVAL; - if (!data) - return -EINVAL; - ptr = res->mem32; - while (ptr && ptr->next) - ptr = ptr->next; - if (ptr) - ptr->next = data; - else - res->mem32 = data; - return 0; -} - - -/* resource removing functions */ - static void pnp_free_port(struct pnp_port *port) { struct pnp_port *next; @@ -279,17 +221,6 @@ static void pnp_free_mem(struct pnp_mem *mem) } } -static void pnp_free_mem32(struct pnp_mem32 *mem32) -{ - struct pnp_mem32 *next; - - while (mem32) { - next = mem32->next; - kfree(mem32); - mem32 = next; - } -} - void pnp_free_resources(struct pnp_resources *resources) { struct pnp_resources *next; @@ -300,597 +231,449 @@ void pnp_free_resources(struct pnp_resources *resources) pnp_free_irq(resources->irq); pnp_free_dma(resources->dma); pnp_free_mem(resources->mem); - pnp_free_mem32(resources->mem32); kfree(resources); resources = next; } } -/* resource validity checking functions */ +/* + * resource validity checking + */ + +#define length(start, end) (*(end) - *(start) + 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))) -static int pnp_check_port(int port, int size, int idx, struct pnp_cfg *config) +struct pnp_dev * pnp_check_port_conflicts(struct pnp_dev * dev, int idx, int mode) { - int i, tmp, rport, rsize; - struct pnp_dev *dev; + 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; - 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; - } - - 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; +} + +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) +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; +} + +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; } +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) +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; - } - 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; + /* 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; + } + + /* 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); - return 0; -} - -static int pnp_check_dma(int dma, struct pnp_cfg *config) -{ - int i, mindma = 1; - struct pnp_dev *dev; - if (!config) - return 1; - 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; - } - 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 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); } - 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); - return 0; -} + /* check for warm conflicts */ + if (pnp_check_irq_conflicts(dev, idx, SEARCH_WARM)) + return CONFLICT_TYPE_PNP_WARM; -/* config generation functions */ -static int pnp_generate_port(struct pnp_cfg *config, int num) -{ - struct pnp_port *port; - unsigned long *value1, *value2, *value3; - if (!config || num < 0 || num > 7) - return -EINVAL; - port = config->port[num]; - if (!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)) { - *value1 += port->align; - *value2 = *value1 + port->size - 1; - if (*value1 > port->max || !port->align) - return -ENOENT; - } return 0; } -static int pnp_generate_mem(struct pnp_cfg *config, int num) -{ - struct pnp_mem *mem; - unsigned long *value1, *value2, *value3; - if (!config || num < 0 || num > 3) - return -EINVAL; - mem = config->mem[num]; - if (!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)) { - *value1 += mem->align; - *value2 = *value1 + mem->size - 1; - if (*value1 > mem->max || !mem->align) - return -ENOENT; - } - return 0; -} -static int pnp_generate_irq(struct pnp_cfg *config, int num) +struct pnp_dev * pnp_check_dma_conflicts(struct pnp_dev * dev, int idx, int mode) { - 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) - 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; + int tmp; + struct pnp_dev * tdev; + unsigned long * dma = &dev->res.dma_resource[idx].start; - for (i=0; i < 16; i++) - { - if(irq->map & (1<<xtab[i])) { - *value1 = *value2 = xtab[i]; - if(pnp_check_interrupt(*value1,config)==0) - return 0; + /* 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 -ENOENT; + return NULL; } -static int pnp_generate_dma(struct pnp_cfg *config, int num) +int pnp_check_dma(struct pnp_dev * dev, int idx) { - struct pnp_dma *dma; - 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) - return -EINVAL; - dma = config->dma[num]; - if (!dma) + 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; - value1 = &config->request.dma_resource[num].start; - value2 = &config->request.dma_resource[num].end; - value3 = &config->request.dma_resource[num].flags; - *value3 = dma->flags | IORESOURCE_DMA; - for (i=0; i < 8; i++) - { - if(dma->map & (1<<xtab[i])) { - *value1 = *value2 = xtab[i]; - if(pnp_check_dma(*value1,config)==0) - return 0; - } + /* check if the resource is valid */ + if (pnp_allow_dma0 == 1) + mindma = 0; + 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; } - return -ENOENT; -} -int pnp_init_res_cfg(struct pnp_res_cfg *res_config) -{ - int idx; + /* 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 (!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 < 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 < 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 < 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; + /* 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); } - return 0; -} -static int pnp_prepare_request(struct pnp_dev *dev, struct pnp_cfg *config, struct pnp_res_cfg *template) -{ - 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]; + /* check for warm conflicts */ + if (pnp_check_dma_conflicts(dev, idx, SEARCH_WARM)) + return CONFLICT_TYPE_PNP_WARM; return 0; } -static int pnp_generate_request(struct pnp_dev *dev, struct pnp_cfg *config, struct pnp_res_cfg *template) + +/** + * 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 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; + int idx; + 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 (i=0; i<=3; i++) - { - if(pnp_generate_mem(config,i)<0) - return -ENOENT; + 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 (i=0; i<=1; i++) - { - if(pnp_generate_irq(config,i)<0) - return -ENOENT; + 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 (i=0; i<=1; i++) - { - if(pnp_generate_dma(config,i)<0) - return -ENOENT; + 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; } +/** + * 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 + * + */ -static struct pnp_cfg * pnp_generate_config(struct pnp_dev *dev, int depnum) +int pnp_generate_rule(struct pnp_dev * dev, int depnum, struct pnp_rule_table * rule) { - 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; + + 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; + return 1; 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; -} - -/* PnP Device Resource Management */ - -/** - * pnp_activate_dev - activates a PnP device for use - * @dev: pointer to the desired device - * - * 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 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); - 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); - if (pnp_can_read(dev)) - dev->protocol->get(dev); - kfree(config); - return 0; -} - -/** - * pnp_disable_dev - disables device - * @dev: pointer to the desired device - * - * inform the correct pnp protocol so that resources can be used by other devices - */ - -int pnp_disable_dev(struct pnp_dev *dev) -{ - if (!dev) - return -EINVAL; - if (dev->status != PNP_READY){ - printk(KERN_INFO "pnp: Disable failed becuase the PnP device '%s' is busy\n", dev->dev.bus_id); - return -EINVAL; - } - if (dev->lock_resources) - return -EPERM; - if (!pnp_can_disable(dev) || !dev->active) - return -EINVAL; - pnp_dbg("the device '%s' has been disabled", dev->dev.bus_id); - return dev->protocol->disable(dev); -} -/** - * 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; + /* 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; } -/** - * 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->start = start; - resource->end = start + size - 1; -} EXPORT_SYMBOL(pnp_build_resource); EXPORT_SYMBOL(pnp_find_resources); @@ -899,12 +682,9 @@ EXPORT_SYMBOL(pnp_add_irq_resource); EXPORT_SYMBOL(pnp_add_dma_resource); 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_activate_dev); -EXPORT_SYMBOL(pnp_disable_dev); -EXPORT_SYMBOL(pnp_raw_set_dev); -EXPORT_SYMBOL(pnp_resource_change); +EXPORT_SYMBOL(pnp_init_resource_table); +EXPORT_SYMBOL(pnp_generate_rule); + /* format is: allowdma0 */ diff --git a/drivers/pnp/support.c b/drivers/pnp/support.c new file mode 100644 index 0000000000000000000000000000000000000000..c54cf87fb8cc566cf3782fcb9a22a686a8af5372 --- /dev/null +++ b/drivers/pnp/support.c @@ -0,0 +1,676 @@ +/* + * support.c - provides standard pnp functions for the use of pnp protocol drivers, + * + * Copyright 2002 Adam Belay <ambx1@neo.rr.com> + * + * Resource parsing functions are based on those in the linux pnpbios driver. + * Copyright Christian Schmidt, Tom Lees, David Hinds, Alan Cox, Thomas Hood, + * Brian Gerst and Adam Belay. + */ + +#include <linux/config.h> +#include <linux/module.h> + +#ifdef CONFIG_PNP_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include <linux/pnp.h> +#include "base.h" + +#define SMALL_TAG_PNPVERNO 0x01 +#define SMALL_TAG_LOGDEVID 0x02 +#define SMALL_TAG_COMPATDEVID 0x03 +#define SMALL_TAG_IRQ 0x04 +#define SMALL_TAG_DMA 0x05 +#define SMALL_TAG_STARTDEP 0x06 +#define SMALL_TAG_ENDDEP 0x07 +#define SMALL_TAG_PORT 0x08 +#define SMALL_TAG_FIXEDPORT 0x09 +#define SMALL_TAG_VENDOR 0x0e +#define SMALL_TAG_END 0x0f +#define LARGE_TAG 0x80 +#define LARGE_TAG_MEM 0x01 +#define LARGE_TAG_ANSISTR 0x02 +#define LARGE_TAG_UNICODESTR 0x03 +#define LARGE_TAG_VENDOR 0x04 +#define LARGE_TAG_MEM32 0x05 +#define LARGE_TAG_FIXEDMEM32 0x06 + + +/** + * pnp_is_active - Determines if a device is active based on its current resources + * @dev: pointer to the desired PnP device + * + */ + +int pnp_is_active(struct pnp_dev * dev) +{ + if (!pnp_port_start(dev, 0) && pnp_port_len(dev, 0) <= 1 && + !pnp_mem_start(dev, 0) && pnp_mem_len(dev, 0) <= 1 && + pnp_irq(dev, 0) == -1 && + pnp_dma(dev, 0) == -1) + return 0; + else + return 1; +} + + +/* + * Current resource reading functions * + */ + +static void current_irqresource(struct pnp_resource_table * res, int irq) +{ + int i = 0; + while ((res->irq_resource[i].flags & IORESOURCE_IRQ) && i < PNP_MAX_IRQ) i++; + if (i < PNP_MAX_IRQ) { + res->irq_resource[i].start = + res->irq_resource[i].end = (unsigned long) irq; + res->irq_resource[i].flags = IORESOURCE_IRQ; // Also clears _UNSET flag + } +} + +static void current_dmaresource(struct pnp_resource_table * res, int dma) +{ + int i = 0; + while ((res->dma_resource[i].flags & IORESOURCE_DMA) && i < PNP_MAX_DMA) i++; + if (i < PNP_MAX_DMA) { + res->dma_resource[i].start = + res->dma_resource[i].end = (unsigned long) dma; + res->dma_resource[i].flags = IORESOURCE_DMA; // Also clears _UNSET flag + } +} + +static void current_ioresource(struct pnp_resource_table * res, int io, int len) +{ + int i = 0; + while ((res->port_resource[i].flags & IORESOURCE_IO) && i < PNP_MAX_PORT) i++; + if (i < PNP_MAX_PORT) { + res->port_resource[i].start = (unsigned long) io; + res->port_resource[i].end = (unsigned long)(io + len - 1); + res->port_resource[i].flags = IORESOURCE_IO; // Also clears _UNSET flag + } +} + +static void current_memresource(struct pnp_resource_table * res, int mem, int len) +{ + int i = 0; + while ((res->mem_resource[i].flags & IORESOURCE_MEM) && i < PNP_MAX_MEM) i++; + if (i < PNP_MAX_MEM) { + res->mem_resource[i].start = (unsigned long) mem; + res->mem_resource[i].end = (unsigned long)(mem + len - 1); + res->mem_resource[i].flags = IORESOURCE_MEM; // Also clears _UNSET flag + } +} + +/** + * pnp_parse_current_resources - Extracts current resource information from a raw PnP resource structure + * @p: pointer to the start of the structure + * @end: pointer to the end of the structure + * @res: pointer to the resource table to record to + * + */ + +unsigned char * pnp_parse_current_resources(unsigned char * p, unsigned char * end, struct pnp_resource_table * res) +{ + int len; + + if (!p) + return NULL; + + /* Blank the resource table values */ + pnp_init_resource_table(res); + + while ((char *)p < (char *)end) { + + if(p[0] & LARGE_TAG) { /* large tag */ + len = (p[2] << 8) | p[1]; + switch (p[0] & 0x7f) { + case LARGE_TAG_MEM: + { + int io = *(short *) &p[4]; + int size = *(short *) &p[10]; + if (len != 9) + goto lrg_err; + current_memresource(res, io, size); + break; + } + case LARGE_TAG_ANSISTR: + { + /* ignore this for now */ + break; + } + case LARGE_TAG_VENDOR: + { + /* do nothing */ + break; + } + case LARGE_TAG_MEM32: + { + int io = *(int *) &p[4]; + int size = *(int *) &p[16]; + if (len != 17) + goto lrg_err; + current_memresource(res, io, size); + break; + } + case LARGE_TAG_FIXEDMEM32: + { + int io = *(int *) &p[4]; + int size = *(int *) &p[8]; + if (len != 9) + goto lrg_err; + current_memresource(res, io, size); + break; + } + default: /* an unkown tag */ + { + lrg_err: + pnp_warn("parser: Unknown large tag '0x%x'.", p[0] & 0x7f); + break; + } + } /* switch */ + p += len + 3; + continue; + } /* end large tag */ + + /* small tag */ + len = p[0] & 0x07; + switch ((p[0]>>3) & 0x0f) { + case SMALL_TAG_IRQ: + { + int i, mask, irq = -1; + if (len < 2 || len > 3) + goto sm_err; + mask= p[1] + p[2]*256; + for (i=0;i<16;i++, mask=mask>>1) + if(mask & 0x01) irq=i; + current_irqresource(res, irq); + break; + } + case SMALL_TAG_DMA: + { + int i, mask, dma = -1; + if (len != 2) + goto sm_err; + mask = p[1]; + for (i=0;i<8;i++, mask = mask>>1) + if(mask & 0x01) dma=i; + current_dmaresource(res, dma); + break; + } + case SMALL_TAG_PORT: + { + int io= p[2] + p[3] *256; + int size = p[7]; + if (len != 7) + goto sm_err; + current_ioresource(res, io, size); + break; + } + case SMALL_TAG_VENDOR: + { + /* do nothing */ + break; + } + case SMALL_TAG_FIXEDPORT: + { + int io = p[1] + p[2] * 256; + int size = p[3]; + if (len != 3) + goto sm_err; + current_ioresource(res, io, size); + break; + } + case SMALL_TAG_END: + { + p = p + 2; + return (unsigned char *)p; + break; + } + default: /* an unkown tag */ + { + sm_err: + pnp_warn("parser: Unknown small tag '0x%x'.", p[0]>>3); + break; + } + } + p += len + 1; + } + pnp_err("parser: Resource structure does not contain an end tag."); + + return NULL; +} + + +/* + * Possible resource reading functions * + */ + +static void possible_mem(unsigned char *p, int size, int depnum, struct pnp_dev *dev) +{ + struct pnp_mem * mem; + mem = pnp_alloc(sizeof(struct pnp_mem)); + if (!mem) + return; + mem->min = ((p[3] << 8) | p[2]) << 8; + mem->max = ((p[5] << 8) | p[4]) << 8; + mem->align = (p[7] << 8) | p[6]; + mem->size = ((p[9] << 8) | p[8]) << 8; + mem->flags = p[1]; + pnp_add_mem_resource(dev,depnum,mem); + return; +} + +static void possible_mem32(unsigned char *p, int size, int depnum, struct pnp_dev *dev) +{ + struct pnp_mem * mem; + mem = pnp_alloc(sizeof(struct pnp_mem)); + if (!mem) + return; + mem->min = (p[5] << 24) | (p[4] << 16) | (p[3] << 8) | p[2]; + mem->max = (p[9] << 24) | (p[8] << 16) | (p[7] << 8) | p[6]; + mem->align = (p[13] << 24) | (p[12] << 16) | (p[11] << 8) | p[10]; + mem->size = (p[17] << 24) | (p[16] << 16) | (p[15] << 8) | p[14]; + mem->flags = p[1]; + pnp_add_mem_resource(dev,depnum,mem); + return; +} + +static void possible_fixed_mem32(unsigned char *p, int size, int depnum, struct pnp_dev *dev) +{ + struct pnp_mem * mem; + mem = pnp_alloc(sizeof(struct pnp_mem)); + if (!mem) + return; + mem->min = mem->max = (p[5] << 24) | (p[4] << 16) | (p[3] << 8) | p[2]; + mem->size = (p[9] << 24) | (p[8] << 16) | (p[7] << 8) | p[6]; + mem->align = 0; + mem->flags = p[1]; + pnp_add_mem_resource(dev,depnum,mem); + return; +} + +static void possible_irq(unsigned char *p, int size, int depnum, struct pnp_dev *dev) +{ + struct pnp_irq * irq; + irq = pnp_alloc(sizeof(struct pnp_irq)); + if (!irq) + return; + irq->map = (p[2] << 8) | p[1]; + if (size > 2) + irq->flags = p[3]; + pnp_add_irq_resource(dev,depnum,irq); + return; +} + +static void possible_dma(unsigned char *p, int size, int depnum, struct pnp_dev *dev) +{ + struct pnp_dma * dma; + dma = pnp_alloc(sizeof(struct pnp_dma)); + if (!dma) + return; + dma->map = p[1]; + dma->flags = p[2]; + pnp_add_dma_resource(dev,depnum,dma); + return; +} + +static void possible_port(unsigned char *p, int size, int depnum, struct pnp_dev *dev) +{ + struct pnp_port * port; + port = pnp_alloc(sizeof(struct pnp_port)); + if (!port) + return; + port->min = (p[3] << 8) | p[2]; + port->max = (p[5] << 8) | p[4]; + port->align = p[6]; + port->size = p[7]; + port->flags = p[1] ? PNP_PORT_FLAG_16BITADDR : 0; + pnp_add_port_resource(dev,depnum,port); + return; +} + +static void possible_fixed_port(unsigned char *p, int size, int depnum, struct pnp_dev *dev) +{ + struct pnp_port * port; + port = pnp_alloc(sizeof(struct pnp_port)); + if (!port) + return; + port->min = port->max = (p[2] << 8) | p[1]; + port->size = p[3]; + port->align = 0; + port->flags = PNP_PORT_FLAG_FIXED; + pnp_add_port_resource(dev,depnum,port); + return; +} + +/** + * pnp_parse_possible_resources - Extracts possible resource information from a raw PnP resource structure + * @p: pointer to the start of the structure + * @end: pointer to the end of the structure + * @dev: pointer to the desired PnP device + * + */ + +unsigned char * pnp_parse_possible_resources(unsigned char * p, unsigned char * end, struct pnp_dev *dev) +{ + int len, depnum = 0, dependent = 0; + + if (!p) + return NULL; + + if (pnp_build_resource(dev, 0) == NULL) + return NULL; + + while ((char *)p < (char *)end) { + + if(p[0] & LARGE_TAG) { /* large tag */ + len = (p[2] << 8) | p[1]; + switch (p[0] & 0x7f) { + case LARGE_TAG_MEM: + { + if (len != 9) + goto lrg_err; + possible_mem(p,len,depnum,dev); + break; + } + case LARGE_TAG_MEM32: + { + if (len != 17) + goto lrg_err; + possible_mem32(p,len,depnum,dev); + break; + } + case LARGE_TAG_FIXEDMEM32: + { + if (len != 9) + goto lrg_err; + possible_fixed_mem32(p,len,depnum,dev); + break; + } + default: /* an unkown tag */ + { + lrg_err: + pnp_warn("parser: Unknown large tag '0x%x'.", p[0] & 0x7f); + break; + } + } /* switch */ + p += len + 3; + continue; + } /* end large tag */ + + /* small tag */ + len = p[0] & 0x07; + switch ((p[0]>>3) & 0x0f) { + case SMALL_TAG_IRQ: + { + if (len < 2 || len > 3) + goto sm_err; + possible_irq(p,len,depnum,dev); + break; + } + case SMALL_TAG_DMA: + { + if (len != 2) + goto sm_err; + possible_dma(p,len,depnum,dev); + break; + } + case SMALL_TAG_STARTDEP: + { + if (len > 1) + goto sm_err; + dependent = 0x100 | PNP_RES_PRIORITY_ACCEPTABLE; + if (len > 0) + dependent = 0x100 | p[1]; + pnp_build_resource(dev,dependent); + depnum = pnp_get_max_depnum(dev); + break; + } + case SMALL_TAG_ENDDEP: + { + if (len != 0) + goto sm_err; + depnum = 0; + break; + } + case SMALL_TAG_PORT: + { + if (len != 7) + goto sm_err; + possible_port(p,len,depnum,dev); + break; + } + case SMALL_TAG_FIXEDPORT: + { + if (len != 3) + goto sm_err; + possible_fixed_port(p,len,depnum,dev); + break; + } + case SMALL_TAG_END: + { + p = p + 2; + return (unsigned char *)p; + break; + } + default: /* an unkown tag */ + { + sm_err: + pnp_warn("parser: Unknown small tag '0x%x'.", p[0]>>3); + break; + } + } + p += len + 1; + } + pnp_err("parser: Resource structure does not contain an end tag."); + + return NULL; +} + + +/* + * Resource Writing functions + */ + +static void write_mem(unsigned char *p, struct resource * res) +{ + unsigned long base = res->start; + unsigned long len = res->end - res->start + 1; + p[2] = (base >> 8) & 0xff; + p[3] = ((base >> 8) >> 8) & 0xff; + p[4] = (base >> 8) & 0xff; + p[5] = ((base >> 8) >> 8) & 0xff; + p[8] = (len >> 8) & 0xff; + p[9] = ((len >> 8) >> 8) & 0xff; + return; +} + +static void write_mem32(unsigned char *p, struct resource * res) +{ + unsigned long base = res->start; + unsigned long len = res->end - res->start + 1; + p[2] = base & 0xff; + p[3] = (base >> 8) & 0xff; + p[4] = (base >> 16) & 0xff; + p[5] = (base >> 24) & 0xff; + p[6] = base & 0xff; + p[7] = (base >> 8) & 0xff; + p[8] = (base >> 16) & 0xff; + p[9] = (base >> 24) & 0xff; + p[14] = len & 0xff; + p[15] = (len >> 8) & 0xff; + p[16] = (len >> 16) & 0xff; + p[17] = (len >> 24) & 0xff; + return; +} + +static void write_fixed_mem32(unsigned char *p, struct resource * res) +{ unsigned long base = res->start; + unsigned long len = res->end - res->start + 1; + p[2] = base & 0xff; + p[3] = (base >> 8) & 0xff; + p[4] = (base >> 16) & 0xff; + p[5] = (base >> 24) & 0xff; + p[6] = len & 0xff; + p[7] = (len >> 8) & 0xff; + p[8] = (len >> 16) & 0xff; + p[9] = (len >> 24) & 0xff; + return; +} + +static void write_irq(unsigned char *p, struct resource * res) +{ + unsigned long map = 0; + map = 1 << res->start; + p[1] = map & 0xff; + p[2] = (map >> 8) & 0xff; + return; +} + +static void write_dma(unsigned char *p, struct resource * res) +{ + unsigned long map = 0; + map = 1 << res->start; + p[1] = map & 0xff; + return; +} + +static void write_port(unsigned char *p, struct resource * res) +{ + unsigned long base = res->start; + unsigned long len = res->end - res->start + 1; + p[2] = base & 0xff; + p[3] = (base >> 8) & 0xff; + p[4] = base & 0xff; + p[5] = (base >> 8) & 0xff; + p[7] = len & 0xff; + return; +} + +static void write_fixed_port(unsigned char *p, struct resource * res) +{ + unsigned long base = res->start; + unsigned long len = res->end - res->start + 1; + p[1] = base & 0xff; + p[2] = (base >> 8) & 0xff; + p[3] = len & 0xff; + return; +} + +/** + * pnp_write_resources - Writes resource information to a raw PnP resource structure + * @p: pointer to the start of the structure + * @end: pointer to the end of the structure + * @res: pointer to a resource table containing the resources to set + * + */ + +unsigned char * pnp_write_resources(unsigned char * p, unsigned char * end, struct pnp_resource_table * res) +{ + int len, port = 0, irq = 0, dma = 0, mem = 0; + + if (!p) + return NULL; + + while ((char *)p < (char *)end) { + + if(p[0] & LARGE_TAG) { /* large tag */ + len = (p[2] << 8) | p[1]; + switch (p[0] & 0x7f) { + case LARGE_TAG_MEM: + { + if (len != 9) + goto lrg_err; + write_mem(p, &res->mem_resource[mem]); + mem++; + break; + } + case LARGE_TAG_MEM32: + { + if (len != 17) + goto lrg_err; + write_mem32(p, &res->mem_resource[mem]); + break; + } + case LARGE_TAG_FIXEDMEM32: + { + if (len != 9) + goto lrg_err; + write_fixed_mem32(p, &res->mem_resource[mem]); + break; + } + default: /* an unkown tag */ + { + lrg_err: + pnp_warn("parser: Unknown large tag '0x%x'.", p[0] & 0x7f); + break; + } + } /* switch */ + p += len + 3; + continue; + } /* end large tag */ + + /* small tag */ + len = p[0] & 0x07; + switch ((p[0]>>3) & 0x0f) { + case SMALL_TAG_IRQ: + { + if (len < 2 || len > 3) + goto sm_err; + write_irq(p, &res->irq_resource[irq]); + irq++; + break; + } + case SMALL_TAG_DMA: + { + if (len != 2) + goto sm_err; + write_dma(p, &res->dma_resource[irq]); + dma++; + break; + } + case SMALL_TAG_PORT: + { + if (len != 7) + goto sm_err; + write_port(p, &res->port_resource[port]); + port++; + break; + } + case SMALL_TAG_FIXEDPORT: + { + if (len != 3) + goto sm_err; + write_fixed_port(p, &res->port_resource[port]); + port++; + break; + } + case SMALL_TAG_END: + { + p = p + 2; + return (unsigned char *)p; + break; + } + default: /* an unkown tag */ + { + sm_err: + pnp_warn("parser: Unknown small tag '0x%x'.", p[0]>>3); + break; + } + } + p += len + 1; + } + pnp_err("parser: Resource structure does not contain an end tag."); + + return NULL; +} + +EXPORT_SYMBOL(pnp_is_active); +EXPORT_SYMBOL(pnp_parse_current_resources); +EXPORT_SYMBOL(pnp_parse_possible_resources); +EXPORT_SYMBOL(pnp_write_resources); diff --git a/drivers/pnp/system.c b/drivers/pnp/system.c index d32e9b5805978ff0259d12f9c07a2c1954c84be9..cadcdcdbe1f7a12838617c6e1a0f040e97a902cb 100644 --- a/drivers/pnp/system.c +++ b/drivers/pnp/system.c @@ -53,7 +53,7 @@ static void __init reserve_resources_of_dev( struct pnp_dev *dev ) { 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; @@ -93,6 +93,7 @@ static int system_pnp_probe(struct pnp_dev * dev, const struct pnp_device_id *de static struct pnp_driver system_pnp_driver = { .name = "system", + .flags = PNP_DRIVER_DO_NOT_ACTIVATE, .id_table = pnp_dev_table, .probe = system_pnp_probe, .remove = NULL, diff --git a/drivers/serial/8250_pnp.c b/drivers/serial/8250_pnp.c index 7ba1705a688ccd6d09baf4c4c2236fcd9be5cf04..957b709c5c3d7649bc5cee2b00c5f05e81e4d789 100644 --- a/drivers/serial/8250_pnp.c +++ b/drivers/serial/8250_pnp.c @@ -317,7 +317,7 @@ static inline void avoid_irq_share(struct pnp_dev *dev) { 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 __devinit check_name(char *name) */ 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 --git a/include/linux/ide.h b/include/linux/ide.h index 32f4e6650463341acd4363acccfaf20634731deb..7510ad7d1aa4200afc0b1004f2f4f5e25f59bcc1 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1705,6 +1705,7 @@ static inline void ide_release_dma(ide_hwif_t *drive) {;} #endif extern void hwif_unregister(ide_hwif_t *); +extern void ide_unregister (unsigned int index); extern void export_ide_init_queue(ide_drive_t *); extern u8 export_probe_for_drive(ide_drive_t *); diff --git a/include/linux/ioport.h b/include/linux/ioport.h index edd1eadb98b82fe37533e0cdf5226e94a044c1ac..7a9431f4673f3bb9ed870eb239c7c2734b8d4210 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -77,6 +77,7 @@ struct resource_list { #define IORESOURCE_MEM_8BIT (0<<3) #define IORESOURCE_MEM_16BIT (1<<3) #define IORESOURCE_MEM_8AND16BIT (2<<3) +#define IORESOURCE_MEM_32BIT (3<<3) #define IORESOURCE_MEM_SHADOWABLE (1<<5) /* dup: IORESOURCE_SHADOWABLE */ #define IORESOURCE_MEM_EXPANSIONROM (1<<6) diff --git a/include/linux/pnp.h b/include/linux/pnp.h index d138647ff235db37e4296e8817e5c488e3968d3d..caf54014088eb8a00f62abab509a5d239e4d2399 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -13,42 +13,144 @@ #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 + +struct pnp_protocol; +struct pnp_dev; + /* - * Device Managemnt + * Resource Management */ -#define DEVICE_COUNT_IRQ 2 -#define DEVICE_COUNT_DMA 2 -#define DEVICE_COUNT_IO 8 -#define DEVICE_COUNT_MEM 4 -#define MAX_DEVICES 8 +/* Use these instead of directly reading pnp_dev to get resource information */ +#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)) == \ + pnp_port_start((dev),(bar))) ? 0 : \ + \ + (pnp_port_end((dev),(bar)) - \ + pnp_port_start((dev),(bar)) + 1)) + +#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)) == \ + pnp_mem_start((dev),(bar))) ? 0 : \ + \ + (pnp_mem_end((dev),(bar)) - \ + pnp_mem_start((dev),(bar)) + 1)) + +#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) + +struct pnp_port { + unsigned short min; /* min base number */ + unsigned short max; /* max base number */ + unsigned char align; /* align boundary */ + unsigned char size; /* size of range */ + unsigned char flags; /* port flags */ + unsigned char pad; /* pad */ + struct pnp_port *next; /* next port */ +}; + +struct pnp_irq { + unsigned short map; /* bitmaks for IRQ lines */ + unsigned char flags; /* IRQ flags */ + unsigned char pad; /* pad */ + struct pnp_irq *next; /* next IRQ */ +}; + +struct pnp_dma { + unsigned char map; /* bitmask for DMA channels */ + unsigned char flags; /* DMA flags */ + struct pnp_dma *next; /* next port */ +}; + +struct pnp_mem { + unsigned int min; /* min base number */ + unsigned int max; /* max base number */ + unsigned int align; /* align boundary */ + unsigned int size; /* size of range */ + unsigned char flags; /* memory flags */ + unsigned char pad; /* pad */ + struct pnp_mem *next; /* next memory resource */ +}; + +#define PNP_RES_PRIORITY_PREFERRED 0 +#define PNP_RES_PRIORITY_ACCEPTABLE 1 +#define PNP_RES_PRIORITY_FUNCTIONAL 2 +#define PNP_RES_PRIORITY_INVALID 65535 + +struct pnp_resources { + unsigned short priority; /* priority */ + struct pnp_port *port; /* first port */ + struct pnp_irq *irq; /* first IRQ */ + struct pnp_dma *dma; /* first DMA */ + struct pnp_mem *mem; /* first memory resource */ + struct pnp_dev *dev; /* parent */ + struct pnp_resources *dep; /* dependent resources */ +}; + +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]; +}; -struct pnp_resource; -struct pnp_protocol; -struct pnp_id; -struct pnp_cfg; + +/* + * Device Managemnt + */ struct pnp_card { - char name[80]; - int status; /* status of the card */ - unsigned char number; /* card number */ + 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 */ - 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) @@ -58,10 +160,6 @@ struct pnp_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) { @@ -84,8 +182,9 @@ static inline void pnpc_set_protodata (struct pnp_card *pcard, void *data) } struct pnp_dev { - char name[80]; /* device name */ - int active; /* status of the device */ + struct device dev; /* Driver Model device interface */ + unsigned char number; /* used as an index, must be unique */ + int active; int capabilities; int status; @@ -93,24 +192,20 @@ struct pnp_dev { 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*/ + 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 config_mode; /* flags that determine how the device's resources should be configured */ 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 */ + int flags; /* used by protocols */ struct proc_dir_entry *procent; /* device entry in /proc/bus/isapnp */ }; @@ -119,13 +214,14 @@ struct pnp_dev { #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)) + 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) { @@ -152,6 +248,12 @@ struct pnp_fixup { 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 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 #define PNP_WRITE 0x0002 @@ -165,14 +267,14 @@ struct pnp_fixup { ((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)) +#define pnp_can_configure(dev) ((!(dev)->active) && ((dev)->config_mode & PNP_CONFIG_AUTO) && \ + ((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 +#define PNP_READY 0x0000 +#define PNP_ATTACHED 0x0001 +#define PNP_BUSY 0x0002 +#define PNP_FAULTY 0x0004 /* @@ -180,21 +282,21 @@ struct pnp_fixup { */ 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 +318,9 @@ struct pnp_driver { 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; }; @@ -227,135 +329,16 @@ struct pnpc_driver { /* - * 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_len(dev,bar) \ - ((pnp_port_start((dev),(bar)) == 0 && \ - pnp_port_end((dev),(bar)) == \ - pnp_port_start((dev),(bar))) ? 0 : \ - \ - (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_len(dev,bar) \ - ((pnp_mem_start((dev),(bar)) == 0 && \ - pnp_mem_end((dev),(bar)) == \ - pnp_mem_start((dev),(bar))) ? 0 : \ - \ - (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_PORT_FLAG_16BITADDR (1<<0) -#define PNP_PORT_FLAG_FIXED (1<<1) - -struct pnp_port { - unsigned short min; /* min base number */ - unsigned short max; /* max base number */ - unsigned char align; /* align boundary */ - 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 */ -}; - -struct pnp_irq { - 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 */ -}; - -struct pnp_mem { - unsigned int min; /* min base number */ - unsigned int max; /* max base number */ - unsigned int align; /* align boundary */ - 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 */ -}; - -#define PNP_RES_PRIORITY_PREFERRED 0 -#define PNP_RES_PRIORITY_ACCEPTABLE 1 -#define PNP_RES_PRIORITY_FUNCTIONAL 2 -#define PNP_RES_PRIORITY_INVALID 65535 - -struct pnp_resources { - unsigned short priority; /* priority */ - unsigned short dependent; /* dependent resources */ - struct pnp_port *port; /* first port */ - struct pnp_irq *irq; /* first IRQ */ - struct pnp_dma *dma; /* first DMA */ - struct pnp_mem *mem; /* first memory resource */ - struct pnp_mem32 *mem32; /* first 32-bit memory */ - struct pnp_dev *dev; /* parent */ - 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_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; -}; - - -/* * Protocol Management */ struct pnp_protocol { struct list_head protocol_list; - char name[DEVICE_NAME_SIZE]; + char * name; - /* functions */ - int (*get)(struct pnp_dev *dev); - int (*set)(struct pnp_dev *dev, struct pnp_cfg *config); + /* resource control functions */ + int (*get)(struct pnp_dev *dev, struct pnp_resource_table *res); + int (*set)(struct pnp_dev *dev, struct pnp_resource_table *res); int (*disable)(struct pnp_dev *dev); /* used by pnp layer only (look but don't touch) */ @@ -384,6 +367,8 @@ void pnp_unregister_protocol(struct pnp_protocol *protocol); 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); @@ -393,29 +378,42 @@ int pnp_add_irq_resource(struct pnp_dev *dev, int depnum, struct pnp_irq *data); int pnp_add_dma_resource(struct pnp_dev *dev, int depnum, struct pnp_dma *data); 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_generate_rule(struct pnp_dev * dev, int depnum, struct pnp_rule_table * rule); + +/* manager */ +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); +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); 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); + +/* support */ +int pnp_is_active(struct pnp_dev * dev); +unsigned char * pnp_parse_current_resources(unsigned char * p, unsigned char * end, struct pnp_resource_table * res); +unsigned char * pnp_parse_possible_resources(unsigned char * p, unsigned char * end, struct pnp_dev * dev); +unsigned char * pnp_write_resources(unsigned char * p, unsigned char * end, struct pnp_resource_table * res); #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; } @@ -423,19 +421,27 @@ static inline int pnp_add_irq_resource(struct pnp_dev *dev, int depnum, struct p static inline int pnp_add_dma_resource(struct pnp_dev *dev, int depnum, struct pnp_irq *data) { return -ENODEV; } 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 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; } + +/* manager */ +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) { ; } +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; } 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) { ; } +/* support */ +static inline int pnp_is_active(struct pnp_dev * dev) { return -ENODEV; ) +static inline unsigned char * pnp_parse_current_resources(unsigned char * p, unsigned char * end, struct pnp_resource_table * res) { return NULL; } +static inline unsigned char * pnp_parse_possible_resources(unsigned char * p, unsigned char * end, struct pnp_dev * dev) { return NULL; } +static inline unsigned char * pnp_write_resources(unsigned char * p, unsigned char * end, struct pnp_resource_table * res) { return NULL; ) #endif /* CONFIG_PNP */ @@ -458,6 +464,7 @@ void pnpc_detach(struct pnp_card *card); #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 +474,12 @@ static inline void pnp_release_card_device(struct pnp_dev *dev) { ; } 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) diff --git a/sound/oss/sb_card.c b/sound/oss/sb_card.c index 2689d6277418f556ba3205a53b00d937e917592d..999c69afb161726bff61b975c947e63b76b8b96f 100644 --- a/sound/oss/sb_card.c +++ b/sound/oss/sb_card.c @@ -1,1035 +1,329 @@ /* - * sound/sb_card.c + * sound/oss/sb_card.c * - * Detection routine for the Sound Blaster cards. + * Detection routine for the ISA Sound Blaster and compatable sound + * cards. * + * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this + * software for more info. * - * Copyright (C) by Hannu Savolainen 1993-1997 + * This is a complete rewrite of the detection routines. This was + * prompted by the PnP API change during v2.5 and the ugly state the + * code was in. * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. + * Copyright (C) by Paul Laufer 2002. Based on code originally by + * Hannu Savolainen which was modified by many others over the + * years. Authors specifically mentioned in the previous version were: + * Daniel Stone, Alessandro Zummo, Jeff Garzik, Arnaldo Carvalho de + * Melo, Daniel Church, and myself. * - * 26-11-1999 Patched to compile without ISA PnP support in the - * kernel - Daniel Stone (tamriel@ductape.net) - * - * 06-01-2000 Refined and bugfixed ISA PnP support, added - * CMI 8330 support - Alessandro Zummo <azummo@ita.flashnet.it> - * - * 18-01-2000 Separated sb_card and sb_common - * Jeff Garzik <jgarzik@pobox.com> - * - * 04-02-2000 Added Soundblaster AWE 64 PnP support, isapnpjump - * Alessandro Zummo <azummo@ita.flashnet.it> - * - * 11-02-2000 Added Soundblaster AWE 32 PnP support, refined PnP code - * Alessandro Zummo <azummo@ita.flashnet.it> - * - * 13-02-2000 Hopefully fixed awe/sb16 related bugs, code cleanup - * Alessandro Zummo <azummo@ita.flashnet.it> - * - * 13-03-2000 Added some more cards, thanks to Torsten Werner. - * Removed joystick and wavetable code, there are better places for them. - * Code cleanup plus some fixes. - * Alessandro Zummo <azummo@ita.flashnet.it> - * - * 26-03-2000 Fixed acer, esstype and sm_games module options. - * Alessandro Zummo <azummo@ita.flashnet.it> - * - * 12-04-2000 ISAPnP cleanup, reorg, fixes, and multiple card support. - * Thanks to Gaël Quéri and Alessandro Zummo for testing and fixes. - * Paul E. Laufer <pelaufer@csupomona.edu> - * - * 06-05-2000 added another card. Daniel M. Newman <dmnewman@pobox.com> - * - * 25-05-2000 Added Creative SB AWE64 Gold (CTL00B2). - * Pål-Kristian Engstad <engstad@att.net> - * - * 12-08-2000 Added Creative SB32 PnP (CTL009F). - * Kasatenko Ivan Alex. <skywriter@rnc.ru> - * - * 21-09-2000 Got rid of attach_sbmpu - * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - * - * 28-10-2000 Added pnplegacy support - * Daniel Church <dchurch@mbhs.edu> - * - * 01-10-2001 Added a new flavor of Creative SB AWE64 PnP (CTL00E9). - * Jerome Cornet <jcornet@free.fr> + * 02-05-2003 Original Release, Paul Laufer <paul@laufernet.com> + * 02-07-2003 Bug made it into first release. Take two. */ #include <linux/config.h> -#include <linux/mca.h> #include <linux/module.h> +#include <linux/moduleparam.h> #include <linux/init.h> -#include <linux/isapnp.h> - +#ifdef CONFIG_MCA +#include <linux/mca.h> +#endif /* CONFIG_MCA */ #include "sound_config.h" - #include "sb_mixer.h" #include "sb.h" - -#ifdef __ISAPNP__ -#define SB_CARDS_MAX 5 -#else -#define SB_CARDS_MAX 1 +#ifdef CONFIG_PNP_CARD +#include <linux/pnp.h> #endif +#include "sb_card.h" -static int sbmpu[SB_CARDS_MAX] = {0}; -static int sb_cards_num = 0; +MODULE_DESCRIPTION("OSS Soundblaster ISA PnP and legacy sound driver"); +MODULE_LICENSE("GPL"); extern void *smw_free; -/* - * Note DMA2 of -1 has the right meaning in the SB16 driver as well - * as here. It will cause either an error if it is needed or a fallback - * to the 8bit channel. - */ - static int __initdata mpu_io = 0; static int __initdata io = -1; static int __initdata irq = -1; static int __initdata dma = -1; -static int __initdata dma16 = -1; /* Set this for modules that need it */ -static int __initdata type = 0; /* Can set this to a specific card type */ -static int __initdata esstype = 0; /* ESS chip type */ -static int __initdata acer = 0; /* Do acer notebook init? */ -static int __initdata sm_games = 0; /* Logitech soundman games? */ +static int __initdata dma16 = -1; +static int __initdata type = 0; /* Can set this to a specific card type */ +static int __initdata esstype = 0; /* ESS chip type */ +static int __initdata acer = 0; /* Do acer notebook init? */ +static int __initdata sm_games = 0; /* Logitech soundman games? */ -static void __init attach_sb_card(struct address_info *hw_config) -{ - if(!sb_dsp_init(hw_config, THIS_MODULE)) - hw_config->slots[0] = -1; -} +struct sb_card_config *legacy = NULL; -static int __init probe_sb(struct address_info *hw_config) -{ - struct sb_module_options sbmo; +#ifdef CONFIG_PNP_CARD +static int __initdata pnp = 1; +/* +static int __initdata uart401 = 0; +*/ +#else +static int __initdata pnp = 0; +#endif - if (hw_config->io_base == -1 || hw_config->dma == -1 || hw_config->irq == -1) - { - printk(KERN_ERR "sb: I/O, IRQ, and DMA are mandatory\n"); - return -EINVAL; +module_param(io, int, 000); +MODULE_PARM_DESC(io, "Soundblaster i/o base address (0x220,0x240,0x260,0x280)"); +module_param(irq, int, 000); +MODULE_PARM_DESC(irq, "IRQ (5,7,9,10)"); +module_param(dma, int, 000); +MODULE_PARM_DESC(dma, "8-bit DMA channel (0,1,3)"); +module_param(dma16, int, 000); +MODULE_PARM_DESC(dma16, "16-bit DMA channel (5,6,7)"); +module_param(mpu_io, int, 000); +MODULE_PARM_DESC(mpu_io, "MPU base address"); +module_param(type, int, 000); +MODULE_PARM_DESC(type, "You can set this to specific card type (doesn't " \ + "work with pnp)"); +module_param(sm_games, int, 000); +MODULE_PARM_DESC(sm_games, "Enable support for Logitech soundman games " \ + "(doesn't work with pnp)"); +module_param(esstype, int, 000); +MODULE_PARM_DESC(esstype, "ESS chip type (doesn't work with pnp)"); +module_param(acer, int, 000); +MODULE_PARM_DESC(acer, "Set this to detect cards in some ACER notebooks "\ + "(doesn't work with pnp)"); + +#ifdef CONFIG_PNP_CARD +module_param(pnp, int, 000); +MODULE_PARM_DESC(pnp, "Went set to 0 will disable detection using PnP. "\ + "Default is 1.\n"); +/* Not done yet.... */ +/* +module_param(uart401, int, 000); +MODULE_PARM_DESC(uart401, "When set to 1, will attempt to detect and enable"\ + "the mpu on some clones"); +*/ +#endif /* CONFIG_PNP_CARD */ + +/* OSS subsystem card registration shared by PnP and legacy routines */ +static int sb_register_oss(struct sb_card_config *scc, struct sb_module_options *sbmo) +{ + if(!sb_dsp_detect(&scc->conf, 0, 0, sbmo)) { + printk(KERN_ERR "sb: Failed DSP Detect.\n"); + kfree(scc); + return -ENODEV; } - -#ifdef CONFIG_MCA - /* MCA code added by ZP Gu (zpg@castle.net) */ - if (MCA_bus) { /* no multiple REPLY card probing */ - int slot; - u8 pos2, pos3, pos4; - - slot = mca_find_adapter( 0x5138, 0 ); - if( slot == MCA_NOTFOUND ) - { - slot = mca_find_adapter( 0x5137, 0 ); - - if (slot != MCA_NOTFOUND) - mca_set_adapter_name( slot, "REPLY SB16 & SCSI Adapter" ); - } - else - { - mca_set_adapter_name( slot, "REPLY SB16 Adapter" ); - } - - if (slot != MCA_NOTFOUND) - { - mca_mark_as_used(slot); - pos2 = mca_read_stored_pos( slot, 2 ); - pos3 = mca_read_stored_pos( slot, 3 ); - pos4 = mca_read_stored_pos( slot, 4 ); - - if (pos2 & 0x4) - { - /* enabled? */ - static unsigned short irq[] = { 0, 5, 7, 10 }; - /* - static unsigned short midiaddr[] = {0, 0x330, 0, 0x300 }; - */ - - hw_config->io_base = 0x220 + 0x20 * (pos2 >> 6); - hw_config->irq = irq[(pos4 >> 5) & 0x3]; - hw_config->dma = pos3 & 0xf; - /* Reply ADF wrong on High DMA, pos[1] should start w/ 00 */ - hw_config->dma2 = (pos3 >> 4) & 0x3; - if (hw_config->dma2 == 0) - hw_config->dma2 = hw_config->dma; - else - hw_config->dma2 += 4; - /* - hw_config->driver_use_2 = midiaddr[(pos2 >> 3) & 0x3]; - */ - - printk(KERN_INFO "sb: Reply MCA SB at slot=%d \ -iobase=0x%x irq=%d lo_dma=%d hi_dma=%d\n", - slot+1, - hw_config->io_base, hw_config->irq, - hw_config->dma, hw_config->dma2); - } - else - { - printk (KERN_INFO "sb: Reply SB Base I/O address disabled\n"); - } - } + if(!sb_dsp_init(&scc->conf, THIS_MODULE)) { + printk(KERN_ERR "sb: Failed DSP init.\n"); + kfree(scc); + return -ENODEV; + } + if(scc->mpucnf.io_base > 0) { + scc->mpu = 1; + printk(KERN_INFO "sb: Turning on MPU\n"); + if(!probe_sbmpu(&scc->mpucnf, THIS_MODULE)) + scc->mpu = 0; } -#endif - - /* Setup extra module options */ - - sbmo.acer = acer; - sbmo.sm_games = sm_games; - sbmo.esstype = esstype; - return sb_dsp_detect(hw_config, 0, 0, &sbmo); + return 1; } -static void __exit unload_sb(struct address_info *hw_config, int card) +static void sb_unload(struct sb_card_config *scc) { - if(hw_config->slots[0]!=-1) - sb_dsp_unload(hw_config, sbmpu[card]); + sb_dsp_unload(&scc->conf, 0); + if(scc->mpu) + unload_sbmpu(&scc->mpucnf); + kfree(scc); } -static struct address_info cfg[SB_CARDS_MAX]; -static struct address_info cfg_mpu[SB_CARDS_MAX]; - -struct pci_dev *sb_dev[SB_CARDS_MAX] = {NULL}, - *mpu_dev[SB_CARDS_MAX] = {NULL}, - *opl_dev[SB_CARDS_MAX] = {NULL}; - - -#ifdef __ISAPNP__ -static int isapnp = 1; -static int isapnpjump = 0; -static int multiple = 1; -static int pnplegacy = 0; -static int reverse = 0; -static int uart401 = 0; - -static int audio_activated[SB_CARDS_MAX] = {0}; -static int mpu_activated[SB_CARDS_MAX] = {0}; -static int opl_activated[SB_CARDS_MAX] = {0}; -#else -static int isapnp = 0; -static int multiple = 0; -static int pnplegacy = 0; -#endif - -MODULE_DESCRIPTION("Soundblaster driver"); -MODULE_LICENSE("GPL"); - -MODULE_PARM(io, "i"); -MODULE_PARM(irq, "i"); -MODULE_PARM(dma, "i"); -MODULE_PARM(dma16, "i"); -MODULE_PARM(mpu_io, "i"); -MODULE_PARM(type, "i"); -MODULE_PARM(sm_games, "i"); -MODULE_PARM(esstype, "i"); -MODULE_PARM(acer, "i"); - -#ifdef __ISAPNP__ -MODULE_PARM(isapnp, "i"); -MODULE_PARM(isapnpjump, "i"); -MODULE_PARM(multiple, "i"); -MODULE_PARM(pnplegacy, "i"); -MODULE_PARM(reverse, "i"); -MODULE_PARM(uart401, "i"); -MODULE_PARM_DESC(isapnp, "When set to 0, Plug & Play support will be disabled"); -MODULE_PARM_DESC(isapnpjump, "Jumps to a specific slot in the driver's PnP table. Use the source, Luke."); -MODULE_PARM_DESC(multiple, "When set to 0, will not search for multiple cards"); -MODULE_PARM_DESC(pnplegacy, "When set to 1, will search for a legacy SB card along with any PnP cards."); -MODULE_PARM_DESC(reverse, "When set to 1, will reverse ISAPnP search order"); -MODULE_PARM_DESC(uart401, "When set to 1, will attempt to detect and enable the mpu on some clones"); -#endif - -MODULE_PARM_DESC(io, "Soundblaster i/o base address (0x220,0x240,0x260,0x280)"); -MODULE_PARM_DESC(irq, "IRQ (5,7,9,10)"); -MODULE_PARM_DESC(dma, "8-bit DMA channel (0,1,3)"); -MODULE_PARM_DESC(dma16, "16-bit DMA channel (5,6,7)"); -MODULE_PARM_DESC(mpu_io, "Mpu base address"); -MODULE_PARM_DESC(type, "You can set this to specific card type"); -MODULE_PARM_DESC(sm_games, "Enable support for Logitech soundman games"); -MODULE_PARM_DESC(esstype, "ESS chip type"); -MODULE_PARM_DESC(acer, "Set this to detect cards in some ACER notebooks"); - -#ifdef __ISAPNP__ - -/* Please add new entries at the end of the table */ -static struct { - char *name; - unsigned short card_vendor, card_device, - audio_vendor, audio_function, - mpu_vendor, mpu_function, - opl_vendor, opl_function; - short dma, dma2, mpu_io, mpu_irq; /* see sb_init() */ -} sb_isapnp_list[] __initdata = { - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0024), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0025), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0026), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0027), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0028), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0029), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002a), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002b), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00ed), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster Vibra16S", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0051), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster Vibra16C", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0070), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster Vibra16CL", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0080), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster Vibra16X", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00F0), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0043), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0039), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0042), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0043), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0044), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0045), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0046), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0047), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0048), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0054), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009C), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), - 0,0,0,0, - 0,1,1,-1}, - {"Creative SB32 PnP", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009F), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009D), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64 Gold", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009E), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64 Gold", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00B2), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C1), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C3), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C5), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C7), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E4), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E9), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), - 0,0,0,0, - 0,1,1,-1}, - {"ESS 1688", - ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0968), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968), - 0,0,0,0, - 0,1,2,-1}, - {"ESS 1868", - ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), - 0,0,0,0, - 0,1,2,-1}, - {"ESS 1868", - ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611), - 0,0,0,0, - 0,1,2,-1}, - {"ESS 1869 PnP AudioDrive", - ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0003), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), - 0,0,0,0, - 0,1,2,-1}, - {"ESS 1869", - ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1869), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), - 0,0,0,0, - 0,1,2,-1}, - {"ESS 1878", - ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1878), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878), - 0,0,0,0, - 0,1,2,-1}, - {"ESS 1879", - ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1879), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879), - 0,0,0,0, - 0,1,2,-1}, - {"CMI 8330 SoundPRO", - ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), - 0,1,0,-1}, - {"Diamond DT0197H", - ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), - 0,-1,0,0}, - {"ALS007", - ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), - 0,-1,0,0}, - {"ALS100", - ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), - 1,0,0,0}, - {"ALS110", - ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x1001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x1001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), - 1,0,0,0}, - {"ALS120", - ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), - 1,0,0,0}, - {"ALS200", - ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0020), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0020), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), - 1,0,0,0}, - {"RTL3000", - ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), - 1,0,0,0}, - {0} -}; - -static struct isapnp_device_id id_table[] __devinitdata = { - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0024), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0025), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0026), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0027), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0028), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0029), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002a), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002b), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00ed), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0051), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0070), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0080), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00F0), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0043), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0039), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0042), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0043), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0044), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0045), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0048), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0054), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009C), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009F), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009D), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009E), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00B2), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C1), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C3), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C5), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C7), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E4), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E9), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, - - { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0968), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968), 0 }, - - { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), 0 }, - - { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611), 0 }, - - { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0003), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), 0 }, - - { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1869), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), 0 }, - - { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1878), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878), 0 }, - - { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1879), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879), 0 }, - - { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x1001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x1001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0020), 0 }, +/* Register legacy card with OSS subsystem */ +static int sb_init_legacy(void) +{ + struct sb_module_options sbmo = {0}; - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0020), 0 }, + if((legacy = kmalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "sb: Error: Could not allocate memory\n"); + return -ENOMEM; + } + memset(legacy, 0, sizeof(struct sb_card_config)); - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + legacy->conf.io_base = io; + legacy->conf.irq = irq; + legacy->conf.dma = dma; + legacy->conf.dma2 = dma16; + legacy->conf.card_subtype = type; - { ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), 0 }, + legacy->mpucnf.io_base = mpu_io; + legacy->mpucnf.irq = -1; + legacy->mpucnf.dma = -1; + legacy->mpucnf.dma2 = -1; - { ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), 0 }, + sbmo.esstype = esstype; + sbmo.sm_games = sm_games; + sbmo.acer = acer; - { ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, - {0} -}; + return sb_register_oss(legacy, &sbmo); +} -MODULE_DEVICE_TABLE(isapnp, id_table); +#ifdef CONFIG_PNP_CARD -static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev) +/* Populate the OSS subsystem structures with information from PnP */ +static void sb_dev2cfg(struct pnp_dev *dev, struct sb_card_config *scc) { - int err; - - /* Device already active? Let's use it */ - if(dev->active) - return(dev); - - if((err = dev->activate(dev)) < 0) { - printk(KERN_ERR "sb: %s %s config failed (out of resources?)[%d]\n", devname, resname, err); - - dev->deactivate(dev); - - return(NULL); + scc->conf.io_base = -1; + scc->conf.irq = -1; + scc->conf.dma = -1; + scc->conf.dma2 = -1; + scc->mpucnf.io_base = -1; + scc->mpucnf.irq = -1; + scc->mpucnf.dma = -1; + scc->mpucnf.dma2 = -1; + + /* All clones layout their PnP tables differently and some use + different logical devices for the MPU */ + if(!strncmp("CTL",scc->card_id,3)) { + scc->conf.io_base = pnp_port_start(dev,0); + scc->conf.irq = pnp_irq(dev,0); + scc->conf.dma = pnp_dma(dev,0); + scc->conf.dma2 = pnp_dma(dev,1); + scc->mpucnf.io_base = pnp_port_start(dev,1); + return; } - return(dev); -} - -static struct pci_dev *sb_init(struct pci_bus *bus, struct address_info *hw_config, struct address_info *mpu_config, int slot, int card) -{ - - /* Configure Audio device */ - if((sb_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].audio_vendor, sb_isapnp_list[slot].audio_function, NULL))) - { - int ret; - ret = sb_dev[card]->prepare(sb_dev[card]); - /* If device is active, assume configured with /proc/isapnp - * and use anyway. Some other way to check this? */ - if(ret && ret != -EBUSY) { - printk(KERN_ERR "sb: ISAPnP found device that could not be autoconfigured.\n"); - return(NULL); - } - if(ret == -EBUSY) - audio_activated[card] = 1; - - if((sb_dev[card] = activate_dev(sb_isapnp_list[slot].name, "sb", sb_dev[card]))) - { - hw_config->io_base = sb_dev[card]->resource[0].start; - hw_config->irq = sb_dev[card]->irq_resource[0].start; - hw_config->dma = sb_dev[card]->dma_resource[sb_isapnp_list[slot].dma].start; - if(sb_isapnp_list[slot].dma2 != -1) - hw_config->dma2 = sb_dev[card]->dma_resource[sb_isapnp_list[slot].dma2].start; - else - hw_config->dma2 = -1; - } else - return(NULL); - } else - return(NULL); - - /* Cards with separate OPL3 device (ALS, CMI, etc.) - * This is just to activate the device so the OPL module can use it */ - if(sb_isapnp_list[slot].opl_vendor || sb_isapnp_list[slot].opl_function) { - if((opl_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].opl_vendor, sb_isapnp_list[slot].opl_function, NULL))) { - int ret = opl_dev[card]->prepare(opl_dev[card]); - /* If device is active, assume configured with - * /proc/isapnp and use anyway */ - if(ret && ret != -EBUSY) { - printk(KERN_ERR "sb: OPL device could not be autoconfigured.\n"); - return(sb_dev[card]); - } - if(ret == -EBUSY) - opl_activated[card] = 1; - - /* Some have irq and dma for opl. the opl3 driver wont - * use 'em so don't configure 'em and hope it works -PEL */ - opl_dev[card]->irq_resource[0].flags = 0; - opl_dev[card]->dma_resource[0].flags = 0; - - opl_dev[card] = activate_dev(sb_isapnp_list[slot].name, "opl3", opl_dev[card]); - } else - printk(KERN_ERR "sb: %s isapnp panic: opl3 device not found\n", sb_isapnp_list[slot].name); + if(!strncmp("ESS",scc->card_id,3)) { + scc->conf.io_base = pnp_port_start(dev,0); + scc->conf.irq = pnp_irq(dev,0); + scc->conf.dma = pnp_dma(dev,0); + scc->conf.dma2 = pnp_dma(dev,1); + scc->mpucnf.io_base = pnp_port_start(dev,2); + return; } - - /* Cards with MPU as part of Audio device (CTL and ESS) */ - if(!sb_isapnp_list[slot].mpu_vendor) { - mpu_config->io_base = sb_dev[card]->resource[sb_isapnp_list[slot].mpu_io].start; - return(sb_dev[card]); + if(!strncmp("CMI",scc->card_id,3)) { + scc->conf.io_base = pnp_port_start(dev,0); + scc->conf.irq = pnp_irq(dev,0); + scc->conf.dma = pnp_dma(dev,0); + scc->conf.dma2 = pnp_dma(dev,1); + return; } - - /* Cards with separate MPU device (ALS, CMI, etc.) */ - if(!uart401) - return(sb_dev[card]); - if((mpu_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].mpu_vendor, sb_isapnp_list[slot].mpu_function, NULL))) - { - int ret = mpu_dev[card]->prepare(mpu_dev[card]); - /* If device is active, assume configured with /proc/isapnp - * and use anyway */ - if(ret && ret != -EBUSY) { - printk(KERN_ERR "sb: MPU device could not be autoconfigured.\n"); - return(sb_dev[card]); - } - if(ret == -EBUSY) - mpu_activated[card] = 1; - - /* Some cards ask for irq but don't need them - azummo */ - if(sb_isapnp_list[slot].mpu_irq == -1) - mpu_dev[card]->irq_resource[0].flags = 0; - - if((mpu_dev[card] = activate_dev(sb_isapnp_list[slot].name, "mpu", mpu_dev[card]))) { - mpu_config->io_base = mpu_dev[card]->resource[sb_isapnp_list[slot].mpu_io].start; - if(sb_isapnp_list[slot].mpu_irq != -1) - mpu_config->irq = mpu_dev[card]->irq_resource[sb_isapnp_list[slot].mpu_irq].start; + if(!strncmp("RWB",scc->card_id,3)) { + scc->conf.io_base = pnp_port_start(dev,0); + scc->conf.irq = pnp_irq(dev,0); + scc->conf.dma = pnp_dma(dev,0); + return; + } + if(!strncmp("ALS",scc->card_id,3)) { + if(!strncmp("ALS0007",scc->card_id,7)) { + scc->conf.io_base = pnp_port_start(dev,0); + scc->conf.irq = pnp_irq(dev,0); + scc->conf.dma = pnp_dma(dev,0); + } else { + scc->conf.io_base = pnp_port_start(dev,0); + scc->conf.irq = pnp_irq(dev,0); + scc->conf.dma = pnp_dma(dev,1); + scc->conf.dma2 = pnp_dma(dev,0); } + return; + } + if(!strncmp("RTL",scc->card_id,3)) { + scc->conf.io_base = pnp_port_start(dev,0); + scc->conf.irq = pnp_irq(dev,0); + scc->conf.dma = pnp_dma(dev,1); + scc->conf.dma2 = pnp_dma(dev,0); } - else - printk(KERN_ERR "sb: %s isapnp panic: mpu not found\n", sb_isapnp_list[slot].name); - - return(sb_dev[card]); } -static int __init sb_isapnp_init(struct address_info *hw_config, struct address_info *mpu_config, struct pci_bus *bus, int slot, int card) +/* Probe callback function for the PnP API */ +static int sb_pnp_probe(struct pnp_card *card, const struct pnp_card_id *card_id) { - char *busname = bus->name[0] ? bus->name : sb_isapnp_list[slot].name; - - printk(KERN_INFO "sb: %s detected\n", busname); - - /* Initialize this baby. */ - - if(sb_init(bus, hw_config, mpu_config, slot, card)) { - /* We got it. */ - - printk(KERN_NOTICE "sb: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n", - busname, - hw_config->io_base, hw_config->irq, hw_config->dma, - hw_config->dma2); - return 1; + struct sb_card_config *scc; + struct sb_module_options sbmo = {0}; /* Default to 0 for PnP */ + struct pnp_dev *dev = pnp_request_card_device(card, card_id->devs[0].id, NULL); + + if(!dev){ + return -EBUSY; } - else - printk(KERN_INFO "sb: Failed to initialize %s\n", busname); - return 0; -} - -static int __init sb_isapnp_probe(struct address_info *hw_config, struct address_info *mpu_config, int card) -{ - static int first = 1; - int i; + if((scc = kmalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "sb: Error: Could not allocate memory\n"); + return -ENOMEM; + } + memset(scc, 0, sizeof(struct sb_card_config)); - /* Count entries in sb_isapnp_list */ - for (i = 0; sb_isapnp_list[i].card_vendor != 0; i++); - i--; + printk(KERN_INFO "sb: PnP: Found Card Named = \"%s\", Card PnP id = " \ + "%s, Device PnP id = %s\n", dev->dev.name, card_id->id, + dev->id->id); - /* Check and adjust isapnpjump */ - if( isapnpjump < 0 || isapnpjump > i) { - isapnpjump = reverse ? i : 0; - printk(KERN_ERR "sb: Valid range for isapnpjump is 0-%d. Adjusted to %d.\n", i, isapnpjump); - } + scc->card_id = card_id->id; + scc->dev_id = dev->id->id; + sb_dev2cfg(dev, scc); - if(!first || !reverse) - i = isapnpjump; - first = 0; - while(sb_isapnp_list[i].card_vendor != 0) { - static struct pci_bus *bus = NULL; + printk(KERN_INFO "sb: PnP: Detected at: io=0x%x, irq=%d, " \ + "dma=%d, dma16=%d\n", scc->conf.io_base, scc->conf.irq, + scc->conf.dma, scc->conf.dma2); - while ((bus = isapnp_find_card( - sb_isapnp_list[i].card_vendor, - sb_isapnp_list[i].card_device, - bus))) { - - if(sb_isapnp_init(hw_config, mpu_config, bus, i, card)) { - isapnpjump = i; /* start next search from here */ - return 0; - } - } - i += reverse ? -1 : 1; - } + pnpc_set_drvdata(card, scc); - return -ENODEV; + return sb_register_oss(scc, &sbmo); } -#endif -static int __init init_sb(void) +static void sb_pnp_remove(struct pnp_card *card) { - int card, max = (multiple && isapnp) ? SB_CARDS_MAX : 1; + struct sb_card_config *scc = pnpc_get_drvdata(card); - printk(KERN_INFO "Soundblaster audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); - - for(card = 0; card < max; card++, sb_cards_num++) { -#ifdef __ISAPNP__ - /* Please remember that even with __ISAPNP__ defined one - * should still be able to disable PNP support for this - * single driver! */ - if((!pnplegacy||card>0) && isapnp && (sb_isapnp_probe(&cfg[card], &cfg_mpu[card], card) < 0) ) { - if(!sb_cards_num) { - /* Found no ISAPnP cards, so check for a non-pnp - * card and set the detection loop for 1 cycle - */ - printk(KERN_NOTICE "sb: No ISAPnP cards found, trying standard ones...\n"); - isapnp = 0; - max = 1; - } else - /* found all the ISAPnP cards so exit the - * detection loop. */ - break; - } -#endif + if(!scc) + return; - if(!isapnp || (pnplegacy&&card==0)) { - cfg[card].io_base = io; - cfg[card].irq = irq; - cfg[card].dma = dma; - cfg[card].dma2 = dma16; - } + printk(KERN_INFO "sb: PnP: Removing %s\n", scc->card_id); - cfg[card].card_subtype = type; + sb_unload(scc); +} - if (!probe_sb(&cfg[card])) { - /* if one or more cards already registered, don't - * return an error but print a warning. Note, this - * should never really happen unless the hardware - * or ISAPnP screwed up. */ - if (sb_cards_num) { - printk(KERN_WARNING "sb.c: There was a " \ - "problem probing one of your SoundBlaster " \ - "ISAPnP soundcards. Continuing.\n"); - card--; - sb_cards_num--; - continue; - } else if(pnplegacy && isapnp) { - printk(KERN_NOTICE "sb: No legacy SoundBlaster cards " \ - "found. Continuing with PnP detection.\n"); - pnplegacy=0; - card--; - continue; - } else - return -ENODEV; - } - attach_sb_card(&cfg[card]); +static struct pnpc_driver sb_pnp_driver = { + .name = "OSS SndBlstr", /* 16 character limit */ + .id_table = sb_pnp_card_table, + .probe = sb_pnp_probe, + .remove = sb_pnp_remove, +}; +#endif /* CONFIG_PNP_CARD */ - if(cfg[card].slots[0]==-1) { - if(card==0 && pnplegacy && isapnp) { - printk(KERN_NOTICE "sb: No legacy SoundBlaster cards " \ - "found. Continuing with PnP detection.\n"); - pnplegacy=0; - card--; - continue; - } else - return -ENODEV; - } - - if (!isapnp||(pnplegacy&&card==0)) - cfg_mpu[card].io_base = mpu_io; - if (probe_sbmpu(&cfg_mpu[card], THIS_MODULE)) - sbmpu[card] = 1; +static int __init sb_init(void) +{ + int lres = 0; + int pres = 0; + + printk(KERN_INFO "sb: Init: Starting Probe...\n"); + + if(io != -1 && irq != -1 && dma != -1) { + printk(KERN_INFO "sb: Probing legacy card with io=%x, "\ + "irq=%d, dma=%d, dma16=%d\n",io, irq, dma, dma16); + lres = sb_init_legacy(); + } else if((io != -1 || irq != -1 || dma != -1) || + (!pnp && (io == -1 && irq == -1 && dma == -1))) + printk(KERN_ERR "sb: Error: At least io, irq, and dma "\ + "must be set for legacy cards.\n"); + +#ifdef CONFIG_PNP_CARD + if(pnp) { + pres = pnpc_register_driver(&sb_pnp_driver); } +#endif + printk(KERN_INFO "sb: Init: Done\n"); - if(isapnp) - printk(KERN_NOTICE "sb: %d Soundblaster PnP card(s) found.\n", sb_cards_num); - - return 0; + /* If either PnP or Legacy registered a card then return + * success */ + return (pres > 0 || lres > 0) ? 0 : -ENODEV; } -static void __exit cleanup_sb(void) +static void __exit sb_exit(void) { - int i; - - if (smw_free) { - vfree(smw_free); - smw_free = NULL; - } + printk(KERN_INFO "sb: Unloading...\n"); - for(i = 0; i < sb_cards_num; i++) { - unload_sb(&cfg[i], i); - if (sbmpu[i]) - unload_sbmpu(&cfg_mpu[i]); + /* Unload legacy card */ + if (legacy) { + printk (KERN_INFO "sb: Unloading legacy card\n"); + sb_unload(legacy); + } -#ifdef __ISAPNP__ - if(!audio_activated[i] && sb_dev[i]) - sb_dev[i]->deactivate(sb_dev[i]); - if(!mpu_activated[i] && mpu_dev[i]) - mpu_dev[i]->deactivate(mpu_dev[i]); - if(!opl_activated[i] && opl_dev[i]) - opl_dev[i]->deactivate(opl_dev[i]); +#ifdef CONFIG_PNP_CARD + pnpc_unregister_driver(&sb_pnp_driver); #endif + + if (smw_free) { + vfree(smw_free); + smw_free = NULL; } } -module_init(init_sb); -module_exit(cleanup_sb); - -#ifndef MODULE -static int __init setup_sb(char *str) -{ - /* io, irq, dma, dma2 - just the basics */ - int ints[5]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma16 = ints[4]; - - return 1; -} -__setup("sb=", setup_sb); -#endif +module_init(sb_init); +module_exit(sb_exit); diff --git a/sound/oss/sb_card.h b/sound/oss/sb_card.h new file mode 100644 index 0000000000000000000000000000000000000000..4d61f17e2ba7ac14db4d4213a5df65be825637fb --- /dev/null +++ b/sound/oss/sb_card.h @@ -0,0 +1,147 @@ +/* + * sound/oss/sb_card.h + * + * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this + * software for more info. + * + * 02-05-2002 Original Release, Paul Laufer <paul@laufernet.com> + */ + +struct sb_card_config { + struct address_info conf; + struct address_info mpucnf; + const char *card_id; + const char *dev_id; + int mpu; +}; + +#ifdef CONFIG_PNP_CARD + +/* + * SoundBlaster PnP tables and structures. + */ + +/* Card PnP ID Table */ +static struct pnp_card_id sb_pnp_card_table[] = { + /* Sound Blaster 16 */ + {.id = "CTL0024", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster 16 */ + {.id = "CTL0025", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster 16 */ + {.id = "CTL0026", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster 16 */ + {.id = "CTL0027", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster 16 */ + {.id = "CTL0028", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster 16 */ + {.id = "CTL0029", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster 16 */ + {.id = "CTL002a", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster 16 */ + {.id = "CTL002b", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster 16 */ + {.id = "CTL002c", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster 16 */ + {.id = "CTL00ed", .driver_data = 0, devs : { {.id="CTL0041"}, } }, + /* Sound Blaster 16 */ + {.id = "CTL0086", .driver_data = 0, devs : { {.id="CTL0041"}, } }, + /* Sound Blaster Vibra16S */ + {.id = "CTL0051", .driver_data = 0, devs : { {.id="CTL0001"}, } }, + /* Sound Blaster Vibra16C */ + {.id = "CTL0070", .driver_data = 0, devs : { {.id="CTL0001"}, } }, + /* Sound Blaster Vibra16CL */ + {.id = "CTL0080", .driver_data = 0, devs : { {.id="CTL0041"}, } }, + /* Sound Blaster Vibra16CL */ + {.id = "CTL00F0", .driver_data = 0, devs : { {.id="CTL0043"}, } }, + /* Sound Blaster AWE 32 */ + {.id = "CTL0039", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster AWE 32 */ + {.id = "CTL0042", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster AWE 32 */ + {.id = "CTL0043", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster AWE 32 */ + {.id = "CTL0044", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster AWE 32 */ + {.id = "CTL0045", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster AWE 32 */ + {.id = "CTL0046", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster AWE 32 */ + {.id = "CTL0047", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster AWE 32 */ + {.id = "CTL0048", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster AWE 32 */ + {.id = "CTL0054", .driver_data = 0, devs : { {.id="CTL0031"}, } }, + /* Sound Blaster AWE 32 */ + {.id = "CTL009C", .driver_data = 0, devs : { {.id="CTL0041"}, } }, + /* Createive SB32 PnP */ + {.id = "CTL009F", .driver_data = 0, devs : { {.id="CTL0041"}, } }, + /* Sound Blaster AWE 64 */ + {.id = "CTL009D", .driver_data = 0, devs : { {.id="CTL0042"}, } }, + /* Sound Blaster AWE 64 Gold */ + {.id = "CTL009E", .driver_data = 0, devs : { {.id="CTL0044"}, } }, + /* Sound Blaster AWE 64 Gold */ + {.id = "CTL00B2", .driver_data = 0, devs : { {.id="CTL0044"}, } }, + /* Sound Blaster AWE 64 */ + {.id = "CTL00C1", .driver_data = 0, devs : { {.id="CTL0042"}, } }, + /* Sound Blaster AWE 64 */ + {.id = "CTL00C3", .driver_data = 0, devs : { {.id="CTL0045"}, } }, + /* Sound Blaster AWE 64 */ + {.id = "CTL00C5", .driver_data = 0, devs : { {.id="CTL0045"}, } }, + /* Sound Blaster AWE 64 */ + {.id = "CTL00C7", .driver_data = 0, devs : { {.id="CTL0045"}, } }, + /* Sound Blaster AWE 64 */ + {.id = "CTL00E4", .driver_data = 0, devs : { {.id="CTL0045"}, } }, + /* Sound Blaster AWE 64 */ + {.id = "CTL00E9", .driver_data = 0, devs : { {.id="CTL0045"}, } }, + /* ESS 1868 */ + {.id = "ESS0968", .driver_data = 0, devs : { {.id="ESS0968"}, } }, + /* ESS 1868 */ + {.id = "ESS1868", .driver_data = 0, devs : { {.id="ESS1868"}, } }, + /* ESS 1868 */ + {.id = "ESS1868", .driver_data = 0, devs : { {.id="ESS8611"}, } }, + /* ESS 1869 PnP AudioDrive */ + {.id = "ESS0003", .driver_data = 0, devs : { {.id="ESS1869"}, } }, + /* ESS 1869 */ + {.id = "ESS1869", .driver_data = 0, devs : { {.id="ESS1869"}, } }, + /* ESS 1878 */ + {.id = "ESS1878", .driver_data = 0, devs : { {.id="ESS1878"}, } }, + /* ESS 1879 */ + {.id = "ESS1879", .driver_data = 0, devs : { {.id="ESS1879"}, } }, + /* CMI 8330 SoundPRO */ + {.id = "CMI0001", .driver_data = 0, devs : { {.id="@X@0001"}, + {.id="@H@0001"}, + {.id="@@@0001"}, } }, + /* Diamond DT0197H */ + {.id = "RWR1688", .driver_data = 0, devs : { {.id="@@@0001"}, + {.id="@X@0001"}, + {.id="@H@0001"}, } }, + /* ALS007 */ + {.id = "ALS0007", .driver_data = 0, devs : { {.id="@@@0001"}, + {.id="@X@0001"}, + {.id="@H@0001"}, } }, + /* ALS100 */ + {.id = "ALS0001", .driver_data = 0, devs : { {.id="@@@0001"}, + {.id="@X@0001"}, + {.id="@H@0001"}, } }, + /* ALS110 */ + {.id = "ALS0110", .driver_data = 0, devs : { {.id="@@@1001"}, + {.id="@X@1001"}, + {.id="@H@0001"}, } }, + /* ALS120 */ + {.id = "ALS0120", .driver_data = 0, devs : { {.id="@@@2001"}, + {.id="@X@2001"}, + {.id="@H@0001"}, } }, + /* ALS200 */ + {.id = "ALS0200", .driver_data = 0, devs : { {.id="@@@0020"}, + {.id="@X@0030"}, + {.id="@H@0001"}, } }, + /* ALS200 */ + {.id = "RTL3000", .driver_data = 0, devs : { {.id="@@@2001"}, + {.id="@X@2001"}, + {.id="@H@0001"}, } }, + /* -end- */ + {.id = "", } +}; + +#endif