aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2008-02-22 12:34:44 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-22 17:20:08 -0500
commitc0792e00bc2dd1202d48b838b1cf59d13dd2c74a (patch)
tree538ebec399f27b107b5a80f2235d3982d89f86d8 /sound/usb
parent0ee46c9dadcbbd0daa12da30f226391896d90abb (diff)
[ALSA] race between disconnect and error handling in usbmidi
The driver resubmits URBs from an error handler and schedules the error handler from the URBs' completion handlers. To reliably kill the cycle a flag must be used. Signed-off-by: Oliver Neukum <oneukum@suse.de> Signed-off-by: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/usbmidi.c19
1 files changed, 18 insertions, 1 deletions
diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
index 750e929d5870..6676a177c99e 100644
--- a/sound/usb/usbmidi.c
+++ b/sound/usb/usbmidi.c
@@ -104,12 +104,14 @@ struct snd_usb_midi {
104 struct usb_protocol_ops* usb_protocol_ops; 104 struct usb_protocol_ops* usb_protocol_ops;
105 struct list_head list; 105 struct list_head list;
106 struct timer_list error_timer; 106 struct timer_list error_timer;
107 spinlock_t disc_lock;
107 108
108 struct snd_usb_midi_endpoint { 109 struct snd_usb_midi_endpoint {
109 struct snd_usb_midi_out_endpoint *out; 110 struct snd_usb_midi_out_endpoint *out;
110 struct snd_usb_midi_in_endpoint *in; 111 struct snd_usb_midi_in_endpoint *in;
111 } endpoints[MIDI_MAX_ENDPOINTS]; 112 } endpoints[MIDI_MAX_ENDPOINTS];
112 unsigned long input_triggered; 113 unsigned long input_triggered;
114 unsigned char disconnected;
113}; 115};
114 116
115struct snd_usb_midi_out_endpoint { 117struct snd_usb_midi_out_endpoint {
@@ -306,6 +308,11 @@ static void snd_usbmidi_error_timer(unsigned long data)
306 struct snd_usb_midi *umidi = (struct snd_usb_midi *)data; 308 struct snd_usb_midi *umidi = (struct snd_usb_midi *)data;
307 int i; 309 int i;
308 310
311 spin_lock(&umidi->disc_lock);
312 if (umidi->disconnected) {
313 spin_unlock(&umidi->disc_lock);
314 return;
315 }
309 for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { 316 for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
310 struct snd_usb_midi_in_endpoint *in = umidi->endpoints[i].in; 317 struct snd_usb_midi_in_endpoint *in = umidi->endpoints[i].in;
311 if (in && in->error_resubmit) { 318 if (in && in->error_resubmit) {
@@ -316,6 +323,7 @@ static void snd_usbmidi_error_timer(unsigned long data)
316 if (umidi->endpoints[i].out) 323 if (umidi->endpoints[i].out)
317 snd_usbmidi_do_output(umidi->endpoints[i].out); 324 snd_usbmidi_do_output(umidi->endpoints[i].out);
318 } 325 }
326 spin_unlock(&umidi->disc_lock);
319} 327}
320 328
321/* helper function to send static data that may not DMA-able */ 329/* helper function to send static data that may not DMA-able */
@@ -1049,7 +1057,14 @@ void snd_usbmidi_disconnect(struct list_head* p)
1049 int i; 1057 int i;
1050 1058
1051 umidi = list_entry(p, struct snd_usb_midi, list); 1059 umidi = list_entry(p, struct snd_usb_midi, list);
1052 del_timer_sync(&umidi->error_timer); 1060 /*
1061 * an URB's completion handler may start the timer and
1062 * a timer may submit an URB. To reliably break the cycle
1063 * a flag under lock must be used
1064 */
1065 spin_lock_irq(&umidi->disc_lock);
1066 umidi->disconnected = 1;
1067 spin_unlock_irq(&umidi->disc_lock);
1053 for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { 1068 for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
1054 struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; 1069 struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
1055 if (ep->out) 1070 if (ep->out)
@@ -1062,6 +1077,7 @@ void snd_usbmidi_disconnect(struct list_head* p)
1062 if (ep->in) 1077 if (ep->in)
1063 usb_kill_urb(ep->in->urb); 1078 usb_kill_urb(ep->in->urb);
1064 } 1079 }
1080 del_timer_sync(&umidi->error_timer);
1065} 1081}
1066 1082
1067static void snd_usbmidi_rawmidi_free(struct snd_rawmidi *rmidi) 1083static void snd_usbmidi_rawmidi_free(struct snd_rawmidi *rmidi)
@@ -1685,6 +1701,7 @@ int snd_usb_create_midi_interface(struct snd_usb_audio* chip,
1685 umidi->quirk = quirk; 1701 umidi->quirk = quirk;
1686 umidi->usb_protocol_ops = &snd_usbmidi_standard_ops; 1702 umidi->usb_protocol_ops = &snd_usbmidi_standard_ops;
1687 init_timer(&umidi->error_timer); 1703 init_timer(&umidi->error_timer);
1704 spin_lock_init(&umidi->disc_lock);
1688 umidi->error_timer.function = snd_usbmidi_error_timer; 1705 umidi->error_timer.function = snd_usbmidi_error_timer;
1689 umidi->error_timer.data = (unsigned long)umidi; 1706 umidi->error_timer.data = (unsigned long)umidi;
1690 1707