* [PATCH 1/2] ALSA: aloop: Update the PCM Slave controls for a matching playback
2026-10-21 7:46 [PATCH 0/2] ALSA: aloop: Update the PCM Slave controls for a matching playback Henrik Enquist via B4 Relay
@ 2026-10-21 7:46 ` Henrik Enquist via B4 Relay
2026-10-21 7:46 ` [PATCH 2/2] selftests/alsa: Check the aloop " Henrik Enquist via B4 Relay
2026-10-21 12:56 ` [PATCH 0/2] ALSA: aloop: Update the " Takashi Iwai
2 siblings, 0 replies; 4+ messages in thread
From: Henrik Enquist via B4 Relay @ 2026-10-21 7:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai, Mark Brown, Shuah Khan
Cc: linux-sound, linux-kernel, linux-kselftest, stable, Henrik Enquist
From: Henrik Enquist <henrik.enquist@gmail.com>
The "PCM Slave" format, rate, channels and access controls report the
parameters of the playback side of a cable. loopback_check_format()
updates them when a playback starts, but returns early when a capture
is open and its parameters match. The controls then keep the module
defaults, or the parameters of an earlier stream.
A playback that joins an open capture with the same parameters is the
normal case without notify, where it is constrained to the capture's
parameters, and also with notify whenever the player happens to use
them. An application that reads the controls to find the playback's
parameters, for example to reopen the capture side at the right rate
after a restart, then gets the wrong values.
Only skip the capture stop when the parameters match, and update the
controls in every case. A value event is still sent only for a control
whose value changes.
Reproducer:
arecord -D hw:Loopback,1,0 -r 44100 -f S16_LE -c 2 /dev/null &
aplay -D hw:Loopback,0,0 -t raw -r 44100 -f S16_LE -c 2 /dev/zero &
amixer -c Loopback cget "iface=PCM,name='PCM Slave Rate',device=1"
Before: the control reports 48000, the module default.
After: it reports 44100.
Fixes: 597603d615d2 ("ALSA: introduce the snd-aloop module for the PCM loopback")
Cc: stable@vger.kernel.org
Signed-off-by: Henrik Enquist <henrik.enquist@gmail.com>
---
sound/drivers/aloop.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index d6a059ab37..824d320b66 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -412,11 +412,14 @@ static int loopback_check_format(struct loopback_cable *cable, int stream)
runtime->channels != cruntime->channels ||
is_access_interleaved(runtime->access) !=
is_access_interleaved(cruntime->access);
- if (!check)
- return 0;
- if (stream == SNDRV_PCM_STREAM_CAPTURE)
+ /*
+ * Matching params also go on to the update below, so that
+ * the setup controls follow a playback that joins an open
+ * capture.
+ */
+ if (check && stream == SNDRV_PCM_STREAM_CAPTURE)
return -EIO;
- else if (cruntime->state == SNDRV_PCM_STATE_RUNNING) {
+ else if (check && cruntime->state == SNDRV_PCM_STATE_RUNNING) {
/* close must not free the peer runtime below */
snd_refcount_get(&cable->stop_count);
stop_capture = true;
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH 2/2] selftests/alsa: Check the aloop PCM Slave controls for a matching playback
2026-10-21 7:46 [PATCH 0/2] ALSA: aloop: Update the PCM Slave controls for a matching playback Henrik Enquist via B4 Relay
2026-10-21 7:46 ` [PATCH 1/2] " Henrik Enquist via B4 Relay
@ 2026-10-21 7:46 ` Henrik Enquist via B4 Relay
2026-10-21 12:56 ` [PATCH 0/2] ALSA: aloop: Update the " Takashi Iwai
2 siblings, 0 replies; 4+ messages in thread
From: Henrik Enquist via B4 Relay @ 2026-10-21 7:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai, Mark Brown, Shuah Khan
Cc: linux-sound, linux-kernel, linux-kselftest, Henrik Enquist
From: Henrik Enquist <henrik.enquist@gmail.com>
Add a test that starts a playback with the same parameters as a running
capture, and checks that the capture keeps running and that the
"PCM Slave" controls report the playback's parameters, with a value
event for each control that changed. Before the previous patch, the
controls keep the parameters of the earlier stream and the test fails.
Signed-off-by: Henrik Enquist <henrik.enquist@gmail.com>
---
tools/testing/selftests/alsa/aloop-test.c | 40 +++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/tools/testing/selftests/alsa/aloop-test.c b/tools/testing/selftests/alsa/aloop-test.c
index a58b9fdbdc..74d6ccc127 100644
--- a/tools/testing/selftests/alsa/aloop-test.c
+++ b/tools/testing/selftests/alsa/aloop-test.c
@@ -323,6 +323,46 @@ TEST_F(aloop, params_change_stops_capture_with_notify) {
snd_pcm_close(capt);
}
+/*
+ * A playback that joins a running capture with the same parameters doesn't
+ * stop it, and the "PCM Slave" controls still report the playback's
+ * parameters rather than those of an earlier stream.
+ */
+TEST_F(aloop, matching_playback_updates_controls) {
+ snd_pcm_t *capt, *play;
+ unsigned int events;
+
+ /* Leave the controls at other parameters than the ones used below. */
+ ASSERT_EQ(open_pcm(&play, self->play_name, SND_PCM_STREAM_PLAYBACK, &playback_params), 0);
+ ASSERT_EQ(start_playback(play, &playback_params), 0);
+ snd_pcm_close(play);
+
+ ASSERT_EQ(set_notify(self->ctl, false), 0);
+ ASSERT_EQ(open_pcm(&capt, self->capt_name, SND_PCM_STREAM_CAPTURE, &capture_params), 0);
+ ASSERT_EQ(snd_pcm_start(capt), 0);
+ read_events(self->ctl);
+
+ ASSERT_EQ(open_pcm(&play, self->play_name, SND_PCM_STREAM_PLAYBACK, &capture_params), 0);
+ ASSERT_EQ(start_playback(play, &capture_params), 0);
+
+ EXPECT_EQ(snd_pcm_state(capt), SND_PCM_STATE_RUNNING);
+
+ EXPECT_EQ(cable_ctl_get(self->ctl, "PCM Slave Active"), 1);
+ EXPECT_EQ(cable_ctl_get(self->ctl, "PCM Slave Format"), capture_params.format);
+ EXPECT_EQ(cable_ctl_get(self->ctl, "PCM Slave Rate"), capture_params.rate);
+ EXPECT_EQ(cable_ctl_get(self->ctl, "PCM Slave Channels"), capture_params.channels);
+ EXPECT_EQ(cable_ctl_get(self->ctl, "PCM Slave Access Mode"), 0);
+
+ events = read_events(self->ctl);
+ EXPECT_TRUE(events & EV_FORMAT);
+ EXPECT_TRUE(events & EV_RATE);
+ EXPECT_TRUE(events & EV_CHANNELS);
+ EXPECT_TRUE(events & EV_ACCESS);
+
+ snd_pcm_close(play);
+ snd_pcm_close(capt);
+}
+
/* With notify, a capture opened second is still pinned to the playback's parameters. */
TEST_F(aloop, capture_constrained_with_notify) {
struct probe_result res;
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH 0/2] ALSA: aloop: Update the PCM Slave controls for a matching playback
2026-10-21 7:46 [PATCH 0/2] ALSA: aloop: Update the PCM Slave controls for a matching playback Henrik Enquist via B4 Relay
2026-10-21 7:46 ` [PATCH 1/2] " Henrik Enquist via B4 Relay
2026-10-21 7:46 ` [PATCH 2/2] selftests/alsa: Check the aloop " Henrik Enquist via B4 Relay
@ 2026-10-21 12:56 ` Takashi Iwai
2 siblings, 0 replies; 4+ messages in thread
From: Takashi Iwai @ 2026-10-21 12:56 UTC (permalink / raw)
To: henrik.enquist
Cc: Jaroslav Kysela, Takashi Iwai, Mark Brown, Shuah Khan,
linux-sound, linux-kernel, linux-kselftest, stable
On Wed, 21 Oct 2026 09:46:44 +0200,
Henrik Enquist via B4 Relay wrote:
>
> Hi,
>
> One more aloop fix, after the notify mode one (ded1060369 "ALSA:
> aloop: Don't constrain playback params in notify mode"). I ran into
> this while working on a new CamillaDSP feature that uses notify mode.
> The "PCM Slave" controls are meant to report the playback's
> parameters, but often they don't. loopback_check_format() returns
> early when a capture is open and the parameters match, before it
> updates the controls. They then keep the module defaults, or the
> values of an earlier stream.
>
> This isn't specific to notify mode. Without notify, a playback that
> joins a running capture always has matching parameters, so the controls
> are never updated in that case. The early return has been there since
> the driver was added, hence the Fixes tag on the 2010 commit.
>
> Patch 1 skips only the capture stop when the parameters match, and
> always updates the controls. A value event is still sent only for a
> control whose value changes, so nothing new shows up except where a
> stale value gets corrected.
>
> Patch 2 adds a test for it to the aloop selftest. It doesn't need to go
> to stable, so it can wait for the next merge window if that's easier.
>
> Testing: for-linus at 443ed4edc2, arm64 VM, kernel with KASAN,
> lockdep, DEBUG_ATOMIC_SLEEP and DEBUG_LIST, comparing the patched
> driver with the unpatched one built from the same tree.
> - The selftest passes 5 of 5 patched. Unpatched, only the new test
> fails: the controls keep the earlier stream's values (96000 Hz,
> S32_LE, 4 channels, non-interleaved) and no value events arrive.
> - The reproducer in patch 1 reports 48000 unpatched and 44100 patched.
> - No KASAN, lockdep or other warnings in dmesg.
> Not tested: real hardware, other architectures.
>
> AI disclosure: as with the notify series, I used Claude Code (Claude
> Opus 5.5). It traced the bug through the driver and its history, wrote
> both patches and their changelogs, built the test kernels and ran the
> tests. I reviewed all of it.
>
> Henrik
>
> ---
> Henrik Enquist (2):
> ALSA: aloop: Update the PCM Slave controls for a matching playback
> selftests/alsa: Check the aloop PCM Slave controls for a matching playback
Applied both patches now. Thanks.
Takashi
^ permalink raw reply [flat|nested] 4+ messages in thread