2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* sound/oss/sb_card.c
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* 02-05-2003 Original Release, Paul Laufer <paul@laufernet.com>
|
|
|
|
* 02-07-2003 Bug made it into first release. Take two.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/moduleparam.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/init.h>
|
|
|
|
#include "sound_config.h"
|
|
|
|
#include "sb_mixer.h"
|
|
|
|
#include "sb.h"
|
|
|
|
#ifdef CONFIG_PNP
|
|
|
|
#include <linux/pnp.h>
|
|
|
|
#endif /* CONFIG_PNP */
|
|
|
|
#include "sb_card.h"
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("OSS Soundblaster ISA PnP and legacy sound driver");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
|
|
extern void *smw_free;
|
|
|
|
|
|
|
|
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;
|
|
|
|
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 struct sb_card_config *legacy = NULL;
|
|
|
|
|
|
|
|
#ifdef CONFIG_PNP
|
2006-03-27 17:17:16 +08:00
|
|
|
static int pnp_registered;
|
2005-04-17 06:20:36 +08:00
|
|
|
static int __initdata pnp = 1;
|
|
|
|
/*
|
|
|
|
static int __initdata uart401 = 0;
|
|
|
|
*/
|
|
|
|
#else
|
|
|
|
static int __initdata pnp = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
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
|
|
|
|
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 */
|
|
|
|
|
|
|
|
/* 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 (!request_region(scc->conf.io_base, 16, "soundblaster")) {
|
|
|
|
printk(KERN_ERR "sb: ports busy.\n");
|
|
|
|
kfree(scc);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!sb_dsp_detect(&scc->conf, 0, 0, sbmo)) {
|
|
|
|
release_region(scc->conf.io_base, 16);
|
|
|
|
printk(KERN_ERR "sb: Failed DSP Detect.\n");
|
|
|
|
kfree(scc);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sb_unload(struct sb_card_config *scc)
|
|
|
|
{
|
|
|
|
sb_dsp_unload(&scc->conf, 0);
|
|
|
|
if(scc->mpu)
|
|
|
|
unload_sbmpu(&scc->mpucnf);
|
|
|
|
kfree(scc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Register legacy card with OSS subsystem */
|
2006-03-27 17:17:16 +08:00
|
|
|
static int __init sb_init_legacy(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct sb_module_options sbmo = {0};
|
|
|
|
|
2007-02-14 16:33:16 +08:00
|
|
|
if((legacy = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) {
|
2005-04-17 06:20:36 +08:00
|
|
|
printk(KERN_ERR "sb: Error: Could not allocate memory\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
legacy->conf.io_base = io;
|
|
|
|
legacy->conf.irq = irq;
|
|
|
|
legacy->conf.dma = dma;
|
|
|
|
legacy->conf.dma2 = dma16;
|
|
|
|
legacy->conf.card_subtype = type;
|
|
|
|
|
|
|
|
legacy->mpucnf.io_base = mpu_io;
|
|
|
|
legacy->mpucnf.irq = -1;
|
|
|
|
legacy->mpucnf.dma = -1;
|
|
|
|
legacy->mpucnf.dma2 = -1;
|
|
|
|
|
|
|
|
sbmo.esstype = esstype;
|
|
|
|
sbmo.sm_games = sm_games;
|
|
|
|
sbmo.acer = acer;
|
|
|
|
|
|
|
|
return sb_register_oss(legacy, &sbmo);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_PNP
|
|
|
|
|
|
|
|
/* Populate the OSS subsystem structures with information from PnP */
|
|
|
|
static void sb_dev2cfg(struct pnp_dev *dev, struct sb_card_config *scc)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
if(!strncmp("tBA",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;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-27 17:17:16 +08:00
|
|
|
static unsigned int sb_pnp_devices;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Probe callback function for the PnP API */
|
|
|
|
static int sb_pnp_probe(struct pnp_card_link *card, const struct pnp_card_device_id *card_id)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-02-14 16:33:16 +08:00
|
|
|
if((scc = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) {
|
2005-04-17 06:20:36 +08:00
|
|
|
printk(KERN_ERR "sb: Error: Could not allocate memory\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
printk(KERN_INFO "sb: PnP: Found Card Named = \"%s\", Card PnP id = " \
|
|
|
|
"%s, Device PnP id = %s\n", card->card->name, card_id->id,
|
|
|
|
dev->id->id);
|
|
|
|
|
|
|
|
scc->card_id = card_id->id;
|
|
|
|
scc->dev_id = dev->id->id;
|
|
|
|
sb_dev2cfg(dev, scc);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
pnp_set_card_drvdata(card, scc);
|
2006-03-27 17:17:16 +08:00
|
|
|
sb_pnp_devices++;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return sb_register_oss(scc, &sbmo);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sb_pnp_remove(struct pnp_card_link *card)
|
|
|
|
{
|
|
|
|
struct sb_card_config *scc = pnp_get_card_drvdata(card);
|
|
|
|
|
|
|
|
if(!scc)
|
|
|
|
return;
|
|
|
|
|
|
|
|
printk(KERN_INFO "sb: PnP: Removing %s\n", scc->card_id);
|
|
|
|
|
|
|
|
sb_unload(scc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct pnp_card_driver sb_pnp_driver = {
|
|
|
|
.name = "OSS SndBlstr", /* 16 character limit */
|
|
|
|
.id_table = sb_pnp_card_table,
|
|
|
|
.probe = sb_pnp_probe,
|
|
|
|
.remove = sb_pnp_remove,
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pnp_card, sb_pnp_card_table);
|
|
|
|
#endif /* CONFIG_PNP */
|
|
|
|
|
2007-07-02 03:06:37 +08:00
|
|
|
static void sb_unregister_all(void)
|
2006-03-27 17:17:16 +08:00
|
|
|
{
|
|
|
|
#ifdef CONFIG_PNP
|
|
|
|
if (pnp_registered)
|
|
|
|
pnp_unregister_card_driver(&sb_pnp_driver);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
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
|
|
|
|
if(pnp) {
|
2006-03-27 17:17:16 +08:00
|
|
|
int err = pnp_register_card_driver(&sb_pnp_driver);
|
|
|
|
if (!err)
|
|
|
|
pnp_registered = 1;
|
|
|
|
pres = sb_pnp_devices;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
printk(KERN_INFO "sb: Init: Done\n");
|
|
|
|
|
|
|
|
/* If either PnP or Legacy registered a card then return
|
|
|
|
* success */
|
2006-03-27 17:17:16 +08:00
|
|
|
if (pres == 0 && lres <= 0) {
|
|
|
|
sb_unregister_all();
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit sb_exit(void)
|
|
|
|
{
|
|
|
|
printk(KERN_INFO "sb: Unloading...\n");
|
|
|
|
|
|
|
|
/* Unload legacy card */
|
|
|
|
if (legacy) {
|
|
|
|
printk (KERN_INFO "sb: Unloading legacy card\n");
|
|
|
|
sb_unload(legacy);
|
|
|
|
}
|
|
|
|
|
2006-03-27 17:17:16 +08:00
|
|
|
sb_unregister_all();
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-03-28 17:56:49 +08:00
|
|
|
vfree(smw_free);
|
|
|
|
smw_free = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(sb_init);
|
|
|
|
module_exit(sb_exit);
|