X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?p=pandora-kernel.git;a=blobdiff_plain;f=sound%2Fusb%2Fmidi.c;h=c0054ee9389bcdef91cd97a27dfe95073dd768a9;hp=c83f6143c0eb34d8f77f969e1928427458b4268a;hb=59866da9e4ae54819e3c4e0a8f426bdb0c2ef993;hpb=9785dbdf265ddc47d5c88267d89a97648c0dc14b;ds=sidebyside diff --git a/sound/usb/midi.c b/sound/usb/midi.c index c83f6143c0eb..c0054ee9389b 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -116,6 +116,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; @@ -148,6 +149,7 @@ struct snd_usb_midi_out_endpoint { struct snd_usb_midi_out_endpoint* ep; struct snd_rawmidi_substream *substream; int active; + bool autopm_reference; uint8_t cable; /* cable number << 4 */ uint8_t state; #define STATE_UNKNOWN 0 @@ -1037,6 +1039,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) { @@ -1055,6 +1063,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) @@ -1075,8 +1084,16 @@ 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); - if (err < 0) + port->autopm_reference = err >= 0; + up_read(&umidi->disc_rwsem); + if (err < 0 && err != -EACCES) return -EIO; substream->runtime->private_data = port; port->state = STATE_UNKNOWN; @@ -1087,9 +1104,13 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream) { struct snd_usb_midi* umidi = substream->rmidi->private_data; + struct usbmidi_out_port *port = substream->runtime->private_data; substream_open(substream, 0); - usb_autopm_put_interface(umidi->iface); + down_read(&umidi->disc_rwsem); + if (!umidi->disconnected && port->autopm_reference) + usb_autopm_put_interface(umidi->iface); + up_read(&umidi->disc_rwsem); return 0; } @@ -1399,9 +1420,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) @@ -2113,6 +2137,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));