--- linux-devel-virgin/drivers/sound/sb_card.c Sat Mar 25 15:52:34 2000 +++ linux/drivers/sound/sb_card.c Mon Mar 27 15:38:43 2000 @@ -31,7 +31,10 @@ * * 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. + * Code cleanup plus some fixes. + * + * 27-03-2000 Big code cleanup and reorganization. ISAPnP multiple + * card detection. Paul Laufer * */ @@ -47,7 +50,14 @@ #include "sb_mixer.h" #include "sb.h" -static int sbmpu = 0; +#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE +#define SB_CARDS_MAX 4 +#else +#define SB_CARDS_MAX 1 +#endif + +static int sbmpu[SB_CARDS_MAX] = {0}; +static int sb_cards_num = 0; extern void *smw_free; @@ -55,7 +65,6 @@ { if(!sb_dsp_init(hw_config)) hw_config->slots[0] = -1; - SOUND_LOCK; } static int __init probe_sb(struct address_info *hw_config) @@ -126,30 +135,22 @@ } } #endif - - /* This is useless since is done by sb_dsp_detect - azummo */ - - if (check_region(hw_config->io_base, 16)) - { - printk(KERN_ERR "sb_card: I/O port 0x%x is already in use\n\n", hw_config->io_base); - return 0; - } return sb_dsp_detect(hw_config, 0, 0); } -static void __exit unload_sb(struct address_info *hw_config) +static void __exit unload_sb(struct address_info *hw_config, int card) { if(hw_config->slots[0]!=-1) - sb_dsp_unload(hw_config, sbmpu); + sb_dsp_unload(hw_config, sbmpu[card]); } extern int esstype; /* ESS chip type */ -static struct address_info cfg; -static struct address_info cfg_mpu; +static struct address_info cfg[SB_CARDS_MAX]; +static struct address_info cfg_mpu[SB_CARDS_MAX]; -struct pci_dev *sb_dev = NULL, - *mpu_dev = NULL; +struct pci_dev *sb_dev[SB_CARDS_MAX] = {NULL}, + *mpu_dev[SB_CARDS_MAX] = {NULL}; /* * 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 @@ -167,9 +168,12 @@ #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE static int isapnp = 1; static int isapnpjump = 0; -static int activated = 1; +static int multiple = 1; +static int reverse = 0; +static int activated[SB_CARDS_MAX] = {0}; #else static int isapnp = 0; +static int multiple = 0; #endif MODULE_DESCRIPTION("Soundblaster driver"); @@ -187,8 +191,12 @@ #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE MODULE_PARM(isapnp, "i"); MODULE_PARM(isapnpjump, "i"); +MODULE_PARM(multiple, "i"); +MODULE_PARM(reverse, "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(reverse, "When set to 1, will reverse ISAPnP search order"); #endif MODULE_PARM_DESC(io, "Soundblaster i/o base address (0x220,0x240,0x260,0x280)"); @@ -203,23 +211,155 @@ #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE +/* 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;} +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 }, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0026), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0027), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0029), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002b), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0}, + {"Sound Blaster Vibra16S", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0051), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), + 0,0}, + {"Sound Blaster Vibra16C", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0070), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), + 0,0}, + {"Sound Blaster Vibra16CL", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0080), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), + 0,0}, + {"Sound Blaster Vibra16X", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00F0), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0043), + 0,0}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0039), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0042), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0043), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0044), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0048), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0054), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009C), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), + 0,0}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009D), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), + 0,0}, + {"Sound Blaster AWE 64 Gold", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009E), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), + 0,0}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C1), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), + 0,0}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C3), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C5), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C7), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E4), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0}, + {"ESS 1868", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), + 0,0}, + {"ESS 1868", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611), + 0,0}, + {"ESS 1869 PnP AudioDrive", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0003), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), + 0,0}, + {"ESS 1869", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1869), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), + 0,0}, + {"ESS 1878", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1878), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878), + 0,0}, + {"ESS 1879", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1879), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879), + 0,0}, + {"CMI 8330 SoundPRO", + ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001)}, + {"Diamond DT0197H", + ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001)}, + {"ALS 100", + ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001)}, + {0} +}; + /* That's useful. */ #define show_base(devname, resname, resptr) printk(KERN_INFO "sb: %s %s base located at %#lx\n", devname, resname, (resptr)->start) -static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev) +static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev, int card) { int err; /* Device already active? Let's use it */ - - if(dev->active) - { - activated = 0; + if(dev->active) { + activated[card] = 1; return(dev); } - if((err = dev->activate(dev)) < 0) - { + + 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); @@ -229,320 +369,102 @@ return(dev); } -/* Card's specific initialization functions - */ - -static struct pci_dev *sb_init_generic(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config) +static struct pci_dev *sb_init(struct pci_bus *bus, struct address_info *hw_config, struct address_info *mpu_config, int slot, int card) { - if((sb_dev = isapnp_find_dev(bus, card->vendor, card->device, NULL))) - { - sb_dev->prepare(sb_dev); + /* Configure Audio device */ - if((sb_dev = activate_dev("Soundblaster", "sb", sb_dev))) - { - hw_config->io_base = sb_dev->resource[0].start; - hw_config->irq = sb_dev->irq_resource[0].start; - hw_config->dma = sb_dev->dma_resource[0].start; - hw_config->dma2 = sb_dev->dma_resource[1].start; - mpu_config->io_base = sb_dev->resource[1].start; - } - } - return(sb_dev); -} - -static struct pci_dev *sb_init_ess(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config) -{ - if((sb_dev = isapnp_find_dev(bus, card->vendor, card->device, NULL))) + if((sb_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].audio_vendor, sb_isapnp_list[slot].audio_function, NULL))) { - sb_dev->prepare(sb_dev); - - if((sb_dev = activate_dev("ESS", "sb", sb_dev))) - { - hw_config->io_base = sb_dev->resource[0].start; - hw_config->irq = sb_dev->irq_resource[0].start; - hw_config->dma = sb_dev->dma_resource[0].start; - hw_config->dma2 = sb_dev->dma_resource[1].start; - mpu_config->io_base = sb_dev->resource[2].start; - } - } - return(sb_dev); -} - -static struct pci_dev *sb_init_cmi(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config) -{ - /* - * The CMI8330/C3D is a very 'stupid' chip... where did they get al those @@@ ? - * It's ISAPnP section is badly designed and has many flaws, i'll do my best - * to workaround them. I strongly suggest you to buy a real soundcard. - * The CMI8330 on my motherboard has also the bad habit to activate - * the rear channel of my amplifier instead of the front one. - */ - - /* @X@0001:Soundblaster. - */ + sb_dev[card]->prepare(sb_dev[card]); - if((sb_dev = isapnp_find_dev(bus, - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), NULL))) - { - sb_dev->prepare(sb_dev); - - if((sb_dev = activate_dev("CMI8330", "sb", sb_dev))) + if((sb_dev[card] = activate_dev(sb_isapnp_list[slot].name, "sb", sb_dev[card], card))) { - hw_config->io_base = sb_dev->resource[0].start; - hw_config->irq = sb_dev->irq_resource[0].start; - hw_config->dma = sb_dev->dma_resource[0].start; - hw_config->dma2 = sb_dev->dma_resource[1].start; - - show_base("CMI8330", "sb", &sb_dev->resource[0]); - } + 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[0].start; + hw_config->dma2 = sb_dev[card]->dma_resource[1].start; + } else + return(NULL); + } else + return(NULL); - if(!sb_dev) return(NULL); + /* Cards with MPU part of Audio device */ + + if(!sb_isapnp_list[slot].mpu_vendor) { + mpu_config->io_base = sb_dev[card]->resource[1].start; + return(sb_dev[card]); } - else - printk(KERN_ERR "sb: CMI8330 panic: sb base not found\n"); - /* @H@0001:mpu - */ + /* Cards with separate MPU device */ - if((mpu_dev = isapnp_find_dev(bus, - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), NULL))) + if((mpu_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].mpu_vendor, sb_isapnp_list[slot].mpu_function, NULL))) { - mpu_dev->prepare(mpu_dev); - - /* This disables the interrupt on this resource. Do we need it ? - */ - - mpu_dev->irq_resource[0].flags = 0; + mpu_dev[card]->prepare(mpu_dev[card]); - if((mpu_dev = activate_dev("CMI8330", "mpu", mpu_dev))) - { - show_base("CMI8330", "mpu", &mpu_dev->resource[0]); - mpu_config->io_base = mpu_dev->resource[0].start; - } + if((mpu_dev[card] = activate_dev(sb_isapnp_list[slot].name, "mpu", mpu_dev[card], card))) + mpu_config->io_base = mpu_dev[card]->resource[0].start; } else - printk(KERN_ERR "sb: CMI8330 panic: mpu not found\n"); - - printk(KERN_INFO "sb: CMI8330 mail reports to Alessandro Zummo \n"); - - return(sb_dev); + printk(KERN_ERR "sb: %s panic: mpu not found\n", sb_isapnp_list[slot].name); + + return(sb_dev[card]); } -static struct pci_dev *sb_init_diamond(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config) +static int __init sb_isapnp_init(struct address_info *hw_config, struct address_info *mpu_config, struct pci_bus *bus, int slot, int card) { - /* - * Diamonds DT0197H - * very similar to the CMI8330 above - */ - - /* @@@0001:Soundblaster. - */ - - if((sb_dev = isapnp_find_dev(bus, - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), NULL))) - { - sb_dev->prepare(sb_dev); - - if((sb_dev = activate_dev("DT0197H", "sb", sb_dev))) - { - hw_config->io_base = sb_dev->resource[0].start; - hw_config->irq = sb_dev->irq_resource[0].start; - hw_config->dma = sb_dev->dma_resource[0].start; - hw_config->dma2 = -1; - - show_base("DT0197H", "sb", &sb_dev->resource[0]); - } - - if(!sb_dev) return(NULL); - } - else - printk(KERN_ERR "sb: DT0197H panic: sb base not found\n"); - - /* @X@0001:mpu - */ - - if((mpu_dev = isapnp_find_dev(bus, - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), NULL))) - { - mpu_dev->prepare(mpu_dev); - - if((mpu_dev = activate_dev("DT0197H", "mpu", mpu_dev))) - { - show_base("DT0197H", "mpu", &mpu_dev->resource[0]); - mpu_config->io_base = mpu_dev->resource[0].start; - } - } - else - printk(KERN_ERR "sb: DT0197H panic: mpu not found\n"); - - printk(KERN_INFO "sb: DT0197H mail reports to Torsten Werner \n"); - - return(sb_dev); -} + char *busname = bus->name[0] ? bus->name : sb_isapnp_list[slot].name; -static struct pci_dev *sb_init_als(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config) -{ - /* - * ALS100 - * very similar to both ones above above - */ + printk(KERN_INFO "sb: %s detected\n", busname); - /* @@@0001:Soundblaster. - */ + /* Initialize this baby. */ - if((sb_dev = isapnp_find_dev(bus, - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), NULL))) - { - sb_dev->prepare(sb_dev); + if(sb_init(bus, hw_config, mpu_config, slot, card)) { + /* We got it. */ - if((sb_dev = activate_dev("ALS100", "sb", sb_dev))) - { - hw_config->io_base = sb_dev->resource[0].start; - hw_config->irq = sb_dev->irq_resource[0].start; - hw_config->dma = sb_dev->dma_resource[1].start; - hw_config->dma2 = sb_dev->dma_resource[0].start; - - show_base("ALS100", "sb", &sb_dev->resource[0]); - } - - if(!sb_dev) return(NULL); - } - else - printk(KERN_ERR "sb: ALS100 panic: sb base not found\n"); - - /* @X@0001:mpu - */ - - if((mpu_dev = isapnp_find_dev(bus, - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), NULL))) - { - mpu_dev->prepare(mpu_dev); - - if((mpu_dev = activate_dev("ALS100", "mpu", mpu_dev))) - { - show_base("ALS100", "mpu", &mpu_dev->resource[0]); - mpu_config->io_base = mpu_dev->resource[0].start; - } - } - else - printk(KERN_ERR "sb: ALS100 panic: mpu not found\n"); - - printk(KERN_INFO "sb: ALS100 mail reports to Torsten Werner \n"); - - return(sb_dev); -} - -#define SBF_DEV 0x01 /* Please notice that cards without this flag are on the top in the list */ - - -static struct { unsigned short vendor, function, flags; struct pci_dev * (*initfunc)(struct pci_bus *, struct pci_dev *, struct address_info *, struct address_info *); char *name; } -sb_isapnp_list[] __initdata = { - {ISAPNP_VENDOR('C','M','I'), ISAPNP_FUNCTION(0x0001), 0, &sb_init_cmi, "CMI 8330 SoundPRO" }, - {ISAPNP_VENDOR('R','W','B'), ISAPNP_FUNCTION(0x1688), 0, &sb_init_diamond, "Diamond DT0197H" }, - {ISAPNP_VENDOR('A','L','S'), ISAPNP_FUNCTION(0x0001), 0, &sb_init_als, "ALS 100" }, - {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, - {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, - {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, - {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, - {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0043), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, - {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, - {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968), SBF_DEV, &sb_init_ess, "ESS 1688" }, - {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), SBF_DEV, &sb_init_ess, "ESS 1868" }, - {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611), SBF_DEV, &sb_init_ess, "ESS 1868" }, - {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), SBF_DEV, &sb_init_ess, "ESS 1869" }, - {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878), SBF_DEV, &sb_init_ess, "ESS 1878" }, - {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879), SBF_DEV, &sb_init_ess, "ESS 1879" }, - {0} -}; - -static int __init sb_isapnp_init(struct address_info *hw_config, struct address_info *mpu_config, struct pci_bus *bus, struct pci_dev *card, int slot) -{ - struct pci_dev *idev = NULL; - - /* You missed the init func? That's bad. */ - if(sb_isapnp_list[slot].initfunc) - { - char *busname = bus->name[0] ? bus->name : sb_isapnp_list[slot].name; - - printk(KERN_INFO "sb: %s detected\n", busname); - - /* Initialize this baby. */ - - if((idev = sb_isapnp_list[slot].initfunc(bus, card, hw_config, mpu_config))) - { - /* 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; - } - else - printk(KERN_INFO "sb: Failed to initialize %s\n", busname); + 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; } else - printk(KERN_ERR "sb: Bad entry in sb_card.c PnP table\n"); + printk(KERN_INFO "sb: Failed to initialize %s\n", busname); return 0; } -/* Actually this routine will detect and configure only the first card with successful - initialization. isapnpjump could be used to jump to a specific entry. - Please always add entries at the end of the array. - Should this be fixed? - azummo -*/ - -int __init sb_isapnp_probe(struct address_info *hw_config, struct address_info *mpu_config) +int __init sb_isapnp_probe(struct address_info *hw_config, struct address_info *mpu_config, int card) { + static int first = 1; int i; /* Count entries in sb_isapnp_list */ - for (i = 0; sb_isapnp_list[i].vendor != 0; i++); + for (i = 0; sb_isapnp_list[i].card_vendor != 0; i++); + i--; /* Check and adjust isapnpjump */ - if( isapnpjump < 0 || isapnpjump > ( i - 1 ) ) - { - printk(KERN_ERR "sb: Valid range for isapnpjump is 0-%d. Adjusted to 0.\n", i-1); - isapnpjump = 0; + 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); } - - for (i = isapnpjump; sb_isapnp_list[i].vendor != 0; i++) { - if(!(sb_isapnp_list[i].flags & SBF_DEV)) - { - struct pci_bus *bus = NULL; - - while ((bus = isapnp_find_card( - sb_isapnp_list[i].vendor, - sb_isapnp_list[i].function, - bus))) { + if(!first || !reverse) + i = isapnpjump; + first = 0; + while(sb_isapnp_list[i].card_vendor != 0) { + static struct pci_bus *bus = NULL; + + 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, NULL, i)) - return 0; - } - } - } - - /* No cards found. I'll try now to search inside every card for a logical device - * that matches any entry marked with SBF_DEV in the table. - */ - - for (i = isapnpjump; sb_isapnp_list[i].vendor != 0; i++) { - - if(sb_isapnp_list[i].flags & SBF_DEV) - { - struct pci_dev *card = NULL; - - while ((card = isapnp_find_dev(NULL, - sb_isapnp_list[i].vendor, - sb_isapnp_list[i].function, - card))) { - - if(sb_isapnp_init(hw_config, mpu_config, card->bus, card, i)) - return 0; + if(sb_isapnp_init(hw_config, mpu_config, bus, i, card)) { + isapnpjump = i; /* start next search from here */ + return 0; } } + i += reverse ? -1 : 1; } return -ENODEV; @@ -551,62 +473,77 @@ static int __init init_sb(void) { - printk(KERN_INFO "Soundblaster audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + int card, max = multiple ? SB_CARDS_MAX : 1; - /* Please remember that even with CONFIG_ISAPNP defined one should still be - able to disable PNP support for this single driver! - */ - -#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE - if(isapnp && (sb_isapnp_probe(&cfg, &cfg_mpu) < 0) ) { - printk(KERN_NOTICE "sb_card: No ISAPnP cards found, trying standard ones...\n"); - isapnp = 0; - } + printk(KERN_INFO "Soundblaster audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + for(card = 0; card < max; card++, sb_cards_num++) { +#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE + /* Please remember that even with CONFIG_ISAPNP defined one should still be + able to disable PNP support for this single driver! */ + if(isapnp && (sb_isapnp_probe(&cfg[card], &cfg_mpu[card], card) < 0) ) { + if(sb_cards_num == 0) { + printk(KERN_NOTICE "sb_card: No ISAPnP cards found, trying standard ones...\n"); + isapnp = 0; + } else + break; + } #endif - if( isapnp == 0 ) { - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma16; - } + if( isapnp == 0 && sb_cards_num == 0) { + cfg[card].io_base = io; + cfg[card].irq = irq; + cfg[card].dma = dma; + cfg[card].dma2 = dma16; + } - cfg.card_subtype = type; + cfg[card].card_subtype = type; - if (!probe_sb(&cfg)) - return -ENODEV; - attach_sb_card(&cfg); + if (!probe_sb(&cfg[card])) + return -ENODEV; + attach_sb_card(&cfg[card]); - if(cfg.slots[0]==-1) - return -ENODEV; + if(cfg[card].slots[0]==-1) + return -ENODEV; - if (isapnp == 0) - cfg_mpu.io_base = mpu_io; - if (probe_sbmpu(&cfg_mpu)) - sbmpu = 1; - if (sbmpu) - attach_sbmpu(&cfg_mpu); + if (isapnp == 0) + cfg_mpu[card].io_base = mpu_io; + if (probe_sbmpu(&cfg_mpu[card])) + sbmpu[card] = 1; + if (sbmpu[card]) + attach_sbmpu(&cfg_mpu[card]); + } + + SOUND_LOCK; + + if(isapnp) + printk(KERN_NOTICE "sb_card: %d Soundblaster PnP card(s) found.\n", sb_cards_num); + return 0; } static void __exit cleanup_sb(void) { + int i; + if (smw_free) { vfree(smw_free); smw_free = NULL; } - unload_sb(&cfg); - if (sbmpu) - unload_sbmpu(&cfg_mpu); - SOUND_LOCK_END; -#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE - if(activated) - { - if(sb_dev) sb_dev->deactivate(sb_dev); - if(mpu_dev) mpu_dev->deactivate(mpu_dev); - } + for(i = 0; i < sb_cards_num; i++) { + unload_sb(&cfg[i], i); + if (sbmpu[i]) + unload_sbmpu(&cfg_mpu[i]); + +#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE + if(!activated[i]) { + if(sb_dev[i]) sb_dev[i]->deactivate(sb_dev[i]); + if(mpu_dev[i]) mpu_dev[i]->deactivate(mpu_dev[i]); + } #endif + } + SOUND_LOCK_END; } module_init(init_sb);