diff options
author | Takashi Iwai | 2022-12-05 14:21:24 +0100 |
---|---|---|
committer | Greg Kroah-Hartman | 2022-12-31 13:33:09 +0100 |
commit | 67fd112b4b04030aa8b77a82f999607e7afc57b9 (patch) | |
tree | 52e9e3b04cf5c7be9c4e403043934ed0783f5079 /sound/usb | |
parent | b669bd94a1c8a49b58e77aeddcad44af83d6e23e (diff) |
ALSA: usb-audio: Workaround for XRUN at prepare
commit 198dde085ecc0138e4f0b0b69d18a0c870f2dea6 upstream.
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: Carl 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.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'sound/usb')
-rw-r--r-- | sound/usb/pcm.c | 13 |
1 files changed, 11 insertions, 2 deletions
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 8ed165f036a0..9557bd4d1bbc 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -604,6 +604,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct snd_usb_substream *subs = runtime->private_data; struct snd_usb_audio *chip = subs->stream->chip; + int retry = 0; int ret; ret = snd_usb_lock_shutdown(chip); @@ -614,6 +615,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) goto unlock; } + again: if (subs->sync_endpoint) { ret = snd_usb_endpoint_prepare(chip, subs->sync_endpoint); if (ret < 0) @@ -638,9 +640,16 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) subs->lowlatency_playback = lowlatency_playback_available(runtime, subs); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && - !subs->lowlatency_playback) + !subs->lowlatency_playback) { 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: snd_usb_unlock_shutdown(chip); return ret; |