c2cbdbb158
when simulating a storm of fake GUS interrupts (without actually owning this venerable piece of ISA hardware) the driver falls over (crashes) in two ways: 1) spinlocks being initialized too late: INFO: trying to register non-static key. the code is fine but needs lockdep annotation. turning off the locking correctness validator. [<401058ca>] show_trace_log_lvl+0x1a/0x30 [<401064b2>] show_trace+0x12/0x20 [<401064d6>] dump_stack+0x16/0x20 [<4014a72b>] __lock_acquire+0xcfb/0x1030 [<4014aac0>] lock_acquire+0x60/0x80 [<40721a68>] _spin_lock_irqsave+0x38/0x50 [<4058fc12>] snd_gf1_i_look8+0x22/0x60 [<405906fe>] snd_gus_interrupt+0x13e/0x270 [<401548e8>] handle_IRQ_event+0x28/0x60 [<40155cc1>] handle_fasteoi_irq+0x71/0xe0 [<40107238>] do_IRQ+0x48/0xa0 [<401051fe>] common_interrupt+0x2e/0x40 [<40156822>] register_handler_proc+0x92/0xf0 [<401550c2>] setup_irq+0xe2/0x190 [<40155224>] request_irq+0xb4/0xd0 [<4058f524>] snd_gus_create+0x124/0x3c0 [<40aa4087>] snd_gusclassic_probe+0x2a7/0x4b0 [<403f5eff>] isa_bus_probe+0x1f/0x30 [<403f1944>] driver_probe_device+0x84/0x190 [<403f1a58>] __device_attach+0x8/0x10 [<403f0e63>] bus_for_each_drv+0x53/0x80 [<403f1b1b>] device_attach+0x8b/0x90 [<403f0dd8>] bus_attach_device+0x48/0x80 [<403efdbd>] device_add+0x45d/0x5a0 [<403eff12>] device_register+0x12/0x20 [<403f60c3>] isa_register_driver+0xb3/0x140 [<40aa3dd2>] alsa_card_gusclassic_init+0x12/0x20 [<40a665c3>] kernel_init+0x133/0x310 [<401054a7>] kernel_thread_helper+0x7/0x10 ======================= 2) callback functions not being filled in yet: BUG: unable to handle kernel NULL pointer dereference at virtual address 00000000 printing eip: 00000000 *pde = 00000000 Oops: 0000 [#1] SMP DEBUG_PAGEALLOC CPU: 0 EIP: 0060:[<00000000>] Not tainted VLI EFLAGS: 00010002 (2.6.23 #37) EIP is at 0x0 eax: 7fe94000 ebx: 7fe94000 ecx: 00000000 edx: 00000226 esi: 00000000 edi: 00000005 ebp: 7ff87c28 esp: 7ff87bf4 ds: 007b es: 007b fs: 00d8 gs: 0000 ss: 0068 Process swapper (pid: 1, ti=7ff86000 task=7ff84000 task.ti=7ff86000) Stack: 40590683 408424a9 408db87c 00000029 40787406 00000064 00000046 ff000000 000000ff 00000001 7faefaf0 00000000 00000005 7ff87c40 401548e8 00000000 40a52000 7faefaf0 00000005 7ff87c58 40155cc1 40a52030 00000005 00000000 Call Trace: [<401058ca>] show_trace_log_lvl+0x1a/0x30 [<4010598b>] show_stack_log_lvl+0xab/0xd0 [<40105b7c>] show_registers+0x1cc/0x2d0 [<40105d96>] die+0x116/0x240 [<4011d7bb>] do_page_fault+0x18b/0x670 [<40721d22>] error_code+0x72/0x80 [<401548e8>] handle_IRQ_event+0x28/0x60 [<40155cc1>] handle_fasteoi_irq+0x71/0xe0 [<40107238>] do_IRQ+0x48/0xa0 [<401051fe>] common_interrupt+0x2e/0x40 [<401a344e>] proc_create+0x3e/0x120 [<401a3733>] proc_mkdir_mode+0x23/0x50 [<401a376f>] proc_mkdir+0xf/0x20 [<40156864>] register_handler_proc+0xd4/0xf0 [<401550c2>] setup_irq+0xe2/0x190 [<40155224>] request_irq+0xb4/0xd0 [<4058f524>] snd_gus_create+0x124/0x3c0 [<40aa4087>] snd_gusclassic_probe+0x2a7/0x4b0 [<403f5eff>] isa_bus_probe+0x1f/0x30 [<403f1944>] driver_probe_device+0x84/0x190 [<403f1a58>] __device_attach+0x8/0x10 [<403f0e63>] bus_for_each_drv+0x53/0x80 [<403f1b1b>] device_attach+0x8b/0x90 [<403f0dd8>] bus_attach_device+0x48/0x80 [<403efdbd>] device_add+0x45d/0x5a0 [<403eff12>] device_register+0x12/0x20 [<403f60c3>] isa_register_driver+0xb3/0x140 [<40aa3dd2>] alsa_card_gusclassic_init+0x12/0x20 [<40a665c3>] kernel_init+0x133/0x310 [<401054a7>] kernel_thread_helper+0x7/0x10 ======================= Code: Bad EIP value. EIP: [<00000000>] 0x0 SS:ESP 0068:7ff87bf4 Kernel panic - not syncing: Fatal exception in interrupt with these things fixed, i get the expected 'no such hardware' result from the driver initialization: Calling initcall 0x40aa3dc0: alsa_card_gusclassic_init+0x0/0x20() ALSA sound/isa/gus/gusclassic.c:136: [0x220] check 1 failed - 0xff initcall 0x40aa3dc0: alsa_card_gusclassic_init+0x0/0x20() returned 0. initcall 0x40aa3dc0 ran for 133 msecs: alsa_card_gusclassic_init+0x0/0x20() Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
149 lines
4.6 KiB
C
149 lines
4.6 KiB
C
/*
|
|
* Routine for IRQ handling from GF1/InterWave chip
|
|
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include <sound/driver.h>
|
|
#include <sound/core.h>
|
|
#include <sound/info.h>
|
|
#include <sound/gus.h>
|
|
|
|
#ifdef CONFIG_SND_DEBUG
|
|
#define STAT_ADD(x) ((x)++)
|
|
#else
|
|
#define STAT_ADD(x) while (0) { ; }
|
|
#endif
|
|
|
|
irqreturn_t snd_gus_interrupt(int irq, void *dev_id)
|
|
{
|
|
struct snd_gus_card * gus = dev_id;
|
|
unsigned char status;
|
|
int loop = 100;
|
|
int handled = 0;
|
|
|
|
__again:
|
|
status = inb(gus->gf1.reg_irqstat);
|
|
if (status == 0)
|
|
return IRQ_RETVAL(handled);
|
|
handled = 1;
|
|
// snd_printk("IRQ: status = 0x%x\n", status);
|
|
if (status & 0x02) {
|
|
STAT_ADD(gus->gf1.interrupt_stat_midi_in);
|
|
if (gus->gf1.interrupt_handler_midi_in)
|
|
gus->gf1.interrupt_handler_midi_in(gus);
|
|
}
|
|
if (status & 0x01) {
|
|
STAT_ADD(gus->gf1.interrupt_stat_midi_out);
|
|
if (gus->gf1.interrupt_handler_midi_out)
|
|
gus->gf1.interrupt_handler_midi_out(gus);
|
|
}
|
|
if (status & (0x20 | 0x40)) {
|
|
unsigned int already, _current_;
|
|
unsigned char voice_status, voice;
|
|
struct snd_gus_voice *pvoice;
|
|
|
|
already = 0;
|
|
while (((voice_status = snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ)) & 0xc0) != 0xc0) {
|
|
voice = voice_status & 0x1f;
|
|
_current_ = 1 << voice;
|
|
if (already & _current_)
|
|
continue; /* multi request */
|
|
already |= _current_; /* mark request */
|
|
#if 0
|
|
printk("voice = %i, voice_status = 0x%x, voice_verify = %i\n", voice, voice_status, inb(GUSP(gus, GF1PAGE)));
|
|
#endif
|
|
pvoice = &gus->gf1.voices[voice];
|
|
if (pvoice->use) {
|
|
if (!(voice_status & 0x80)) { /* voice position IRQ */
|
|
STAT_ADD(pvoice->interrupt_stat_wave);
|
|
pvoice->handler_wave(gus, pvoice);
|
|
}
|
|
if (!(voice_status & 0x40)) { /* volume ramp IRQ */
|
|
STAT_ADD(pvoice->interrupt_stat_volume);
|
|
pvoice->handler_volume(gus, pvoice);
|
|
}
|
|
} else {
|
|
STAT_ADD(gus->gf1.interrupt_stat_voice_lost);
|
|
snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
|
|
snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
|
|
}
|
|
}
|
|
}
|
|
if (status & 0x04) {
|
|
STAT_ADD(gus->gf1.interrupt_stat_timer1);
|
|
if (gus->gf1.interrupt_handler_timer1)
|
|
gus->gf1.interrupt_handler_timer1(gus);
|
|
}
|
|
if (status & 0x08) {
|
|
STAT_ADD(gus->gf1.interrupt_stat_timer2);
|
|
if (gus->gf1.interrupt_handler_timer2)
|
|
gus->gf1.interrupt_handler_timer2(gus);
|
|
}
|
|
if (status & 0x80) {
|
|
if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL) & 0x40) {
|
|
STAT_ADD(gus->gf1.interrupt_stat_dma_write);
|
|
if (gus->gf1.interrupt_handler_dma_write)
|
|
gus->gf1.interrupt_handler_dma_write(gus);
|
|
}
|
|
if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL) & 0x40) {
|
|
STAT_ADD(gus->gf1.interrupt_stat_dma_read);
|
|
if (gus->gf1.interrupt_handler_dma_read)
|
|
gus->gf1.interrupt_handler_dma_read(gus);
|
|
}
|
|
}
|
|
if (--loop > 0)
|
|
goto __again;
|
|
return IRQ_NONE;
|
|
}
|
|
|
|
#ifdef CONFIG_SND_DEBUG
|
|
static void snd_gus_irq_info_read(struct snd_info_entry *entry,
|
|
struct snd_info_buffer *buffer)
|
|
{
|
|
struct snd_gus_card *gus;
|
|
struct snd_gus_voice *pvoice;
|
|
int idx;
|
|
|
|
gus = entry->private_data;
|
|
snd_iprintf(buffer, "midi out = %u\n", gus->gf1.interrupt_stat_midi_out);
|
|
snd_iprintf(buffer, "midi in = %u\n", gus->gf1.interrupt_stat_midi_in);
|
|
snd_iprintf(buffer, "timer1 = %u\n", gus->gf1.interrupt_stat_timer1);
|
|
snd_iprintf(buffer, "timer2 = %u\n", gus->gf1.interrupt_stat_timer2);
|
|
snd_iprintf(buffer, "dma write = %u\n", gus->gf1.interrupt_stat_dma_write);
|
|
snd_iprintf(buffer, "dma read = %u\n", gus->gf1.interrupt_stat_dma_read);
|
|
snd_iprintf(buffer, "voice lost = %u\n", gus->gf1.interrupt_stat_voice_lost);
|
|
for (idx = 0; idx < 32; idx++) {
|
|
pvoice = &gus->gf1.voices[idx];
|
|
snd_iprintf(buffer, "voice %i: wave = %u, volume = %u\n",
|
|
idx,
|
|
pvoice->interrupt_stat_wave,
|
|
pvoice->interrupt_stat_volume);
|
|
}
|
|
}
|
|
|
|
void snd_gus_irq_profile_init(struct snd_gus_card *gus)
|
|
{
|
|
struct snd_info_entry *entry;
|
|
|
|
if (! snd_card_proc_new(gus->card, "gusirq", &entry))
|
|
snd_info_set_text_ops(entry, gus, snd_gus_irq_info_read);
|
|
}
|
|
|
|
#endif
|