Messages in this thread Patch in this message | | | Date | Wed, 16 Jul 2003 12:57:03 +0200 (CEST) | From | Thorsten Knabe <> | Subject | [PATCH] 2.6.0-test1/sound/oss/ad1816.c |
| |
Hi.
The following patch fixes various problems with the AD1816 sound driver in 2.6.0-test1:
* Compilation errors with CONFIG_PNP enabled fixed. * PNP specific code rewritten. * SMP fixes. * DMA resource handling fixes. * Other minor code cleanup.
Patch attached. Please apply.
Regards Thorsten
-- E-Mail: linux@thorsten-knabe.de ___ WWW: http://linux.thorsten-knabe.de | | / |horsten |/\nabe--- linux-2.6.0-test1/sound/oss/ad1816.c-orig Tue Jul 15 21:15:37 2003 +++ linux-2.6.0-test1/sound/oss/ad1816.c Wed Jul 16 00:18:36 2003 @@ -1,15 +1,15 @@ /* * - * AD1816 lowlevel sound driver for Linux 2.2.0 and above + * AD1816 lowlevel sound driver for Linux 2.6.0 and above * - * Copyright (C) 1998 by Thorsten Knabe <tek@rbg.informatik.tu-darmstadt.de> + * Copyright (C) 1998-2003 by Thorsten Knabe <linux@thorsten-knabe.de> * * Based on the CS4232/AD1848 driver Copyright (C) by Hannu Savolainen 1993-1996 * * - * version: 1.3.1 - * status: experimental - * date: 1999/4/18 + * version: 1.5 + * status: beta + * date: 2003/07/15 * * Changes: * Oleg Drokin: Some cleanup of load/unload functions. 1998/11/24 @@ -30,8 +30,17 @@ * Christoph Hellwig: Added isapnp support 2000/03/15 * * Arnaldo Carvalho de Melo: get rid of check_region 2001/10/07 + * + * Thorsten Knabe: Compiling with CONFIG_PNP enabled + * works again. It is now possible to use more than one + * AD1816 sound card. Sample rate now may be changed during + * playback/capture. printk() uses log levels everywhere. + * SMP fixes. DMA handling fixes. + * Other minor code cleanup. 2003/07/15 + * */ + #include <linux/config.h> #include <linux/module.h> #include <linux/init.h> @@ -42,9 +51,6 @@ #include "sound_config.h" #define DEBUGNOISE(x) -#define DEBUGINFO(x) -#define DEBUGLOG(x) -#define DEBUGWARN(x) #define CHECK_FOR_POWER { int timeout=100; \ while (timeout > 0 && (inb(devc->base)&0x80)!= 0x80) {\ @@ -63,21 +69,21 @@ int dma_playback; int dma_capture; - int speed; /* open */ + int opened; /* open */ + int speed; int channels; int audio_format; - unsigned char format_bits; int audio_mode; - int opened; int recmask; /* setup */ + unsigned char format_bits; int supported_devices; int supported_rec_devices; unsigned short levels[SOUND_MIXER_NRDEVICES]; + /* misc */ + struct pnp_dev *pnpdev; /* configured via pnp */ int dev_no; /* this is the # in audio_devs and NOT in ad1816_info */ - int irq_ok; - int *osp; spinlock_t lock; } ad1816_info; @@ -85,12 +91,6 @@ static int ad1816_clockfreq = 33000; static int options; -/* for backward mapping of irq to sound device */ - -static volatile char irq2dev[17] = {-1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1}; - - /* supported audio formats */ static int ad_format_mask = AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_MU_LAW | AFMT_A_LAW; @@ -105,33 +105,22 @@ static int ad_read (ad1816_info * devc, int reg) { - unsigned long flags; int result; CHECK_FOR_POWER; - - spin_lock_irqsave(&devc->lock,flags); /* make register access atomic */ outb ((unsigned char) (reg & 0x3f), devc->base+0); result = inb(devc->base+2); result+= inb(devc->base+3)<<8; - spin_unlock_irqrestore(&devc->lock,flags); - return (result); } static void ad_write (ad1816_info * devc, int reg, int data) { - unsigned long flags; - CHECK_FOR_POWER; - - spin_lock_irqsave(&devc->lock,flags); /* make register access atomic */ outb ((unsigned char) (reg & 0xff), devc->base+0); outb ((unsigned char) (data & 0xff),devc->base+2); outb ((unsigned char) ((data>>8)&0xff),devc->base+3); - spin_unlock_irqrestore(&devc->lock,flags); - } /* ------------------------------------------------------------------- */ @@ -144,7 +133,7 @@ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; unsigned char buffer; - DEBUGINFO (printk("ad1816: halt_input called\n")); + DEBUGNOISE(printk(KERN_DEBUG "ad1816: halt_input called\n")); spin_lock_irqsave(&devc->lock,flags); @@ -176,7 +165,7 @@ unsigned char buffer; - DEBUGINFO (printk("ad1816: halt_output called!\n")); + DEBUGNOISE(printk(KERN_DEBUG "ad1816: halt_output called!\n")); spin_lock_irqsave(&devc->lock,flags); /* Mute pcm output */ @@ -210,7 +199,7 @@ unsigned long cnt; ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - DEBUGINFO(printk("ad1816: output_block called buf=%ld count=%d flags=%d\n",buf,count,intrflag)); + DEBUGNOISE(printk(KERN_DEBUG "ad1816: output_block called buf=%ld count=%d flags=%d\n",buf,count,intrflag)); cnt = count/4 - 1; @@ -231,7 +220,7 @@ unsigned long cnt; ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - DEBUGINFO(printk("ad1816: start_input called buf=%ld count=%d flags=%d\n",buf,count,intrflag)); + DEBUGNOISE(printk(KERN_DEBUG "ad1816: start_input called buf=%ld count=%d flags=%d\n",buf,count,intrflag)); cnt = count/4 - 1; @@ -239,7 +228,6 @@ /* set transfer count */ ad_write (devc, 10, cnt & 0xffff); - devc->audio_mode |= PCM_ENABLE_INPUT; spin_unlock_irqrestore(&devc->lock,flags); } @@ -251,33 +239,19 @@ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; unsigned char fmt_bits; - DEBUGINFO (printk ("ad1816: prepare_for_input called: bsize=%d bcount=%d\n",bsize,bcount)); + DEBUGNOISE(printk(KERN_DEBUG "ad1816: prepare_for_input called: bsize=%d bcount=%d\n",bsize,bcount)); spin_lock_irqsave(&devc->lock,flags); - fmt_bits= (devc->format_bits&0x7)<<3; /* set mono/stereo mode */ if (devc->channels > 1) { fmt_bits |=0x4; } - /* set Mono/Stereo in playback/capture register */ outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9); - - /* If compiled into kernel, AD1816_CLOCK is defined, so use it */ -#ifdef AD1816_CLOCK - ad1816_clockfreq=AD1816_CLOCK; -#endif - - /* capture/playback frequency correction for soundcards - with clock chips != 33MHz (allowed range 5 - 100 kHz) */ - if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) { - ad1816_clockfreq=33000; - } - freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; /* write playback/capture speeds */ @@ -297,7 +271,7 @@ ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; unsigned char fmt_bits; - DEBUGINFO (printk ("ad1816: prepare_for_output called: bsize=%d bcount=%d\n",bsize,bcount)); + DEBUGNOISE(printk(KERN_DEBUG "ad1816: prepare_for_output called: bsize=%d bcount=%d\n",bsize,bcount)); spin_lock_irqsave(&devc->lock,flags); @@ -311,17 +285,6 @@ outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9); -#ifdef AD1816_CLOCK - ad1816_clockfreq=AD1816_CLOCK; -#endif - - /* capture/playback frequency correction for soundcards - with clock chips != 33MHz (allowed range 5 - 100 kHz)*/ - - if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) { - ad1816_clockfreq=33000; - } - freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; /* write playback/capture speeds */ @@ -340,7 +303,7 @@ unsigned long flags; ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - DEBUGINFO (printk("ad1816: trigger called! (devc=%d,devc->base=%d\n", devc, devc->base)); + DEBUGNOISE(printk(KERN_DEBUG "ad1816: trigger called! (devc=%d,devc->base=%d\n", devc, devc->base)); /* mode may have changed */ @@ -388,10 +351,17 @@ /* set playback speed */ static int ad1816_set_speed (int dev, int arg) { + unsigned long flags; + unsigned int freq; + int ret; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + spin_lock_irqsave(&devc->lock, flags); if (arg == 0) { - return devc->speed; + ret = devc->speed; + spin_unlock_irqrestore(&devc->lock, flags); + return ret; } /* range checking */ if (arg < 4000) { @@ -400,14 +370,23 @@ if (arg > 55000) { arg = 55000; } - devc->speed = arg; - return devc->speed; + + /* change speed during playback */ + freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; + /* write playback/capture speeds */ + ad_write (devc, 2, freq & 0xffff); + ad_write (devc, 3, freq & 0xffff); + + ret = devc->speed; + spin_unlock_irqrestore(&devc->lock, flags); + return ret; } static unsigned int ad1816_set_bits (int dev, unsigned int arg) { + unsigned long flags; ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; static struct format_tbl { @@ -428,10 +407,13 @@ int i, n = sizeof (format2bits) / sizeof (struct format_tbl); + spin_lock_irqsave(&devc->lock, flags); /* return current format */ - if (arg == 0) - return devc->audio_format; - + if (arg == 0) { + arg = devc->audio_format; + spin_unlock_irqrestore(&devc->lock, flags); + return arg; + } devc->audio_format = arg; /* search matching format bits */ @@ -439,12 +421,15 @@ if (format2bits[i].format == arg) { devc->format_bits = format2bits[i].bits; devc->audio_format = arg; + spin_unlock_irqrestore(&devc->lock, flags); return arg; } /* Still hanging here. Something must be terribly wrong */ devc->format_bits = 0; - return devc->audio_format = AFMT_U8; + devc->audio_format = AFMT_U8; + spin_unlock_irqrestore(&devc->lock, flags); + return(AFMT_U8); } static short ad1816_set_channels (int dev, short arg) @@ -486,9 +471,8 @@ devc->speed = 8000; devc->audio_format=AFMT_U8; devc->channels=1; - - ad1816_reset(devc->dev_no); /* halt all pending output */ spin_unlock_irqrestore(&devc->lock,flags); + ad1816_reset(devc->dev_no); /* halt all pending output */ return 0; } @@ -497,17 +481,15 @@ unsigned long flags; ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - spin_lock_irqsave(&devc->lock,flags); - /* halt all pending output */ ad1816_reset(devc->dev_no); - + + spin_lock_irqsave(&devc->lock,flags); devc->opened = 0; devc->audio_mode = 0; devc->speed = 8000; devc->audio_format=AFMT_U8; devc->format_bits = 0; - spin_unlock_irqrestore(&devc->lock,flags); } @@ -543,25 +525,13 @@ static irqreturn_t ad1816_interrupt (int irq, void *dev_id, struct pt_regs *dummy) { unsigned char status; - ad1816_info *devc; - int dev; - + ad1816_info *devc = (ad1816_info *)dev_id; if (irq < 0 || irq > 15) { printk(KERN_WARNING "ad1816: Got bogus interrupt %d\n", irq); return IRQ_NONE; } - dev = irq2dev[irq]; - - if (dev < 0 || dev >= num_audiodevs) { - printk(KERN_WARNING "ad1816: IRQ2AD1816-mapping failed for " - "irq %d device %d\n", irq,dev); - return IRQ_NONE; - } - - devc = (ad1816_info *) audio_devs[dev]->devc; - spin_lock(&devc->lock); /* read interrupt register */ @@ -569,18 +539,19 @@ /* Clear all interrupt */ outb (~status, devc->base+1); - DEBUGNOISE (printk("ad1816: Got interrupt subclass %d\n",status)); - - devc->irq_ok=1; + DEBUGNOISE(printk(KERN_DEBUG "ad1816: Got interrupt subclass %d\n",status)); - if (status == 0) - DEBUGWARN(printk ("ad1816: interrupt: Got interrupt, but no reason?\n")); + if (status == 0) { + DEBUGNOISE(printk(KERN_DEBUG "ad1816: interrupt: Got interrupt, but no source.\n")); + spin_unlock(&devc->lock); + return IRQ_NONE; + } if (devc->opened && (devc->audio_mode & PCM_ENABLE_INPUT) && (status&64)) - DMAbuf_inputintr (dev); + DMAbuf_inputintr (devc->dev_no); if (devc->opened && (devc->audio_mode & PCM_ENABLE_OUTPUT) && (status & 128)) - DMAbuf_outputintr (dev, 1); + DMAbuf_outputintr (devc->dev_no, 1); spin_unlock(&devc->lock); return IRQ_HANDLED; @@ -671,9 +642,11 @@ static int ad1816_set_recmask (ad1816_info * devc, int mask) { + unsigned long flags; unsigned char recdev; int i, n; + spin_lock_irqsave(&devc->lock, flags); mask &= devc->supported_rec_devices; n = 0; @@ -733,6 +706,7 @@ (ad_read (devc, 20) & 0x8f8f) | recdev | (recdev<<8)); devc->recmask = mask; + spin_unlock_irqrestore(&devc->lock, flags); return mask; } @@ -760,7 +734,7 @@ static int ad1816_mixer_get (ad1816_info * devc, int dev) { - DEBUGINFO(printk("ad1816: mixer_get called!\n")); + DEBUGNOISE(printk(KERN_DEBUG "ad1816: mixer_get called!\n")); /* range check + supported mixer check */ if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES ) @@ -781,8 +755,9 @@ int regoffs; int val; int valmute; + unsigned long flags; - DEBUGINFO(printk("ad1816: mixer_set called!\n")); + DEBUGNOISE(printk(KERN_DEBUG "ad1816: mixer_set called!\n")); if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES ) return -(EINVAL); @@ -813,6 +788,7 @@ /* sanity check */ if (mix_devices[dev][LEFT_CHN].nbits == 0) return -(EINVAL); + spin_lock_irqsave(&devc->lock, flags); /* keep precise volume internal */ devc->levels[dev] = retvol; @@ -840,8 +816,10 @@ */ /* Was just a mono channel */ - if (mix_devices[dev][RIGHT_CHN].nbits == 0) + if (mix_devices[dev][RIGHT_CHN].nbits == 0) { + spin_unlock_irqrestore(&devc->lock, flags); return retvol; + } regoffs = mix_devices[dev][RIGHT_CHN].regno; val = ad_read (devc, regoffs); @@ -857,7 +835,7 @@ valmute &= ~0x80; } ad_write (devc, regoffs, valmute); /* mute */ - + spin_unlock_irqrestore(&devc->lock, flags); return retvol; } @@ -901,7 +879,7 @@ ad1816_info *devc = mixer_devs[dev]->devc; int val; - DEBUGINFO(printk("ad1816: mixer_ioctl called!\n")); + DEBUGNOISE(printk(KERN_DEBUG "ad1816: mixer_ioctl called!\n")); /* Mixer ioctl */ if (((cmd >> 8) & 0xff) == 'M') { @@ -980,83 +958,91 @@ /* ------------------------------------------------------------------- */ -/* stuff for card recognition, init and unloading */ +/* stuff for card recognition, init and unloading PNP ...*/ -/* replace with probe routine */ -static int __init probe_ad1816 ( struct address_info *hw_config ) +/* check if AD1816 present at specified hw_config and register device with OS + * return 1 if initialization was successful, 0 otherwise + */ +static int __init ad1816_init_card (struct address_info *hw_config, + struct pnp_dev *pnp) { - ad1816_info *devc = &dev_info[nr_ad1816_devs]; - int io_base=hw_config->io_base; - int *osp=hw_config->osp; + ad1816_info *devc = NULL; int tmp; + int oss_devno = -1; - printk(KERN_INFO "ad1816: AD1816 sounddriver " - "Copyright (C) 1998 by Thorsten Knabe\n"); - printk(KERN_INFO "ad1816: io=0x%x, irq=%d, dma=%d, dma2=%d, " - "clockfreq=%d, options=%d isadmabug=%d\n", + printk(KERN_INFO "ad1816: initializing card: io=0x%x, irq=%d, dma=%d, " + "dma2=%d, clockfreq=%d, options=%d isadmabug=%d " + "%s\n", hw_config->io_base, hw_config->irq, hw_config->dma, hw_config->dma2, ad1816_clockfreq, options, - isa_dma_bridge_buggy); - - if (!request_region(io_base, 16, "AD1816 Sound")) { - printk(KERN_WARNING "ad1816: I/O port 0x%03x not free\n", - io_base); - goto err; - } + isa_dma_bridge_buggy, + pnp?"(PNP)":""); - DEBUGLOG(printk ("ad1816: detect(%x)\n", io_base)); - + /* ad1816_info structure remaining ? */ if (nr_ad1816_devs >= MAX_AUDIO_DEV) { - printk(KERN_WARNING "ad1816: detect error - step 0\n"); - goto out_release_region; + printk(KERN_WARNING "ad1816: no more ad1816_info structures " + "left\n"); + goto out; } - devc->base = io_base; - devc->irq_ok = 0; - devc->irq = 0; + devc = &dev_info[nr_ad1816_devs]; + devc->base = hw_config->io_base; + devc->irq = hw_config->irq; + devc->dma_playback=hw_config->dma; + devc->dma_capture=hw_config->dma2; devc->opened = 0; - devc->osp = osp; + devc->pnpdev = pnp; spin_lock_init(&devc->lock); + if (!request_region(devc->base, 16, "AD1816 Sound")) { + printk(KERN_WARNING "ad1816: I/O port 0x%03x not free\n", + devc->base); + goto out; + } + + printk(KERN_INFO "ad1816: Examining AD1816 at address 0x%03x.\n", + devc->base); + + + /* tests for ad1816 */ /* base+0: bit 1 must be set but not 255 */ tmp=inb(devc->base); if ( (tmp&0x80)==0 || tmp==255 ) { - DEBUGLOG (printk ("ad1816: Chip is not an AD1816 or chip is not active (Test 0)\n")); + printk (KERN_INFO "ad1816: Chip is not an AD1816 or chip " + "is not active (Test 0)\n"); goto out_release_region; } - /* writes to ireg 8 are copied to ireg 9 */ ad_write(devc,8,12345); if (ad_read(devc,9)!=12345) { - DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 1)\n")); + printk(KERN_INFO "ad1816: Chip is not an AD1816 (Test 1)\n"); goto out_release_region; } /* writes to ireg 8 are copied to ireg 9 */ ad_write(devc,8,54321); if (ad_read(devc,9)!=54321) { - DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 2)\n")); + printk(KERN_INFO "ad1816: Chip is not an AD1816 (Test 2)\n"); goto out_release_region; } - /* writes to ireg 10 are copied to ireg 11 */ ad_write(devc,10,54321); if (ad_read(devc,11)!=54321) { - DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 3)\n")); + printk (KERN_INFO "ad1816: Chip is not an AD1816 (Test 3)\n"); goto out_release_region; } /* writes to ireg 10 are copied to ireg 11 */ ad_write(devc,10,12345); if (ad_read(devc,11)!=12345) { - DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 4)\n")); + printk (KERN_INFO "ad1816: Chip is not an AD1816 (Test 4)\n"); goto out_release_region; } @@ -1064,35 +1050,12 @@ tmp=inb(devc->base+1); outb(0xff,devc->base+1); if (inb(devc->base+1)!=tmp) { - DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 5)\n")); + printk(KERN_INFO "ad1816: Chip is not an AD1816 (Test 5)\n"); goto out_release_region; } - - DEBUGLOG (printk ("ad1816: detect() - Detected OK\n")); - DEBUGLOG (printk ("ad1816: AD1816 Version: %d\n",ad_read(devc,45))); - - /* detection was successful */ - return 1; -out_release_region: - release_region(io_base, 16); - /* detection was NOT successful */ -err: return 0; -} - - -/* allocate resources from the kernel. If any allocation fails, free - all allocated resources and exit attach. - - */ - -static void __init attach_ad1816 (struct address_info *hw_config) -{ - int my_dev; - char dev_name[100]; - ad1816_info *devc = &dev_info[nr_ad1816_devs]; - - devc->base = hw_config->io_base; + printk(KERN_INFO "ad1816: AD1816 (version %d) successfully detected!\n", + ad_read(devc,45)); /* disable all interrupts */ ad_write(devc,1,0); @@ -1101,62 +1064,54 @@ outb (0, devc->base+1); /* allocate irq */ - if (hw_config->irq < 0 || hw_config->irq > 15) + if (devc->irq < 0 || devc->irq > 15) goto out_release_region; - if (request_irq(hw_config->irq, ad1816_interrupt,0, - "SoundPort", hw_config->osp) < 0) { + if (request_irq(devc->irq, ad1816_interrupt,0, + "SoundPort", devc) < 0) { printk(KERN_WARNING "ad1816: IRQ in use\n"); goto out_release_region; } - devc->irq=hw_config->irq; /* DMA stuff */ - if (sound_alloc_dma (hw_config->dma, "Sound System")) { + if (sound_alloc_dma (devc->dma_playback, "Sound System")) { printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n", - hw_config->dma); + devc->dma_playback); goto out_free_irq; } - devc->dma_playback=hw_config->dma; - if ( hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) { - if (sound_alloc_dma(hw_config->dma2, + if ( devc->dma_capture >= 0 && + devc->dma_capture != devc->dma_playback) { + if (sound_alloc_dma(devc->dma_capture, "Sound System (capture)")) { printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n", - hw_config->dma2); + devc->dma_capture); goto out_free_dma; } - devc->dma_capture=hw_config->dma2; devc->audio_mode=DMA_AUTOMODE|DMA_DUPLEX; } else { - devc->dma_capture=-1; + printk(KERN_WARNING "ad1816: Only one DMA channel " + "available/configured. No duplex operation possible\n"); devc->audio_mode=DMA_AUTOMODE; } - sprintf (dev_name,"AD1816 audio driver"); - - conf_printf2 (dev_name, - devc->base, devc->irq, hw_config->dma, hw_config->dma2); + conf_printf2 ("AD1816 audio driver", + devc->base, devc->irq, devc->dma_playback, + devc->dma_capture); /* register device */ - if ((my_dev = sound_install_audiodrv (AUDIO_DRIVER_VERSION, - dev_name, + if ((oss_devno = sound_install_audiodrv (AUDIO_DRIVER_VERSION, + "AD1816 audio driver", &ad1816_audio_driver, sizeof (struct audio_driver), devc->audio_mode, ad_format_mask, devc, - hw_config->dma, - hw_config->dma2)) < 0) { + devc->dma_playback, + devc->dma_capture)) < 0) { printk(KERN_WARNING "ad1816: Can't install sound driver\n"); goto out_free_dma_2; } - /* fill rest of structure with reasonable default values */ - irq2dev[hw_config->irq] = devc->dev_no = my_dev; - devc->opened = 0; - devc->irq_ok = 0; - devc->osp = hw_config->osp; - nr_ad1816_devs++; ad_write(devc,32,0x80f0); /* sound system mode */ if (options&1) { @@ -1186,25 +1141,30 @@ ad1816_mixer_reset (devc); /* register mixer */ - if ((audio_devs[my_dev]->mixer_dev=sound_install_mixer( + if ((audio_devs[oss_devno]->mixer_dev=sound_install_mixer( MIXER_DRIVER_VERSION, - dev_name, + "AD1816 audio driver", &ad1816_mixer_operations, sizeof (struct mixer_operations), - devc)) >= 0) { - audio_devs[my_dev]->min_fragment = 0; + devc)) < 0) { + printk(KERN_WARNING "Can't install mixer\n"); } -out: return; + /* make ad1816_info active */ + nr_ad1816_devs++; + printk(KERN_INFO "ad1816: card successfully installed!\n"); + return 1; + /* error handling */ out_free_dma_2: - if (devc->dma_capture >= 0) - sound_free_dma(hw_config->dma2); + if (devc->dma_capture >= 0 && devc->dma_capture != devc->dma_playback) + sound_free_dma(devc->dma_capture); out_free_dma: - sound_free_dma(hw_config->dma); + sound_free_dma(devc->dma_playback); out_free_irq: - free_irq(hw_config->irq,hw_config->osp); + free_irq(devc->irq, devc); out_release_region: - release_region(hw_config->io_base, 16); - goto out; + release_region(devc->base, 16); +out: + return 0; } static void __exit unload_card(ad1816_info *devc) @@ -1212,7 +1172,7 @@ int mixer, dev = 0; if (devc != NULL) { - DEBUGLOG (printk("ad1816: Unloading card at base=%x\n",devc->base)); + printk("ad1816: Unloading card at address 0x%03x\n",devc->base); dev = devc->dev_no; mixer = audio_devs[dev]->mixer_dev; @@ -1221,45 +1181,41 @@ if(mixer>=0) { sound_unload_mixerdev(mixer); } + /* unreg audiodev */ sound_unload_audiodev(dev); /* free dma channels */ - if (devc->dma_capture>=0) { + if (devc->dma_capture>=0 && + devc->dma_capture != devc->dma_playback) { sound_free_dma(devc->dma_capture); } - - /* card won't get added if resources could not be allocated - thus we need not ckeck if allocation was successful */ sound_free_dma (devc->dma_playback); - free_irq(devc->irq, devc->osp); + /* free irq */ + free_irq(devc->irq, devc); + /* free io */ release_region (devc->base, 16); - - DEBUGLOG (printk("ad1816: Unloading card at base=%x was successful\n",devc->base)); +#ifdef __ISAPNP__ + if (devc->pnpdev) { + pnp_disable_dev(devc->pnpdev); + pnp_device_detach(devc->pnpdev); + } +#endif } else printk(KERN_WARNING "ad1816: no device/card specified\n"); } -static struct address_info cfg; - static int __initdata io = -1; static int __initdata irq = -1; static int __initdata dma = -1; static int __initdata dma2 = -1; #ifdef __ISAPNP__ -struct pci_dev *ad1816_dev = NULL; - -static int activated = 1; - +/* use isapnp for configuration */ static int isapnp = 1; static int isapnpjump = 0; - MODULE_PARM(isapnp, "i"); MODULE_PARM(isapnpjump, "i"); - -#else -static int isapnp = 0; #endif MODULE_PARM(io,"i"); @@ -1270,51 +1226,6 @@ MODULE_PARM(options,"i"); #ifdef __ISAPNP__ - -static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev) -{ - int err; - - if(dev->active) { - activated = 0; - return(dev); - } - - if((err = dev->activate(dev)) < 0) { - printk(KERN_ERR "ad1816: %s %s config failed (out of resources?)[%d]\n", - devname, resname, err); - dev->deactivate(dev); - return(NULL); - } - - return(dev); -} - -static struct pci_dev *ad1816_init_generic(struct pci_bus *bus, struct pci_dev *card, - struct address_info *hw_config) -{ - if((ad1816_dev = isapnp_find_dev(bus, card->vendor, card->device, NULL))) { - ad1816_dev->prepare(ad1816_dev); - - if((ad1816_dev = activate_dev("Analog Devices 1816(A)", "ad1816", ad1816_dev))) { - hw_config->io_base = ad1816_dev->resource[2].start; - hw_config->irq = ad1816_dev->irq_resource[0].start; - hw_config->dma = ad1816_dev->dma_resource[0].start; - hw_config->dma2 = ad1816_dev->dma_resource[1].start; - } - } - - return(ad1816_dev); -} - -static struct ad1816_data { - struct pci_dev * (*initfunc)(struct pci_bus*, struct pci_dev *, struct address_info *); - char *name; -} ad1816_pnp_data[] __initdata = { - { &ad1816_init_generic, "Analog Devices 1815" }, - { &ad1816_init_generic, "Analog Devices 1816A" } -}; - static struct { unsigned short card_vendor, card_device; unsigned short vendor; @@ -1323,107 +1234,101 @@ } isapnp_ad1816_list[] __initdata = { { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7150), - &ad1816_pnp_data[0] }, + 0 }, { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7180), - &ad1816_pnp_data[1] }, + 0 }, {0} }; MODULE_DEVICE_TABLE(isapnp, isapnp_ad1816_list); -static int __init ad1816_init_isapnp(struct address_info *hw_config, - struct pci_bus *bus, struct pci_dev *card, int slot) + +void __init ad1816_config_pnp_card(struct pnp_card *card, unsigned short vendor, + unsigned short function) { - struct pci_dev *idev = NULL; - - /* You missed the init func? That's bad. */ - if(isapnp_ad1816_list[slot].data->initfunc) { - char *busname = bus->name[0] ? bus->name : isapnp_ad1816_list[slot].data->name; - - printk(KERN_INFO "ad1816: %s detected\n", busname); - - /* Initialize this baby. */ - if((idev = isapnp_ad1816_list[slot].data->initfunc(bus, card, hw_config))) { - /* We got it. */ - - printk(KERN_NOTICE "ad1816: 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 "ad1816: Failed to initialize %s\n", busname); - } else - printk(KERN_ERR "ad1816: Bad entry in ad1816.c PnP table\n"); - - return 0; + struct address_info cfg; + struct pnp_dev *card_dev = pnp_find_dev(card, vendor, function, NULL); + if (!card_dev) return; + if (pnp_device_attach(card_dev) < 0) { + printk(KERN_WARNING "ad1816: Failed to attach PnP device\n"); + return; + } + if (pnp_activate_dev(card_dev) < 0) { + printk(KERN_WARNING "ad1816: Failed to activate PnP device\n"); + pnp_device_detach(card_dev); + return; + } + cfg.io_base = pnp_port_start(card_dev, 2); + cfg.irq = pnp_irq(card_dev, 0); + cfg.dma = pnp_irq(card_dev, 0); + cfg.dma2 = pnp_irq(card_dev, 1); + if (!ad1816_init_card(&cfg, card_dev)) { + pnp_disable_dev(card_dev); + pnp_device_detach(card_dev); + } } -/* - * 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 ad1816_probe_isapnp(struct address_info *hw_config) +void __init ad1816_config_pnp_cards(void) { + int nr_pnp_cfg; int i; /* Count entries in isapnp_ad1816_list */ - for (i = 0; isapnp_ad1816_list[i].vendor != 0; i++) - ; + for (nr_pnp_cfg = 0; isapnp_ad1816_list[nr_pnp_cfg].card_vendor != 0; + nr_pnp_cfg++); /* Check and adjust isapnpjump */ - if( isapnpjump < 0 || isapnpjump > ( i - 1 ) ) { - printk(KERN_ERR "ad1816: Valid range for isapnpjump is 0-%d. Adjusted to 0.\n", i-1); + if( isapnpjump < 0 || isapnpjump >= nr_pnp_cfg) { + printk(KERN_WARNING + "ad1816: Valid range for isapnpjump is 0-%d. " + "Adjusted to 0.\n", nr_pnp_cfg-1); isapnpjump = 0; } - - for (i = isapnpjump; isapnp_ad1816_list[i].vendor != 0; i++) { - struct pci_dev *card = NULL; - - while ((card = isapnp_find_dev(NULL, isapnp_ad1816_list[i].vendor, - isapnp_ad1816_list[i].function, card))) - if(ad1816_init_isapnp(hw_config, card->bus, card, i)) - return 0; + for (i = isapnpjump; isapnp_ad1816_list[i].card_vendor != 0; i++) { + struct pnp_card *card = NULL; + /* iterate over all pnp cards */ + while ((card = pnp_find_card(isapnp_ad1816_list[i].card_vendor, + isapnp_ad1816_list[i].card_device, card))) + ad1816_config_pnp_card(card, + isapnp_ad1816_list[i].vendor, + isapnp_ad1816_list[i].function); } - - return -ENODEV; } #endif +/* module initialization */ static int __init init_ad1816(void) { - -#ifdef __ISAPNP__ - if(isapnp && (ad1816_probe_isapnp(&cfg) < 0) ) { - printk(KERN_NOTICE "ad1816: No ISAPnP cards found, trying standard ones...\n"); - isapnp = 0; - } + printk(KERN_INFO "ad1816: AD1816 sounddriver " + "Copyright (C) 1998-2003 by Thorsten Knabe and " + "others\n"); +#ifdef AD1816_CLOCK + /* set ad1816_clockfreq if set during compilation */ + ad1816_clockfreq=AD1816_CLOCK; #endif - - if( isapnp == 0) { - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma2; - } - - if (cfg.io_base == -1 || cfg.irq == -1 || cfg.dma == -1 || cfg.dma2 == -1) { - printk(KERN_INFO "ad1816: dma, dma2, irq and io must be set.\n"); - return -EINVAL; + if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) { + ad1816_clockfreq=33000; } - if (probe_ad1816(&cfg) == 0) { - return -ENODEV; +#ifdef __ISAPNP__ + /* configure PnP cards */ + if(isapnp) ad1816_config_pnp_cards(); +#endif + /* configure card by module params */ + if (io != -1 && irq != -1 && dma != -1) { + struct address_info cfg; + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + ad1816_init_card(&cfg, NULL); } - - attach_ad1816(&cfg); - + if (nr_ad1816_devs <= 0) + return -ENODEV; return 0; } +/* module cleanup */ static void __exit cleanup_ad1816 (void) { int i; @@ -1435,18 +1340,14 @@ unload_card(devc); } nr_ad1816_devs=0; - -#ifdef __ISAPNP__ - if(activated) - if(ad1816_dev) - ad1816_dev->deactivate(ad1816_dev); -#endif + printk(KERN_INFO "ad1816: driver unloaded!\n"); } module_init(init_ad1816); module_exit(cleanup_ad1816); #ifndef MODULE +/* kernel command line parameter evaluation */ static int __init setup_ad1816(char *str) { /* io, irq, dma, dma2 */ @@ -1458,7 +1359,6 @@ irq = ints[2]; dma = ints[3]; dma2 = ints[4]; - return 1; } | |