diff options
author | Jon Masters <jcm@jonmasters.org> | 2006-04-20 13:43:20 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-04-20 18:54:04 +0400 |
commit | 962f831f67301265dcd2cd96408d07d95d556aed (patch) | |
tree | 3034e297898c922479b7ad0c4d964d9fb173a50e /sound | |
parent | c79cfbaccac0ef81ab3e796da1582a83dcef0ff9 (diff) | |
download | linux-962f831f67301265dcd2cd96408d07d95d556aed.tar.xz |
[PATCH] sound: fix hang in mpu401_uart.c
This fixes a hang in mpu401_uart.c that can occur when the mpu401 interface
is non-existent or otherwise doesn't respond to commands but we issue IO
anyway. snd_mpu401_uart_cmd now returns an error code that is passed up
the stack so that an open() will fail immediately in such cases.
Eventually discovered after wine/cxoffice would constantly cause hard
lockups on my desktop immediately after loading (emulating Windows too
well). Turned out that I'd recently moved my sound cards around and using
/dev/sequencer now talks to a sound card with a broken MPU.
This second version changes -EFAULT to -EIO and frees open resources on
error too. Test booted and seems to work ok.
Signed-off-by: Jon Masters <jcm@jonmasters.org>
Cc: Jaroslav Kysela <perex@suse.cz>
Acked-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/drivers/mpu401/mpu401_uart.c | 42 |
1 files changed, 33 insertions, 9 deletions
diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c index 8687ae3c66b8..b49a45cbf67a 100644 --- a/sound/drivers/mpu401/mpu401_uart.c +++ b/sound/drivers/mpu401/mpu401_uart.c @@ -183,7 +183,8 @@ static void snd_mpu401_uart_remove_timer (struct snd_mpu401 *mpu, int input) */ -static void snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd, int ack) +static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd, + int ack) { unsigned long flags; int timeout, ok; @@ -218,9 +219,11 @@ static void snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd, int ok = 1; } spin_unlock_irqrestore(&mpu->input_lock, flags); - if (! ok) + if (!ok) { snd_printk("cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, mpu->read(mpu, MPU401C(mpu)), mpu->read(mpu, MPU401D(mpu))); - // snd_printk("cmd: 0x%x at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, mpu->read(mpu, MPU401C(mpu)), mpu->read(mpu, MPU401D(mpu))); + return 1; + } + return 0; } /* @@ -235,12 +238,19 @@ static int snd_mpu401_uart_input_open(struct snd_rawmidi_substream *substream) if (mpu->open_input && (err = mpu->open_input(mpu)) < 0) return err; if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) { - snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1); - snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1); + if (snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1)) + goto error_out; + if (snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1)) + goto error_out; } mpu->substream_input = substream; set_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); return 0; + +error_out: + if (mpu->open_input && mpu->close_input) + mpu->close_input(mpu); + return -EIO; } static int snd_mpu401_uart_output_open(struct snd_rawmidi_substream *substream) @@ -252,39 +262,52 @@ static int snd_mpu401_uart_output_open(struct snd_rawmidi_substream *substream) if (mpu->open_output && (err = mpu->open_output(mpu)) < 0) return err; if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) { - snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1); - snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1); + if (snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1)) + goto error_out; + if (snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1)) + goto error_out; } mpu->substream_output = substream; set_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); return 0; + +error_out: + if (mpu->open_output && mpu->close_output) + mpu->close_output(mpu); + return -EIO; } static int snd_mpu401_uart_input_close(struct snd_rawmidi_substream *substream) { struct snd_mpu401 *mpu; + int err = 0; mpu = substream->rmidi->private_data; clear_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); mpu->substream_input = NULL; if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) - snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); + err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); if (mpu->close_input) mpu->close_input(mpu); + if (err) + return -EIO; return 0; } static int snd_mpu401_uart_output_close(struct snd_rawmidi_substream *substream) { struct snd_mpu401 *mpu; + int err = 0; mpu = substream->rmidi->private_data; clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); mpu->substream_output = NULL; if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) - snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); + err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); if (mpu->close_output) mpu->close_output(mpu); + if (err) + return -EIO; return 0; } @@ -316,6 +339,7 @@ static void snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substrea snd_mpu401_uart_remove_timer(mpu, 1); clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode); } + } /* |