]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
ALSA: usb-audio: Fix crash at re-preparing the PCM stream
authorTakashi Iwai <tiwai@suse.de>
Tue, 20 Nov 2012 07:20:02 +0000 (08:20 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 26 Nov 2012 20:14:19 +0000 (12:14 -0800)
commit f58161ba1b05a968e5136824b5a16b714b6a5317 upstream.

There are bug reports of a crash with USB-audio devices when PCM
prepare is performed immediately after the stream is stopped via
trigger callback.  It turned out that the problem is that we don't
wait until all URBs are killed.

This patch adds a new function to synchronize the pending stop
operation on an endpoint, and calls in the prepare callback for
avoiding the crash above.

Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=49181

Reported-and-tested-by: Artem S. Tashkinov <t.artem@lycos.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
sound/usb/endpoint.c
sound/usb/endpoint.h
sound/usb/pcm.c

index d9de6670b97ef85bcb14547b6e5302319afeba3d..eafc8897af7dda2418a8272ae9fe7fa272f4925b 100644 (file)
@@ -35,6 +35,7 @@
 
 #define EP_FLAG_ACTIVATED      0
 #define EP_FLAG_RUNNING                1
+#define EP_FLAG_STOPPING       2
 
 /*
  * snd_usb_endpoint is a model that abstracts everything related to an
@@ -502,10 +503,20 @@ static int wait_clear_urbs(struct snd_usb_endpoint *ep)
        if (alive)
                snd_printk(KERN_ERR "timeout: still %d active urbs on EP #%x\n",
                                        alive, ep->ep_num);
+       clear_bit(EP_FLAG_STOPPING, &ep->flags);
 
        return 0;
 }
 
+/* sync the pending stop operation;
+ * this function itself doesn't trigger the stop operation
+ */
+void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep)
+{
+       if (ep && test_bit(EP_FLAG_STOPPING, &ep->flags))
+               wait_clear_urbs(ep);
+}
+
 /*
  * unlink active urbs.
  */
@@ -913,6 +924,8 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
 
                if (wait)
                        wait_clear_urbs(ep);
+               else
+                       set_bit(EP_FLAG_STOPPING, &ep->flags);
        }
 }
 
index cbbbdf226d66b6fa9eec51fe829a1b03cdd8d518..774a0ebc8c5ac5fde2d2d595ad79d858b92f3e95 100644 (file)
@@ -16,6 +16,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
 int  snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep);
 void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
                           int force, int can_sleep, int wait);
+void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
 int  snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
 int  snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
 void snd_usb_endpoint_free(struct list_head *head);
index ee3c15c546e0350fe741481763557f131749c5df..90731af0a762031aca2d878b4ff8e3af644757eb 100644 (file)
@@ -563,6 +563,9 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
                goto unlock;
        }
 
+       snd_usb_endpoint_sync_pending_stop(subs->sync_endpoint);
+       snd_usb_endpoint_sync_pending_stop(subs->data_endpoint);
+
        /* some unit conversions in runtime */
        subs->data_endpoint->maxframesize =
                bytes_to_frames(runtime, subs->data_endpoint->maxpacksize);