From: Joonho Lee <mynameisjune111@gmail.com>
To: Takashi Iwai <tiwai@suse.de>
Cc: Takashi Iwai <tiwai@suse.com>, Jaroslav Kysela <perex@perex.cz>,
linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-api@vger.kernel.org
Subject: [RFC PATCH v4 1/2] ALSA: pcm: allow capture reads while draining
Date: Thu, 15 Oct 2026 09:36:05 +0900 [thread overview]
Message-ID: <rfc-v4-core-capture-drain-1.b56779f7976d4657@gmail.com> (raw)
In-Reply-To: <rfc-v4-core-capture-drain-0.b56779f7976d4657@gmail.com>
Capture drain can leave buffered samples in DRAINING, but the transfer
state check rejects reads in that state. Allow capture reads to consume
the remaining frames while preserving the playback state check.
When drain wakes a blocked capture reader, return the actual available
frame count from wait_for_avail(). A zero count would make the transfer
loop discard a nonempty tail. Finish capture EOF with snd_pcm_drain_done()
so the state transition applies to that substream and leaves a linked
playback stream draining its queued data.
Allow capture hwsync to use the cached final pointer in DRAINING. Capture
has already stopped, so DELAY and SYNC_PTR(HWSYNC) can report its remaining
frames without another driver pointer callback. Playback retains its
existing pointer update.
Synthetic atomic and nonatomic tests reproduce dropped tail frames,
linked playback stopping at capture EOF, and rejected position queries.
With this change, a blocked read gets all 64 tail frames, playback stays
DRAINING at capture EOF, and the queries succeed with no extra callback.
Assisted-by: LLM
Signed-off-by: Joonho Lee <mynameisjune111@gmail.com>
---
sound/core/pcm_lib.c | 16 ++++++++++------
sound/core/pcm_native.c | 2 +-
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 4d665b4148d7..e8f6a9da4a4c 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -2009,8 +2009,8 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
case SNDRV_PCM_STATE_DRAINING:
if (is_playback)
err = -EPIPE;
- else
- avail = 0; /* indicate draining */
+ else
+ avail = snd_pcm_avail(substream);
goto _endloop;
case SNDRV_PCM_STATE_OPEN:
case SNDRV_PCM_STATE_SETUP:
@@ -2208,13 +2208,17 @@ static int pcm_sanity_check(struct snd_pcm_substream *substream)
return 0;
}
-static int pcm_accessible_state(struct snd_pcm_runtime *runtime)
+static int pcm_accessible_state(struct snd_pcm_substream *substream)
{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
switch (runtime->state) {
case SNDRV_PCM_STATE_PREPARED:
case SNDRV_PCM_STATE_RUNNING:
case SNDRV_PCM_STATE_PAUSED:
return 0;
+ case SNDRV_PCM_STATE_DRAINING:
+ return substream->stream == SNDRV_PCM_STREAM_CAPTURE ? 0 : -EBADFD;
case SNDRV_PCM_STATE_XRUN:
return -EPIPE;
case SNDRV_PCM_STATE_SUSPENDED:
@@ -2320,7 +2324,7 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
nonblock = !!(substream->f_flags & O_NONBLOCK);
snd_pcm_stream_lock_irq(substream);
- err = pcm_accessible_state(runtime);
+ err = pcm_accessible_state(substream);
if (err < 0)
goto _end_unlock;
@@ -2348,7 +2352,7 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
if (!avail) {
if (!is_playback &&
runtime->state == SNDRV_PCM_STATE_DRAINING) {
- snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+ snd_pcm_drain_done(substream);
goto _end_unlock;
}
if (nonblock) {
@@ -2388,7 +2392,7 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
atomic_dec(&runtime->buffer_accessing);
if (err < 0)
goto _end_unlock;
- err = pcm_accessible_state(runtime);
+ err = pcm_accessible_state(substream);
if (err < 0)
goto _end_unlock;
appl_ptr += frames;
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index defbb2977efe..657e77d33b21 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -3014,7 +3014,7 @@ static int do_pcm_hwsync(struct snd_pcm_substream *substream)
switch (substream->runtime->state) {
case SNDRV_PCM_STATE_DRAINING:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- return -EBADFD;
+ return 0;
fallthrough;
case SNDRV_PCM_STATE_RUNNING:
return snd_pcm_update_hw_ptr(substream);
next prev parent reply other threads:[~2026-10-15 0:36 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-15 0:35 [RFC PATCH v4 0/2] ALSA: pcm: preserve capture tail during drain Joonho Lee
2026-10-15 0:36 ` Joonho Lee [this message]
2026-10-15 0:36 ` [RFC PATCH v4 2/2] ALSA: pcm: refresh linked pointers before draining Joonho Lee
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=rfc-v4-core-capture-drain-1.b56779f7976d4657@gmail.com \
--to=mynameisjune111@gmail.com \
--cc=linux-api@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sound@vger.kernel.org \
--cc=perex@perex.cz \
--cc=tiwai@suse.com \
--cc=tiwai@suse.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox