summaryrefslogtreecommitdiff
path: root/sound/usb/endpoint.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2021-09-29 11:08:44 +0300
committerTakashi Iwai <tiwai@suse.de>2021-09-30 14:55:26 +0300
commit813a17cab9b708bbb1e0db8902e19857b57196ec (patch)
treef91282e2fbb445a1d96d873bce133dd75b8043a1 /sound/usb/endpoint.c
parentd5f871f89e21bb71827ea57bd484eedea85839a0 (diff)
downloadlinux-813a17cab9b708bbb1e0db8902e19857b57196ec.tar.xz
ALSA: usb-audio: Avoid killing in-flight URBs during draining
While draining a stream, ALSA PCM core stops the stream by issuing snd_pcm_stop() after all data has been sent out. And, at PCM trigger stop, currently USB-audio driver kills the in-flight URBs explicitly, then at sync-stop ops, sync with the finish of all remaining URBs. This might result in a drop of the drained samples as most of USB-audio devices / hosts allow relatively long in-flight samples (as a sort of FIFO). For avoiding the trimming, this patch changes the stream-stop behavior during PCM draining state. Under that condition, the pending URBs won't be killed. The leftover in-flight URBs are caught by the sync-stop operation that shall be performed after the trigger-stop operation. Link: https://lore.kernel.org/r/20210929080844.11583-10-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/endpoint.c')
-rw-r--r--sound/usb/endpoint.c14
1 files changed, 9 insertions, 5 deletions
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 0b336876e36d..42c0d2db8ba8 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -955,7 +955,7 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep)
*
* This function moves the EP to STOPPING state if it's being RUNNING.
*/
-static int stop_urbs(struct snd_usb_endpoint *ep, bool force)
+static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending)
{
unsigned int i;
unsigned long flags;
@@ -972,6 +972,9 @@ static int stop_urbs(struct snd_usb_endpoint *ep, bool force)
ep->next_packet_queued = 0;
spin_unlock_irqrestore(&ep->lock, flags);
+ if (keep_pending)
+ return 0;
+
for (i = 0; i < ep->nurbs; i++) {
if (test_bit(i, &ep->active_mask)) {
if (!test_and_set_bit(i, &ep->unlink_mask)) {
@@ -995,7 +998,7 @@ static int release_urbs(struct snd_usb_endpoint *ep, bool force)
snd_usb_endpoint_set_callback(ep, NULL, NULL, NULL);
/* stop and unlink urbs */
- err = stop_urbs(ep, force);
+ err = stop_urbs(ep, force, false);
if (err)
return err;
@@ -1527,7 +1530,7 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
return 0;
__error:
- snd_usb_endpoint_stop(ep);
+ snd_usb_endpoint_stop(ep, false);
return -EPIPE;
}
@@ -1535,6 +1538,7 @@ __error:
* snd_usb_endpoint_stop: stop an snd_usb_endpoint
*
* @ep: the endpoint to stop (may be NULL)
+ * @keep_pending: keep in-flight URBs
*
* A call to this function will decrement the running count of the endpoint.
* In case the last user has requested the endpoint stop, the URBs will
@@ -1545,7 +1549,7 @@ __error:
* The caller needs to synchronize the pending stop operation via
* snd_usb_endpoint_sync_pending_stop().
*/
-void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep)
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, bool keep_pending)
{
if (!ep)
return;
@@ -1560,7 +1564,7 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep)
if (!atomic_dec_return(&ep->running)) {
if (ep->sync_source)
WRITE_ONCE(ep->sync_source->sync_sink, NULL);
- stop_urbs(ep, false);
+ stop_urbs(ep, false, keep_pending);
}
}