* [PATCH v2 1/2] ALSA: doc: pcmtest: Update to match the driver
2026-11-11 9:04 [PATCH v2 0/2] ALSA: pcmtest: Add a volume control with event fault injection HyeongJun An
@ 2026-11-11 9:04 ` HyeongJun An
2026-11-11 9:04 ` [PATCH v2 2/2] ALSA: pcmtest: Add a volume control with event fault injection HyeongJun An
1 sibling, 0 replies; 3+ messages in thread
From: HyeongJun An @ 2026-11-11 9:04 UTC (permalink / raw)
To: tiwai
Cc: perex, ivan.orlov0322, broonie, linux-sound, linux-kernel, HyeongJun An
The documentation does not match the driver in a few places.
The inject_open_err parameter is missing from the parameter list and
from the list of callbacks that can return an error. The fill_mode
parameter is a short, not a bool. The selftest is
alsa/test-pcmtest-driver.c, not alsa/pcmtest-test.sh. Pause support is
not listed.
Fix these.
Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
---
Documentation/sound/cards/pcmtest.rst | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/Documentation/sound/cards/pcmtest.rst b/Documentation/sound/cards/pcmtest.rst
index e163522f3205..ed0b57f583b7 100644
--- a/Documentation/sound/cards/pcmtest.rst
+++ b/Documentation/sound/cards/pcmtest.rst
@@ -16,22 +16,24 @@ At this moment the driver can do the following things:
* Generate random or pattern-based capturing data
* Inject delays into the playback and capturing processes
* Inject errors during the PCM callbacks
+ * Pause the streams
It supports up to 8 substreams and 4 channels. Also it supports both interleaved and
non-interleaved access modes.
Also, this driver can check the playback stream for containing the predefined pattern,
-which is used in the corresponding selftest (alsa/pcmtest-test.sh) to check the PCM middle
-layer data transferring functionality. Additionally, this driver redefines the default
-RESET ioctl, and the selftest covers this PCM API functionality as well.
+which is used in the corresponding selftest (alsa/test-pcmtest-driver.c) to check the PCM
+middle layer data transferring functionality. Additionally, this driver redefines the
+default RESET ioctl, and the selftest covers this PCM API functionality as well.
Configuration
-------------
The driver has several parameters besides the common ALSA module parameters:
- * fill_mode (bool) - Buffer fill mode (see below)
+ * fill_mode (short) - Buffer fill mode (see below)
* inject_delay (int)
+ * inject_open_err (bool)
* inject_hwpars_err (bool)
* inject_prepare_err (bool)
* inject_trigger_err (bool)
@@ -93,6 +95,7 @@ writing '1' to the 'inject_hwpars_err' module parameter:
Errors can be injected into the following PCM callbacks:
+ * open (EBUSY)
* hw_params (EBUSY)
* prepare (EINVAL)
* trigger (EINVAL)
--
2.43.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v2 2/2] ALSA: pcmtest: Add a volume control with event fault injection
2026-11-11 9:04 [PATCH v2 0/2] ALSA: pcmtest: Add a volume control with event fault injection HyeongJun An
2026-11-11 9:04 ` [PATCH v2 1/2] ALSA: doc: pcmtest: Update to match the driver HyeongJun An
@ 2026-11-11 9:04 ` HyeongJun An
1 sibling, 0 replies; 3+ messages in thread
From: HyeongJun An @ 2026-11-11 9:04 UTC (permalink / raw)
To: tiwai
Cc: perex, ivan.orlov0322, broonie, linux-sound, linux-kernel, HyeongJun An
The mixer-test selftest checks that a control sends an event when its
value changes, and only then. No virtual driver has a control that
breaks this rule, so without broken hardware nothing shows that these
checks work.
Add a stereo volume control to pcmtest, and two parameters that make
its put callback report the wrong result. With inject_ctl_no_event it
reports no change, and with inject_ctl_spurious_event it reports a
change every time. Document both parameters.
The control is always present. It is named "Test Playback Volume", a
name that the OSS mixer emulation does not map.
With both parameters off, mixer-test passes on the control. With
either one on, only the matching event check fails.
Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
---
Documentation/sound/cards/pcmtest.rst | 14 ++++++
sound/drivers/pcmtest.c | 68 +++++++++++++++++++++++++++
2 files changed, 82 insertions(+)
diff --git a/Documentation/sound/cards/pcmtest.rst b/Documentation/sound/cards/pcmtest.rst
index ed0b57f583b7..351f1123b8e7 100644
--- a/Documentation/sound/cards/pcmtest.rst
+++ b/Documentation/sound/cards/pcmtest.rst
@@ -17,6 +17,7 @@ At this moment the driver can do the following things:
* Inject delays into the playback and capturing processes
* Inject errors during the PCM callbacks
* Pause the streams
+ * Report wrong change results from a mixer control
It supports up to 8 substreams and 4 channels. Also it supports both interleaved and
non-interleaved access modes.
@@ -37,6 +38,8 @@ The driver has several parameters besides the common ALSA module parameters:
* inject_hwpars_err (bool)
* inject_prepare_err (bool)
* inject_trigger_err (bool)
+ * inject_ctl_no_event (bool)
+ * inject_ctl_spurious_event (bool)
Capture Data Generation
@@ -100,6 +103,17 @@ Errors can be injected into the following PCM callbacks:
* prepare (EINVAL)
* trigger (EINVAL)
+Control event injection
+-----------------------
+
+The driver has a 'Test Playback Volume' control. Its 'put' callback reports a change
+only when the value changes, so a change event is sent only then.
+
+With 'inject_ctl_no_event' set, the callback reports no change, and no event is sent
+even when the value changes. With 'inject_ctl_spurious_event' set, it reports a change
+on every accepted write. The mixer-test selftest (alsa/mixer-test.c) checks for both.
+If both parameters are set, 'inject_ctl_no_event' wins.
+
Playback test
-------------
diff --git a/sound/drivers/pcmtest.c b/sound/drivers/pcmtest.c
index 66bee8a2ce34..98685e6158cc 100644
--- a/sound/drivers/pcmtest.c
+++ b/sound/drivers/pcmtest.c
@@ -34,6 +34,7 @@
#include <linux/init.h>
#include <sound/pcm.h>
#include <sound/core.h>
+#include <sound/control.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/string.h>
@@ -57,6 +58,8 @@
#define MAX_PATTERN_LEN 4096
+#define MAX_VOLUME 100
+
static int index = -1;
static char *id = "pcmtest";
static bool enable = true;
@@ -65,6 +68,8 @@ static bool inject_hwpars_err;
static bool inject_prepare_err;
static bool inject_trigger_err;
static bool inject_open_err;
+static bool inject_ctl_no_event;
+static bool inject_ctl_spurious_event;
static short fill_mode = FILL_MODE_PAT;
@@ -109,11 +114,17 @@ module_param(inject_trigger_err, bool, 0600);
MODULE_PARM_DESC(inject_trigger_err, "Inject EINVAL error in the 'trigger' callback");
module_param(inject_open_err, bool, 0600);
MODULE_PARM_DESC(inject_open_err, "Inject EBUSY error in the 'open' callback");
+module_param(inject_ctl_no_event, bool, 0600);
+MODULE_PARM_DESC(inject_ctl_no_event, "Report no change from the control 'put' callback");
+module_param(inject_ctl_spurious_event, bool, 0600);
+MODULE_PARM_DESC(inject_ctl_spurious_event, "Always report a change from the control 'put' callback");
struct pcmtst {
struct snd_pcm *pcm;
struct snd_card *card;
struct platform_device *pdev;
+ spinlock_t mixer_lock; /* protects volume[] */
+ long volume[2];
};
struct pcmtst_buf_iter {
@@ -544,6 +555,58 @@ static const struct snd_pcm_ops snd_pcmtst_capture_ops = {
.pointer = snd_pcmtst_pcm_pointer,
};
+static int snd_pcmtst_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = MAX_VOLUME;
+ return 0;
+}
+
+static int snd_pcmtst_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct pcmtst *pcmtst = snd_kcontrol_chip(kcontrol);
+
+ guard(spinlock_irq)(&pcmtst->mixer_lock);
+ ucontrol->value.integer.value[0] = pcmtst->volume[0];
+ ucontrol->value.integer.value[1] = pcmtst->volume[1];
+ return 0;
+}
+
+static int snd_pcmtst_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct pcmtst *pcmtst = snd_kcontrol_chip(kcontrol);
+ long left = ucontrol->value.integer.value[0];
+ long right = ucontrol->value.integer.value[1];
+ bool changed;
+
+ if (left < 0 || left > MAX_VOLUME || right < 0 || right > MAX_VOLUME)
+ return -EINVAL;
+
+ guard(spinlock_irq)(&pcmtst->mixer_lock);
+ changed = pcmtst->volume[0] != left || pcmtst->volume[1] != right;
+ pcmtst->volume[0] = left;
+ pcmtst->volume[1] = right;
+
+ if (inject_ctl_no_event)
+ return 0;
+ if (inject_ctl_spurious_event)
+ return 1;
+ return changed;
+}
+
+static const struct snd_kcontrol_new snd_pcmtst_volume = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Test Playback Volume",
+ .info = snd_pcmtst_volume_info,
+ .get = snd_pcmtst_volume_get,
+ .put = snd_pcmtst_volume_put,
+};
+
static int snd_pcmtst_new_pcm(struct pcmtst *pcmtst)
{
struct snd_pcm *pcm;
@@ -582,11 +645,16 @@ static int pcmtst_probe(struct platform_device *pdev)
pcmtst = card->private_data;
pcmtst->card = card;
pcmtst->pdev = pdev;
+ spin_lock_init(&pcmtst->mixer_lock);
err = snd_pcmtst_new_pcm(pcmtst);
if (err < 0)
return err;
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_pcmtst_volume, pcmtst));
+ if (err < 0)
+ return err;
+
strscpy(card->driver, "PCM-TEST Driver");
strscpy(card->shortname, "PCM-Test");
strscpy(card->longname, "PCM-Test virtual driver");
--
2.43.0
^ permalink raw reply [flat|nested] 3+ messages in thread