Commit 198dde08 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: usb-audio: Workaround for XRUN at prepare

Under certain situations (typically in the implicit feedback mode),
USB-audio driver starts a playback stream already at PCM prepare call
even before the actual PCM trigger-START call.  For implicit feedback
mode, this effectively starts two streams for data and sync
endpoints, and if a coupled sync stream gets XRUN at this point, it
results in an error -EPIPE.

The problem is that currently we return -EPIPE error as is from the
prepare.  Then application tries to recover again via the prepare
call, but it'll fail again because the sync-stop is missing.  The
sync-stop is missing because it's an internal trigger call (hence the
PCM core isn't involved).

Since we'll need to re-issue the prepare in anyway when trapped into
this pitfall, this patch attempts to address it in a bit different
way; namely, the driver tries to prepare once again after syncing the
stop manually by itself -- so applications don't see the internal
error.  At the second failure, we report the error as is, but this
shouldn't happen in normal situations.
Reported-and-tested-by: default avatarCarl Hetherington <lists@carlh.net>
Cc: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/b4e71631-4a94-613-27b2-fb595792630@carlh.net
Link: https://lore.kernel.org/r/20221205132124.11585-4-tiwai@suse.deSigned-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent e661c488
...@@ -604,6 +604,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) ...@@ -604,6 +604,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_usb_substream *subs = runtime->private_data; struct snd_usb_substream *subs = runtime->private_data;
struct snd_usb_audio *chip = subs->stream->chip; struct snd_usb_audio *chip = subs->stream->chip;
int retry = 0;
int ret; int ret;
ret = snd_usb_lock_shutdown(chip); ret = snd_usb_lock_shutdown(chip);
...@@ -614,6 +615,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) ...@@ -614,6 +615,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
goto unlock; goto unlock;
} }
again:
if (subs->sync_endpoint) { if (subs->sync_endpoint) {
ret = snd_usb_endpoint_prepare(chip, subs->sync_endpoint); ret = snd_usb_endpoint_prepare(chip, subs->sync_endpoint);
if (ret < 0) if (ret < 0)
...@@ -638,9 +640,16 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) ...@@ -638,9 +640,16 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
subs->lowlatency_playback = lowlatency_playback_available(runtime, subs); subs->lowlatency_playback = lowlatency_playback_available(runtime, subs);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
!subs->lowlatency_playback) !subs->lowlatency_playback) {
ret = start_endpoints(subs); ret = start_endpoints(subs);
/* if XRUN happens at starting streams (possibly with implicit
* fb case), restart again, but only try once.
*/
if (ret == -EPIPE && !retry++) {
sync_pending_stops(subs);
goto again;
}
}
unlock: unlock:
snd_usb_unlock_shutdown(chip); snd_usb_unlock_shutdown(chip);
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment