|
@@ -104,12 +104,14 @@ struct snd_usb_midi {
|
|
|
struct usb_protocol_ops* usb_protocol_ops;
|
|
|
struct list_head list;
|
|
|
struct timer_list error_timer;
|
|
|
+ spinlock_t disc_lock;
|
|
|
|
|
|
struct snd_usb_midi_endpoint {
|
|
|
struct snd_usb_midi_out_endpoint *out;
|
|
|
struct snd_usb_midi_in_endpoint *in;
|
|
|
} endpoints[MIDI_MAX_ENDPOINTS];
|
|
|
unsigned long input_triggered;
|
|
|
+ unsigned char disconnected;
|
|
|
};
|
|
|
|
|
|
struct snd_usb_midi_out_endpoint {
|
|
@@ -306,6 +308,11 @@ static void snd_usbmidi_error_timer(unsigned long data)
|
|
|
struct snd_usb_midi *umidi = (struct snd_usb_midi *)data;
|
|
|
int i;
|
|
|
|
|
|
+ spin_lock(&umidi->disc_lock);
|
|
|
+ if (umidi->disconnected) {
|
|
|
+ spin_unlock(&umidi->disc_lock);
|
|
|
+ return;
|
|
|
+ }
|
|
|
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
|
|
|
struct snd_usb_midi_in_endpoint *in = umidi->endpoints[i].in;
|
|
|
if (in && in->error_resubmit) {
|
|
@@ -316,6 +323,7 @@ static void snd_usbmidi_error_timer(unsigned long data)
|
|
|
if (umidi->endpoints[i].out)
|
|
|
snd_usbmidi_do_output(umidi->endpoints[i].out);
|
|
|
}
|
|
|
+ spin_unlock(&umidi->disc_lock);
|
|
|
}
|
|
|
|
|
|
/* helper function to send static data that may not DMA-able */
|
|
@@ -1049,7 +1057,14 @@ void snd_usbmidi_disconnect(struct list_head* p)
|
|
|
int i;
|
|
|
|
|
|
umidi = list_entry(p, struct snd_usb_midi, list);
|
|
|
- del_timer_sync(&umidi->error_timer);
|
|
|
+ /*
|
|
|
+ * an URB's completion handler may start the timer and
|
|
|
+ * a timer may submit an URB. To reliably break the cycle
|
|
|
+ * a flag under lock must be used
|
|
|
+ */
|
|
|
+ spin_lock_irq(&umidi->disc_lock);
|
|
|
+ umidi->disconnected = 1;
|
|
|
+ spin_unlock_irq(&umidi->disc_lock);
|
|
|
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
|
|
|
struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
|
|
|
if (ep->out)
|
|
@@ -1062,6 +1077,7 @@ void snd_usbmidi_disconnect(struct list_head* p)
|
|
|
if (ep->in)
|
|
|
usb_kill_urb(ep->in->urb);
|
|
|
}
|
|
|
+ del_timer_sync(&umidi->error_timer);
|
|
|
}
|
|
|
|
|
|
static void snd_usbmidi_rawmidi_free(struct snd_rawmidi *rmidi)
|
|
@@ -1685,6 +1701,7 @@ int snd_usb_create_midi_interface(struct snd_usb_audio* chip,
|
|
|
umidi->quirk = quirk;
|
|
|
umidi->usb_protocol_ops = &snd_usbmidi_standard_ops;
|
|
|
init_timer(&umidi->error_timer);
|
|
|
+ spin_lock_init(&umidi->disc_lock);
|
|
|
umidi->error_timer.function = snd_usbmidi_error_timer;
|
|
|
umidi->error_timer.data = (unsigned long)umidi;
|
|
|
|