aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2012-12-03 11:12:46 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-01-11 09:03:36 -0800
commit5a6400c991f269eaab5a175d8b7c656155a74129 (patch)
tree98bd2050b1f3c827fc464ba198699117aa87a394 /sound
parent51afc625ccd2e38f76f690ccce93e11a21d9543b (diff)
downloadkernel_samsung_smdk4412-5a6400c991f269eaab5a175d8b7c656155a74129.zip
kernel_samsung_smdk4412-5a6400c991f269eaab5a175d8b7c656155a74129.tar.gz
kernel_samsung_smdk4412-5a6400c991f269eaab5a175d8b7c656155a74129.tar.bz2
ALSA: usb-audio: Avoid autopm calls after disconnection
commit 59866da9e4ae54819e3c4e0a8f426bdb0c2ef993 upstream. Add a similar protection against the disconnection race and the invalid use of usb instance after disconnection, as well as we've done for the USB audio PCM. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=51201 Reviewd-by: Clemens Ladisch <clemens@ladisch.de> Tested-by: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'sound')
-rw-r--r--sound/usb/midi.c23
1 files changed, 22 insertions, 1 deletions
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index a0fe5dd..0ee3d39 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -115,6 +115,7 @@ struct snd_usb_midi {
struct list_head list;
struct timer_list error_timer;
spinlock_t disc_lock;
+ struct rw_semaphore disc_rwsem;
struct mutex mutex;
u32 usb_id;
int next_midi_device;
@@ -1021,6 +1022,12 @@ static void substream_open(struct snd_rawmidi_substream *substream, int open)
struct snd_usb_midi* umidi = substream->rmidi->private_data;
struct snd_kcontrol *ctl;
+ down_read(&umidi->disc_rwsem);
+ if (umidi->disconnected) {
+ up_read(&umidi->disc_rwsem);
+ return;
+ }
+
mutex_lock(&umidi->mutex);
if (open) {
if (umidi->opened++ == 0 && umidi->roland_load_ctl) {
@@ -1039,6 +1046,7 @@ static void substream_open(struct snd_rawmidi_substream *substream, int open)
}
}
mutex_unlock(&umidi->mutex);
+ up_read(&umidi->disc_rwsem);
}
static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
@@ -1059,8 +1067,15 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
snd_BUG();
return -ENXIO;
}
+
+ down_read(&umidi->disc_rwsem);
+ if (umidi->disconnected) {
+ up_read(&umidi->disc_rwsem);
+ return -ENODEV;
+ }
err = usb_autopm_get_interface(umidi->iface);
port->autopm_reference = err >= 0;
+ up_read(&umidi->disc_rwsem);
if (err < 0 && err != -EACCES)
return -EIO;
substream->runtime->private_data = port;
@@ -1075,8 +1090,10 @@ static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream)
struct usbmidi_out_port *port = substream->runtime->private_data;
substream_open(substream, 0);
- if (port->autopm_reference)
+ down_read(&umidi->disc_rwsem);
+ if (!umidi->disconnected && port->autopm_reference)
usb_autopm_put_interface(umidi->iface);
+ up_read(&umidi->disc_rwsem);
return 0;
}
@@ -1386,9 +1403,12 @@ void snd_usbmidi_disconnect(struct list_head* p)
* a timer may submit an URB. To reliably break the cycle
* a flag under lock must be used
*/
+ down_write(&umidi->disc_rwsem);
spin_lock_irq(&umidi->disc_lock);
umidi->disconnected = 1;
spin_unlock_irq(&umidi->disc_lock);
+ up_write(&umidi->disc_rwsem);
+
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
if (ep->out)
@@ -2100,6 +2120,7 @@ int snd_usbmidi_create(struct snd_card *card,
umidi->usb_protocol_ops = &snd_usbmidi_standard_ops;
init_timer(&umidi->error_timer);
spin_lock_init(&umidi->disc_lock);
+ init_rwsem(&umidi->disc_rwsem);
mutex_init(&umidi->mutex);
umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor),
le16_to_cpu(umidi->dev->descriptor.idProduct));