* [RFC PATCH 01/11] ALSA: rename mixer_evo to mixer_audient
2026-11-01 0:00 ` [RFC PATCH 00/11] " Christian Ruppert
@ 2026-11-01 0:00 ` Christian Ruppert
2026-11-01 0:00 ` [RFC PATCH 02/11] ALSA: mixer_audient: explicitly define full control names Christian Ruppert
` (10 subsequent siblings)
11 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-01 0:00 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Rename mixer_evo files and functions to *_audient in preparation of a more
generic driver for audient devices.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
MAINTAINERS | 7 +-
sound/usb/Makefile | 2 +-
sound/usb/{mixer_evo.c => mixer_audient.c} | 247 +++++++++++----------
sound/usb/mixer_audient.h | 12 +
sound/usb/mixer_evo.h | 12 -
sound/usb/mixer_quirks.c | 4 +-
6 files changed, 147 insertions(+), 137 deletions(-)
rename sound/usb/{mixer_evo.c => mixer_audient.c} (54%)
create mode 100644 sound/usb/mixer_audient.h
delete mode 100644 sound/usb/mixer_evo.h
diff --git a/MAINTAINERS b/MAINTAINERS
index fee9d090b280..a1139ae3404d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4354,12 +4354,13 @@ F: drivers/net/ieee802154/at86rf230.h
F: drivers/net/ieee802154/atusb.c
F: drivers/net/ieee802154/atusb.h
-AUDIENT EVO MIXER DRIVER
+AUDIENT MIXER DRIVER
M: Christian Ruppert <arc@gmx.li>
+M: Will Porter <mrwillporter@gmail.com>
L: linux-sound@vger.kernel.org
S: Maintained
-F: sound/usb/mixer_evo.c
-F: sound/usb/mixer_evo.h
+F: sound/usb/mixer_audient.c
+F: sound/usb/mixer_audient.h
AUDIT SUBSYSTEM
M: Paul Moore <paul@paul-moore.com>
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index 2e842dcc73de..44f4e4c820fb 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -11,7 +11,7 @@ snd-usb-audio-y := card.o \
helper.o \
implicit.o \
mixer.o \
- mixer_evo.o \
+ mixer_audient.o \
mixer_quirks.o \
mixer_scarlett.o \
mixer_scarlett2.o \
diff --git a/sound/usb/mixer_evo.c b/sound/usb/mixer_audient.c
similarity index 54%
rename from sound/usb/mixer_evo.c
rename to sound/usb/mixer_audient.c
index c1e0332232f2..8c1524c9b165 100644
--- a/sound/usb/mixer_evo.c
+++ b/sound/usb/mixer_audient.c
@@ -3,7 +3,7 @@
* Audient EVO driver for ALSA
* Copyright (C) 2026 Christian Ruppert <arc@gmx.li>
*
- * Based on the work of Ivan Hazucha for audient-evo-py.
+ * EVO4 support is based on the work of Ivan Hazucha for audient-evo-py.
* https://github.com/vanzaho/audient-evo-py/
*/
@@ -20,22 +20,22 @@
#include "mixer.h"
#include "mixer_quirks.h"
#include "helper.h"
-#include "mixer_evo.h"
+#include "mixer_audient.h"
-enum snd_evo_type {
- SND_EVO_TYPE_MONITOR = 0,
- SND_EVO_TYPE_PHANTOM,
- SND_EVO_TYPE_MUTE,
- SND_EVO_TYPE_MUTE_OUT,
- SND_EVO_TYPE_MIXER,
- SND_EVO_TYPE_NCTYPES
+enum snd_audient_type {
+ SND_AUDIENT_TYPE_MONITOR = 0,
+ SND_AUDIENT_TYPE_PHANTOM,
+ SND_AUDIENT_TYPE_MUTE,
+ SND_AUDIENT_TYPE_MUTE_OUT,
+ SND_AUDIENT_TYPE_MIXER,
+ SND_AUDIENT_TYPE_NCTYPES
};
/* Mixer matrix: -128dB (mute) to +6dB */
-static const DECLARE_TLV_DB_MINMAX_MUTE(snd_evo_tlv_mixer, -12800, 600);
+static const DECLARE_TLV_DB_MINMAX_MUTE(snd_audient_tlv_mixer, -12800, 600);
-static const struct snd_evo_ctrl_type {
+static const struct snd_audient_ctrl_type {
snd_ctl_elem_type_t type;
bool invert;
union {
@@ -47,8 +47,8 @@ static const struct snd_evo_ctrl_type {
const unsigned int *tlv;
u16 wValue;
u16 wIndex;
-} snd_evo_ctypes[SND_EVO_TYPE_NCTYPES] = {
- [SND_EVO_TYPE_MONITOR] = {
+} snd_audient_ctypes[SND_AUDIENT_TYPE_NCTYPES] = {
+ [SND_AUDIENT_TYPE_MONITOR] = {
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.min.integer = 0,
.max.integer = 127,
@@ -56,7 +56,7 @@ static const struct snd_evo_ctrl_type {
.wIndex = 0x3800,
/* no TLV here because we can't describe the cross-fader */
},
- [SND_EVO_TYPE_PHANTOM] = {
+ [SND_AUDIENT_TYPE_PHANTOM] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.invert = false,
.min.integer = 0,
@@ -64,7 +64,7 @@ static const struct snd_evo_ctrl_type {
.wValue = 0x0000,
.wIndex = 0x3A00,
},
- [SND_EVO_TYPE_MUTE] = {
+ [SND_AUDIENT_TYPE_MUTE] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.invert = true,
.min.integer = 0,
@@ -72,7 +72,7 @@ static const struct snd_evo_ctrl_type {
.wValue = 0x0200,
.wIndex = 0x3A00,
},
- [SND_EVO_TYPE_MUTE_OUT] = {
+ [SND_AUDIENT_TYPE_MUTE_OUT] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.invert = true,
.min.integer = 0,
@@ -80,135 +80,135 @@ static const struct snd_evo_ctrl_type {
.wValue = 0x0100,
.wIndex = 0x3B00,
},
- [SND_EVO_TYPE_MIXER] = {
+ [SND_AUDIENT_TYPE_MIXER] = {
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.min.integer = -128 * 256,
.max.integer = 6 * 256,
.wValue = 0x0100,
.wIndex = 0x3C00,
- .tlv = snd_evo_tlv_mixer,
+ .tlv = snd_audient_tlv_mixer,
},
};
-#define SND_EVO_FLAGS_DIRECTION (1L<<0)
-#define SND_EVO_FLAGS_CAPTURE (1L<<0)
-#define SND_EVO_FLAGS_PLAYBACK (0L<<0)
+#define SND_AUDIENT_FLAGS_DIRECTION (1L<<0)
+#define SND_AUDIENT_FLAGS_CAPTURE (1L<<0)
+#define SND_AUDIENT_FLAGS_PLAYBACK (0L<<0)
-struct snd_evo_ctrl {
+struct snd_audient_ctrl {
char *basename;
- enum snd_evo_type type;
+ enum snd_audient_type type;
unsigned int channels;
unsigned int hwchannels;
unsigned int val_offs;
unsigned long flags;
};
-static const struct snd_evo_ctrl evo4_controls[] = {
+static const struct snd_audient_ctrl snd_audient_evo4_controls[] = {
{
.basename = "Master",
- .type = SND_EVO_TYPE_MUTE_OUT,
+ .type = SND_AUDIENT_TYPE_MUTE_OUT,
.channels = 1,
.hwchannels = 1,
.val_offs = 0,
- .flags = SND_EVO_FLAGS_PLAYBACK,
+ .flags = SND_AUDIENT_FLAGS_PLAYBACK,
},
{
.basename = "Mic",
- .type = SND_EVO_TYPE_MUTE,
+ .type = SND_AUDIENT_TYPE_MUTE,
.channels = 4,
.hwchannels = 2,
.val_offs = 0,
- .flags = SND_EVO_FLAGS_PLAYBACK,
+ .flags = SND_AUDIENT_FLAGS_PLAYBACK,
},
{
.basename = "Monitor",
- .type = SND_EVO_TYPE_MONITOR,
+ .type = SND_AUDIENT_TYPE_MONITOR,
.channels = 1,
.hwchannels = 1,
.val_offs = 0,
- .flags = SND_EVO_FLAGS_PLAYBACK,
+ .flags = SND_AUDIENT_FLAGS_PLAYBACK,
},
{
.basename = "Mic 1",
- .type = SND_EVO_TYPE_MIXER,
+ .type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 0,
- .flags = SND_EVO_FLAGS_CAPTURE,
+ .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
.basename = "Mic 2",
- .type = SND_EVO_TYPE_MIXER,
+ .type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 2,
- .flags = SND_EVO_FLAGS_CAPTURE,
+ .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
.basename = "Master Left",
- .type = SND_EVO_TYPE_MIXER,
+ .type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 4,
- .flags = SND_EVO_FLAGS_CAPTURE,
+ .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
.basename = "Master Right",
- .type = SND_EVO_TYPE_MIXER,
+ .type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 6,
- .flags = SND_EVO_FLAGS_CAPTURE,
+ .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
.basename = "Loopback Left",
- .type = SND_EVO_TYPE_MIXER,
+ .type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 8,
- .flags = SND_EVO_FLAGS_CAPTURE,
+ .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
.basename = "Loopback Right",
- .type = SND_EVO_TYPE_MIXER,
+ .type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 10,
- .flags = SND_EVO_FLAGS_CAPTURE,
+ .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
.basename = "Mic 1 Phantom",
- .type = SND_EVO_TYPE_PHANTOM,
+ .type = SND_AUDIENT_TYPE_PHANTOM,
.channels = 1,
.hwchannels = 1,
.val_offs = 0,
- .flags = SND_EVO_FLAGS_CAPTURE,
+ .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
.basename = "Mic 2 Phantom",
- .type = SND_EVO_TYPE_PHANTOM,
+ .type = SND_AUDIENT_TYPE_PHANTOM,
.channels = 1,
.hwchannels = 1,
.val_offs = 1,
- .flags = SND_EVO_FLAGS_CAPTURE,
+ .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{ 0 }, /* sentinel */
};
-static const struct evo_devinfo {
+static const struct audient_devinfo {
u32 usb_id;
- const struct snd_evo_ctrl *controls;
-} evo_devinfo[] = {
+ const struct snd_audient_ctrl *controls;
+} audient_devinfo[] = {
{
.usb_id = USB_ID(USB_AUDIENT_VID, USB_EVO4_PID),
- .controls = evo4_controls,
+ .controls = snd_audient_evo4_controls,
},
{ 0 } /* sentinel */
};
-static inline unsigned long snd_evo_kctl_priv(enum snd_evo_type type,
- u8 valoffs, u8 hwchannels)
+static inline unsigned long snd_audient_kctl_priv(enum snd_audient_type type,
+ u8 valoffs, u8 hwchannels)
{
unsigned long type_l = type & 0xFF;
unsigned long valoffs_l = valoffs & 0xFF;
@@ -218,22 +218,24 @@ static inline unsigned long snd_evo_kctl_priv(enum snd_evo_type type,
}
static __always_inline
-const struct snd_evo_ctrl_type *snd_evo_kctl_type(struct snd_kcontrol *kctl)
+const struct snd_audient_ctrl_type *snd_audient_kctl_type(struct snd_kcontrol
+ *kctl)
{
- return &snd_evo_ctypes[kctl->private_value & 0xFF];
+ return &snd_audient_ctypes[kctl->private_value & 0xFF];
}
-static __always_inline u16 snd_evo_kctl_valoffs(struct snd_kcontrol *kctl)
+static __always_inline u16 snd_audient_kctl_valoffs(struct snd_kcontrol *kctl)
{
return (kctl->private_value >> 8) & 0xFF;
}
-static __always_inline u16 snd_evo_kctl_hwchannels(struct snd_kcontrol *kctl)
+static __always_inline
+u16 snd_audient_kctl_hwchannels(struct snd_kcontrol *kctl)
{
return (kctl->private_value >> 16) & 0xFF;
}
-static inline u16 snd_evo_kctl_channels(struct snd_kcontrol *kctl)
+static inline u16 snd_audient_kctl_channels(struct snd_kcontrol *kctl)
{
struct usb_mixer_elem_info *info = kctl->private_data;
@@ -241,43 +243,45 @@ static inline u16 snd_evo_kctl_channels(struct snd_kcontrol *kctl)
}
-#define EVO_REQ_CUR (0x01)
-#define EVO_REQTYPE_SET (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)
-#define EVO_REQTYPE_GET (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)
+#define AUDIENT_REQTYPE_SET (USB_DIR_OUT | USB_TYPE_CLASS \
+ | USB_RECIP_INTERFACE)
+#define AUDIENT_REQTYPE_GET (USB_DIR_IN | USB_TYPE_CLASS \
+ | USB_RECIP_INTERFACE)
-static inline int snd_evo_get_cur(struct usb_mixer_interface *mixer,
- u16 value, u16 index,
- void *buf, size_t len)
+static inline int snd_audient_get_cur(struct usb_mixer_interface *mixer,
+ u16 value, u16 index,
+ void *buf, size_t len)
{
struct usb_device *const dev = mixer->chip->dev;
return snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
- EVO_REQ_CUR, EVO_REQTYPE_GET,
+ UAC2_CS_CUR, AUDIENT_REQTYPE_GET,
value, index, buf, len);
}
-static inline int snd_evo_set_cur(struct usb_mixer_interface *mixer,
- u16 value, u16 index,
- void *buf, size_t len)
+static inline int snd_audient_set_cur(struct usb_mixer_interface *mixer,
+ u16 value, u16 index,
+ void *buf, size_t len)
{
struct usb_device *const dev = mixer->chip->dev;
return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
- EVO_REQ_CUR, EVO_REQTYPE_SET,
+ UAC2_CS_CUR, AUDIENT_REQTYPE_SET,
value, index, buf, len);
}
-static int snd_ctl_evo_boolean_get(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_value *ctl_val)
+static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ctl_val)
{
struct usb_mixer_elem_list *const list = snd_kcontrol_chip(kctl);
struct usb_mixer_interface *const mixer = list->mixer;
- const unsigned int nchan = snd_evo_kctl_channels(kctl);
- const unsigned int hwchan = snd_evo_kctl_hwchannels(kctl);
- const struct snd_evo_ctrl_type *const ctype = snd_evo_kctl_type(kctl);
- const u16 value = ctype->wValue + snd_evo_kctl_valoffs(kctl);
+ const unsigned int nchan = snd_audient_kctl_channels(kctl);
+ const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
+ const struct snd_audient_ctrl_type *const ctype
+ = snd_audient_kctl_type(kctl);
+ const u16 value = ctype->wValue + snd_audient_kctl_valoffs(kctl);
const u16 index = ctype->wIndex;
int i;
@@ -290,8 +294,8 @@ static int snd_ctl_evo_boolean_get(struct snd_kcontrol *kctl,
u32 buf;
int ret;
- ret = snd_evo_get_cur(mixer, value + i, index,
- &buf, sizeof(buf));
+ ret = snd_audient_get_cur(mixer, value + i, index,
+ &buf, sizeof(buf));
if (ret < 0)
return ret;
@@ -309,15 +313,16 @@ static int snd_ctl_evo_boolean_get(struct snd_kcontrol *kctl,
return 0;
}
-static int snd_ctl_evo_boolean_set(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_value *ctl_val)
+static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ctl_val)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
struct usb_mixer_interface *mixer = list->mixer;
- const unsigned int hwchan = snd_evo_kctl_hwchannels(kctl);
- const struct snd_evo_ctrl_type *const ctype = snd_evo_kctl_type(kctl);
- const u16 value = ctype->wValue + snd_evo_kctl_valoffs(kctl);
+ const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
+ const struct snd_audient_ctrl_type *const ctype
+ = snd_audient_kctl_type(kctl);
+ const u16 value = ctype->wValue + snd_audient_kctl_valoffs(kctl);
const u16 index = ctype->wIndex;
int changed = 0;
@@ -335,7 +340,7 @@ static int snd_ctl_evo_boolean_set(struct snd_kcontrol *kctl,
if (ctype->invert)
buf ^= cpu_to_le32(0x1);
- ret = snd_evo_get_cur(mixer, value + i, index,
+ ret = snd_audient_get_cur(mixer, value + i, index,
&rbuf, sizeof(rbuf));
if (ret < 0)
return ret;
@@ -343,8 +348,8 @@ static int snd_ctl_evo_boolean_set(struct snd_kcontrol *kctl,
if (rbuf != buf) {
changed = 1;
- ret = snd_evo_set_cur(mixer, value + i, index,
- &buf, sizeof(buf));
+ ret = snd_audient_set_cur(mixer, value + i, index,
+ &buf, sizeof(buf));
if (ret < 0)
return ret;
}
@@ -353,16 +358,17 @@ static int snd_ctl_evo_boolean_set(struct snd_kcontrol *kctl,
return changed;
}
-static int snd_ctl_evo_integer_get(struct snd_kcontrol *kctl,
+static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ctl_val)
{
struct usb_mixer_elem_list *const list = snd_kcontrol_chip(kctl);
struct usb_mixer_interface *const mixer = list->mixer;
- const unsigned int nchan = snd_evo_kctl_channels(kctl);
- const unsigned int hwchan = snd_evo_kctl_hwchannels(kctl);
- const struct snd_evo_ctrl_type *const ctype = snd_evo_kctl_type(kctl);
- const u16 value = ctype->wValue + snd_evo_kctl_valoffs(kctl);
+ const unsigned int nchan = snd_audient_kctl_channels(kctl);
+ const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
+ const struct snd_audient_ctrl_type *const ctype
+ = snd_audient_kctl_type(kctl);
+ const u16 value = ctype->wValue + snd_audient_kctl_valoffs(kctl);
const u16 index = ctype->wIndex;
int i;
@@ -376,8 +382,8 @@ static int snd_ctl_evo_integer_get(struct snd_kcontrol *kctl,
s16 val;
int ret;
- ret = snd_evo_get_cur(mixer, value + i, index,
- &buf, sizeof(buf));
+ ret = snd_audient_get_cur(mixer, value + i, index,
+ &buf, sizeof(buf));
if (ret < 0)
return ret;
@@ -391,15 +397,16 @@ static int snd_ctl_evo_integer_get(struct snd_kcontrol *kctl,
return 0;
}
-static int snd_ctl_evo_integer_set(struct snd_kcontrol *kctl,
+static int snd_ctl_audient_integer_set(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ctl_val)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
struct usb_mixer_interface *mixer = list->mixer;
- const unsigned int hwchan = snd_evo_kctl_hwchannels(kctl);
- const struct snd_evo_ctrl_type *const ctype = snd_evo_kctl_type(kctl);
- const u16 value = ctype->wValue + snd_evo_kctl_valoffs(kctl);
+ const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
+ const struct snd_audient_ctrl_type *const ctype
+ = snd_audient_kctl_type(kctl);
+ const u16 value = ctype->wValue + snd_audient_kctl_valoffs(kctl);
const u16 index = ctype->wIndex;
int changed = 0;
@@ -414,16 +421,16 @@ static int snd_ctl_evo_integer_set(struct snd_kcontrol *kctl,
s16 buf = cpu_to_le16(ctl_val->value.integer.value[i]);
int ret;
- ret = snd_evo_get_cur(mixer, value + i, index,
- &rbuf, sizeof(rbuf));
+ ret = snd_audient_get_cur(mixer, value + i, index,
+ &rbuf, sizeof(rbuf));
if (ret < 0)
return ret;
if (rbuf != buf) {
changed = 1;
- ret = snd_evo_set_cur(mixer, value + i, index,
- &buf, sizeof(buf));
+ ret = snd_audient_set_cur(mixer, value + i, index,
+ &buf, sizeof(buf));
if (ret < 0)
return ret;
}
@@ -432,23 +439,25 @@ static int snd_ctl_evo_integer_set(struct snd_kcontrol *kctl,
return changed;
}
-static int snd_ctl_evo_info(struct snd_kcontrol *kctl,
+static int snd_ctl_audient_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *uinfo)
{
- const struct snd_evo_ctrl_type *const ctype = snd_evo_kctl_type(kctl);
+ const struct snd_audient_ctrl_type *const ctype
+ = snd_audient_kctl_type(kctl);
uinfo->type = ctype->type;
- uinfo->count = snd_evo_kctl_channels(kctl);
+ uinfo->count = snd_audient_kctl_channels(kctl);
uinfo->value.integer.min = ctype->min.integer;
uinfo->value.integer.max = ctype->max.integer;
return 0;
}
-static int snd_evo_add_ctrl(struct usb_mixer_interface *mixer,
- const struct snd_evo_ctrl *ctrl)
+static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
+ const struct snd_audient_ctrl *ctrl)
{
- const struct snd_evo_ctrl_type *ctype = &snd_evo_ctypes[ctrl->type];
+ const struct snd_audient_ctrl_type *ctype
+ = &snd_audient_ctypes[ctrl->type];
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
struct snd_kcontrol_new knew = { 0 };
struct snd_kcontrol *kctl;
@@ -456,9 +465,9 @@ static int snd_evo_add_ctrl(struct usb_mixer_interface *mixer,
knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
knew.name = name;
- knew.info = snd_ctl_evo_info;
- knew.private_value = snd_evo_kctl_priv(ctrl->type, ctrl->val_offs,
- ctrl->hwchannels);
+ knew.info = snd_ctl_audient_info;
+ knew.private_value = snd_audient_kctl_priv(ctrl->type, ctrl->val_offs,
+ ctrl->hwchannels);
knew.tlv.p = ctype->tlv;
knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE
| SNDRV_CTL_ELEM_ACCESS_VOLATILE
@@ -466,20 +475,20 @@ static int snd_evo_add_ctrl(struct usb_mixer_interface *mixer,
switch (ctype->type) {
case SNDRV_CTL_ELEM_TYPE_INTEGER:
- knew.get = snd_ctl_evo_integer_get;
- knew.put = snd_ctl_evo_integer_set;
+ knew.get = snd_ctl_audient_integer_get;
+ knew.put = snd_ctl_audient_integer_set;
break;
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
- knew.get = snd_ctl_evo_boolean_get;
- knew.put = snd_ctl_evo_boolean_set;
+ knew.get = snd_ctl_audient_boolean_get;
+ knew.put = snd_ctl_audient_boolean_set;
break;
default:
return -EINVAL;
}
snprintf(name, sizeof(name), "%s %s %s", ctrl->basename,
- ((ctrl->flags & SND_EVO_FLAGS_DIRECTION)
- == SND_EVO_FLAGS_CAPTURE) ? "Capture" : "Playback",
+ ((ctrl->flags & SND_AUDIENT_FLAGS_DIRECTION)
+ == SND_AUDIENT_FLAGS_CAPTURE) ? "Capture" : "Playback",
(ctype->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN)
? "Switch" : "Volume");
@@ -503,12 +512,12 @@ static int snd_evo_add_ctrl(struct usb_mixer_interface *mixer,
/* called from mixer_quirks.c */
-int snd_evo_controls_create(struct usb_mixer_interface *mixer)
+int snd_audient_controls_create(struct usb_mixer_interface *mixer)
{
- const struct evo_devinfo *info;
- const struct snd_evo_ctrl *ctrl;
+ const struct audient_devinfo *info;
+ const struct snd_audient_ctrl *ctrl;
- for (info = evo_devinfo; info->controls; info++)
+ for (info = audient_devinfo; info->controls; info++)
if (info->usb_id == mixer->chip->usb_id)
break;
@@ -516,7 +525,7 @@ int snd_evo_controls_create(struct usb_mixer_interface *mixer)
return -ENODEV;
for (ctrl = info->controls; ctrl->basename; ctrl++) {
- const int ret = snd_evo_add_ctrl(mixer, ctrl);
+ const int ret = snd_audient_add_ctrl(mixer, ctrl);
if (ret < 0)
return ret;
diff --git a/sound/usb/mixer_audient.h b/sound/usb/mixer_audient.h
new file mode 100644
index 000000000000..db4f31e02b5e
--- /dev/null
+++ b/sound/usb/mixer_audient.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __USB_MIXER_AUDIENT_H
+#define __USB_MIXER_AUDIENT_H
+
+#include "mixer.h"
+
+#define USB_AUDIENT_VID (0x2708)
+#define USB_EVO4_PID (0x0006)
+
+int snd_audient_controls_create(struct usb_mixer_interface *mixer);
+
+#endif /* __USB_MIXER_AUDIENT_H */
diff --git a/sound/usb/mixer_evo.h b/sound/usb/mixer_evo.h
deleted file mode 100644
index 7fe7e197185c..000000000000
--- a/sound/usb/mixer_evo.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __USB_MIXER_EVO_H
-#define __USB_MIXER_EVO_H
-
-#include "mixer.h"
-
-#define USB_AUDIENT_VID (0x2708)
-#define USB_EVO4_PID (0x0006)
-
-int snd_evo_controls_create(struct usb_mixer_interface *mixer);
-
-#endif /* __USB_MIXER_EVO_H */
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 01210cc3fae7..af53c5c20e65 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -38,7 +38,7 @@
#include "mixer_scarlett2.h"
#include "mixer_us16x08.h"
#include "mixer_s1810c.h"
-#include "mixer_evo.h"
+#include "mixer_audient.h"
#include "helper.h"
#include "fcp.h"
@@ -4541,7 +4541,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
break;
case USB_ID(USB_AUDIENT_VID, USB_EVO4_PID): /* Audient EVO 4 */
- err = snd_evo_controls_create(mixer);
+ err = snd_audient_controls_create(mixer);
break;
case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC PATCH 02/11] ALSA: mixer_audient: explicitly define full control names
2026-11-01 0:00 ` [RFC PATCH 00/11] " Christian Ruppert
2026-11-01 0:00 ` [RFC PATCH 01/11] ALSA: rename mixer_evo to mixer_audient Christian Ruppert
@ 2026-11-01 0:00 ` Christian Ruppert
2026-11-01 0:00 ` [RFC PATCH 03/11] ALSA: mixer_audient: clarify USB control message definition Christian Ruppert
` (9 subsequent siblings)
11 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-01 0:00 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Previously, control names were dynamically created depending if the
control was an input or an output, a volume or a switch. Get rid of this
logic by simply defining the full name in the control structure.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 50 +++++++++++----------------------------
1 file changed, 14 insertions(+), 36 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 8c1524c9b165..917b5bc53869 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -90,107 +90,92 @@ static const struct snd_audient_ctrl_type {
},
};
-#define SND_AUDIENT_FLAGS_DIRECTION (1L<<0)
-#define SND_AUDIENT_FLAGS_CAPTURE (1L<<0)
-#define SND_AUDIENT_FLAGS_PLAYBACK (0L<<0)
struct snd_audient_ctrl {
- char *basename;
+ char *name;
enum snd_audient_type type;
unsigned int channels;
unsigned int hwchannels;
unsigned int val_offs;
- unsigned long flags;
};
static const struct snd_audient_ctrl snd_audient_evo4_controls[] = {
{
- .basename = "Master",
+ .name = "Master Playback Switch",
.type = SND_AUDIENT_TYPE_MUTE_OUT,
.channels = 1,
.hwchannels = 1,
.val_offs = 0,
- .flags = SND_AUDIENT_FLAGS_PLAYBACK,
},
{
- .basename = "Mic",
+ .name = "Mic Playback Switch",
.type = SND_AUDIENT_TYPE_MUTE,
.channels = 4,
.hwchannels = 2,
.val_offs = 0,
- .flags = SND_AUDIENT_FLAGS_PLAYBACK,
},
{
- .basename = "Monitor",
+ .name = "Monitor Playback Volume",
.type = SND_AUDIENT_TYPE_MONITOR,
.channels = 1,
.hwchannels = 1,
.val_offs = 0,
- .flags = SND_AUDIENT_FLAGS_PLAYBACK,
},
{
- .basename = "Mic 1",
+ .name = "Mic 1 Capture Volume",
.type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 0,
- .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
- .basename = "Mic 2",
+ .name = "Mic 2 Capture Volume",
.type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 2,
- .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
- .basename = "Master Left",
+ .name = "Master Left Capture Volume",
.type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 4,
- .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
- .basename = "Master Right",
+ .name = "Master Right Capture Volume",
.type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 6,
- .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
- .basename = "Loopback Left",
+ .name = "Loopback Left Capture Volume",
.type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 8,
- .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
- .basename = "Loopback Right",
+ .name = "Loopback Right Capture Volume",
.type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 10,
- .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
- .basename = "Mic 1 Phantom",
+ .name = "Mic 1 Phantom Capture Switch",
.type = SND_AUDIENT_TYPE_PHANTOM,
.channels = 1,
.hwchannels = 1,
.val_offs = 0,
- .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
- .basename = "Mic 2 Phantom",
+ .name = "Mic 2 Phantom Capture Switch",
.type = SND_AUDIENT_TYPE_PHANTOM,
.channels = 1,
.hwchannels = 1,
.val_offs = 1,
- .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{ 0 }, /* sentinel */
};
@@ -458,13 +443,12 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
{
const struct snd_audient_ctrl_type *ctype
= &snd_audient_ctypes[ctrl->type];
- char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
struct snd_kcontrol_new knew = { 0 };
struct snd_kcontrol *kctl;
struct usb_mixer_elem_info *elem;
knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- knew.name = name;
+ knew.name = ctrl->name;
knew.info = snd_ctl_audient_info;
knew.private_value = snd_audient_kctl_priv(ctrl->type, ctrl->val_offs,
ctrl->hwchannels);
@@ -486,12 +470,6 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
return -EINVAL;
}
- snprintf(name, sizeof(name), "%s %s %s", ctrl->basename,
- ((ctrl->flags & SND_AUDIENT_FLAGS_DIRECTION)
- == SND_AUDIENT_FLAGS_CAPTURE) ? "Capture" : "Playback",
- (ctype->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN)
- ? "Switch" : "Volume");
-
elem = kzalloc_obj(*elem);
if (!elem)
return -ENOMEM;
@@ -524,7 +502,7 @@ int snd_audient_controls_create(struct usb_mixer_interface *mixer)
if (!info->controls)
return -ENODEV;
- for (ctrl = info->controls; ctrl->basename; ctrl++) {
+ for (ctrl = info->controls; ctrl->name; ctrl++) {
const int ret = snd_audient_add_ctrl(mixer, ctrl);
if (ret < 0)
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC PATCH 03/11] ALSA: mixer_audient: clarify USB control message definition
2026-11-01 0:00 ` [RFC PATCH 00/11] " Christian Ruppert
2026-11-01 0:00 ` [RFC PATCH 01/11] ALSA: rename mixer_evo to mixer_audient Christian Ruppert
2026-11-01 0:00 ` [RFC PATCH 02/11] ALSA: mixer_audient: explicitly define full control names Christian Ruppert
@ 2026-11-01 0:00 ` Christian Ruppert
2026-11-01 0:00 ` [RFC PATCH 04/11] ALSA: mixer_audient: Add length check to USB transfer Christian Ruppert
` (8 subsequent siblings)
11 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-01 0:00 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Previously, base values for wIndex and wValue were directly given in the
control type. Construct them from a unit ID and selector for clarity.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 76 +++++++++++++++++++++------------------
1 file changed, 41 insertions(+), 35 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 917b5bc53869..82591c32140f 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -45,15 +45,15 @@ static const struct snd_audient_ctrl_type {
int integer;
} max;
const unsigned int *tlv;
- u16 wValue;
- u16 wIndex;
+ u8 unit;
+ u8 sel;
} snd_audient_ctypes[SND_AUDIENT_TYPE_NCTYPES] = {
[SND_AUDIENT_TYPE_MONITOR] = {
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.min.integer = 0,
.max.integer = 127,
- .wValue = 0x0000,
- .wIndex = 0x3800,
+ .unit = 0x38,
+ .sel = 0x00,
/* no TLV here because we can't describe the cross-fader */
},
[SND_AUDIENT_TYPE_PHANTOM] = {
@@ -61,31 +61,31 @@ static const struct snd_audient_ctrl_type {
.invert = false,
.min.integer = 0,
.max.integer = 1,
- .wValue = 0x0000,
- .wIndex = 0x3A00,
+ .unit = 0x3A,
+ .sel = 0x00,
},
[SND_AUDIENT_TYPE_MUTE] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.invert = true,
.min.integer = 0,
.max.integer = 1,
- .wValue = 0x0200,
- .wIndex = 0x3A00,
+ .unit = 0x3A,
+ .sel = 0x02,
},
[SND_AUDIENT_TYPE_MUTE_OUT] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.invert = true,
.min.integer = 0,
.max.integer = 1,
- .wValue = 0x0100,
- .wIndex = 0x3B00,
+ .unit = 0x3B,
+ .sel = 0x01,
},
[SND_AUDIENT_TYPE_MIXER] = {
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.min.integer = -128 * 256,
.max.integer = 6 * 256,
- .wValue = 0x0100,
- .wIndex = 0x3C00,
+ .unit = 0x3C,
+ .sel = 0x01,
.tlv = snd_audient_tlv_mixer,
},
};
@@ -209,7 +209,7 @@ const struct snd_audient_ctrl_type *snd_audient_kctl_type(struct snd_kcontrol
return &snd_audient_ctypes[kctl->private_value & 0xFF];
}
-static __always_inline u16 snd_audient_kctl_valoffs(struct snd_kcontrol *kctl)
+static __always_inline u16 snd_audient_kctl_cidxoffs(struct snd_kcontrol *kctl)
{
return (kctl->private_value >> 8) & 0xFF;
}
@@ -233,11 +233,16 @@ static inline u16 snd_audient_kctl_channels(struct snd_kcontrol *kctl)
#define AUDIENT_REQTYPE_GET (USB_DIR_IN | USB_TYPE_CLASS \
| USB_RECIP_INTERFACE)
+#define U8_U8(HI, LO) ((((u16)(HI)) << 8) | ((LO) & 0xFF))
+
static inline int snd_audient_get_cur(struct usb_mixer_interface *mixer,
- u16 value, u16 index,
- void *buf, size_t len)
+ const u8 unit, const u8 sel,
+ const u8 cidx,
+ void *const buf, const size_t len)
{
struct usb_device *const dev = mixer->chip->dev;
+ const u16 value = U8_U8(sel, cidx);
+ const u16 index = U8_U8(unit, mixer->hostif->desc.bInterfaceNumber);
return snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
UAC2_CS_CUR, AUDIENT_REQTYPE_GET,
@@ -245,10 +250,13 @@ static inline int snd_audient_get_cur(struct usb_mixer_interface *mixer,
}
static inline int snd_audient_set_cur(struct usb_mixer_interface *mixer,
- u16 value, u16 index,
- void *buf, size_t len)
+ const u8 unit, const u8 sel,
+ const u8 cidx,
+ void *const buf, const size_t len)
{
struct usb_device *const dev = mixer->chip->dev;
+ const u16 value = U8_U8(sel, cidx);
+ const u16 index = U8_U8(unit, mixer->hostif->desc.bInterfaceNumber);
return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
UAC2_CS_CUR, AUDIENT_REQTYPE_SET,
@@ -266,8 +274,7 @@ static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
const struct snd_audient_ctrl_type *const ctype
= snd_audient_kctl_type(kctl);
- const u16 value = ctype->wValue + snd_audient_kctl_valoffs(kctl);
- const u16 index = ctype->wIndex;
+ const u8 base = snd_audient_kctl_cidxoffs(kctl);
int i;
@@ -279,8 +286,8 @@ static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
u32 buf;
int ret;
- ret = snd_audient_get_cur(mixer, value + i, index,
- &buf, sizeof(buf));
+ ret = snd_audient_get_cur(mixer, ctype->unit, ctype->sel,
+ base + i, &buf, sizeof(buf));
if (ret < 0)
return ret;
@@ -307,8 +314,7 @@ static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
const struct snd_audient_ctrl_type *const ctype
= snd_audient_kctl_type(kctl);
- const u16 value = ctype->wValue + snd_audient_kctl_valoffs(kctl);
- const u16 index = ctype->wIndex;
+ const u8 base = snd_audient_kctl_cidxoffs(kctl);
int changed = 0;
int i;
@@ -325,15 +331,16 @@ static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
if (ctype->invert)
buf ^= cpu_to_le32(0x1);
- ret = snd_audient_get_cur(mixer, value + i, index,
- &rbuf, sizeof(rbuf));
+ ret = snd_audient_get_cur(mixer, ctype->unit, ctype->sel,
+ base + i, &rbuf, sizeof(rbuf));
if (ret < 0)
return ret;
if (rbuf != buf) {
changed = 1;
- ret = snd_audient_set_cur(mixer, value + i, index,
+ ret = snd_audient_set_cur(mixer, ctype->unit,
+ ctype->sel, base + i,
&buf, sizeof(buf));
if (ret < 0)
return ret;
@@ -353,8 +360,7 @@ static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
const struct snd_audient_ctrl_type *const ctype
= snd_audient_kctl_type(kctl);
- const u16 value = ctype->wValue + snd_audient_kctl_valoffs(kctl);
- const u16 index = ctype->wIndex;
+ const u8 base = snd_audient_kctl_cidxoffs(kctl);
int i;
@@ -367,8 +373,8 @@ static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
s16 val;
int ret;
- ret = snd_audient_get_cur(mixer, value + i, index,
- &buf, sizeof(buf));
+ ret = snd_audient_get_cur(mixer, ctype->unit, ctype->sel,
+ base + i, &buf, sizeof(buf));
if (ret < 0)
return ret;
@@ -391,8 +397,7 @@ static int snd_ctl_audient_integer_set(struct snd_kcontrol *kctl,
const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
const struct snd_audient_ctrl_type *const ctype
= snd_audient_kctl_type(kctl);
- const u16 value = ctype->wValue + snd_audient_kctl_valoffs(kctl);
- const u16 index = ctype->wIndex;
+ const u8 base = snd_audient_kctl_cidxoffs(kctl);
int changed = 0;
int i;
@@ -406,15 +411,16 @@ static int snd_ctl_audient_integer_set(struct snd_kcontrol *kctl,
s16 buf = cpu_to_le16(ctl_val->value.integer.value[i]);
int ret;
- ret = snd_audient_get_cur(mixer, value + i, index,
- &rbuf, sizeof(rbuf));
+ ret = snd_audient_get_cur(mixer, ctype->unit, ctype->sel,
+ base + i, &rbuf, sizeof(rbuf));
if (ret < 0)
return ret;
if (rbuf != buf) {
changed = 1;
- ret = snd_audient_set_cur(mixer, value + i, index,
+ ret = snd_audient_set_cur(mixer, ctype->unit,
+ ctype->sel, base + i,
&buf, sizeof(buf));
if (ret < 0)
return ret;
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC PATCH 04/11] ALSA: mixer_audient: Add length check to USB transfer
2026-11-01 0:00 ` [RFC PATCH 00/11] " Christian Ruppert
` (2 preceding siblings ...)
2026-11-01 0:00 ` [RFC PATCH 03/11] ALSA: mixer_audient: clarify USB control message definition Christian Ruppert
@ 2026-11-01 0:00 ` Christian Ruppert
2026-11-01 0:00 ` [RFC PATCH 05/11] ALSA: mixer_audient: make USB control transfer size configurable Christian Ruppert
` (7 subsequent siblings)
11 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-01 0:00 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Check USB control transfers not only for errors but also for expected
length and return -EIO in case of error.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 82591c32140f..24d2f7a0bdc2 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -244,9 +244,14 @@ static inline int snd_audient_get_cur(struct usb_mixer_interface *mixer,
const u16 value = U8_U8(sel, cidx);
const u16 index = U8_U8(unit, mixer->hostif->desc.bInterfaceNumber);
- return snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
- UAC2_CS_CUR, AUDIENT_REQTYPE_GET,
- value, index, buf, len);
+ const int ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
+ UAC2_CS_CUR, AUDIENT_REQTYPE_GET,
+ value, index, buf, len);
+
+ if ((ret >= 0) && (ret != len))
+ return -EIO;
+
+ return ret;
}
static inline int snd_audient_set_cur(struct usb_mixer_interface *mixer,
@@ -258,9 +263,14 @@ static inline int snd_audient_set_cur(struct usb_mixer_interface *mixer,
const u16 value = U8_U8(sel, cidx);
const u16 index = U8_U8(unit, mixer->hostif->desc.bInterfaceNumber);
- return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
- UAC2_CS_CUR, AUDIENT_REQTYPE_SET,
- value, index, buf, len);
+ const int ret = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
+ UAC2_CS_CUR, AUDIENT_REQTYPE_SET,
+ value, index, buf, len);
+
+ if ((ret >= 0) && (ret != len))
+ return -EIO;
+
+ return ret;
}
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC PATCH 05/11] ALSA: mixer_audient: make USB control transfer size configurable
2026-11-01 0:00 ` [RFC PATCH 00/11] " Christian Ruppert
` (3 preceding siblings ...)
2026-11-01 0:00 ` [RFC PATCH 04/11] ALSA: mixer_audient: Add length check to USB transfer Christian Ruppert
@ 2026-11-01 0:00 ` Christian Ruppert
2026-11-01 0:00 ` [RFC PATCH 06/11] ALSA: mixer_audient: Allow arbitrary boolean control values Christian Ruppert
` (6 subsequent siblings)
11 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-01 0:00 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Previously, the USB transfer size was hardcoded in the datatype get/set
functions. Make it configurable in the ctype structure in preparation of
new control types for the id14.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 130 ++++++++++++++++++++++++--------------
1 file changed, 83 insertions(+), 47 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 24d2f7a0bdc2..1438cc192d34 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -35,8 +35,12 @@ enum snd_audient_type {
/* Mixer matrix: -128dB (mute) to +6dB */
static const DECLARE_TLV_DB_MINMAX_MUTE(snd_audient_tlv_mixer, -12800, 600);
+#define SND_AUDIENT_CTYPE_FLAGS_SIGNED (1L << 0)
+
static const struct snd_audient_ctrl_type {
snd_ctl_elem_type_t type;
+ unsigned long flags;
+ size_t len;
bool invert;
union {
int integer;
@@ -50,6 +54,7 @@ static const struct snd_audient_ctrl_type {
} snd_audient_ctypes[SND_AUDIENT_TYPE_NCTYPES] = {
[SND_AUDIENT_TYPE_MONITOR] = {
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .len = 2,
.min.integer = 0,
.max.integer = 127,
.unit = 0x38,
@@ -59,6 +64,7 @@ static const struct snd_audient_ctrl_type {
[SND_AUDIENT_TYPE_PHANTOM] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.invert = false,
+ .len = 4,
.min.integer = 0,
.max.integer = 1,
.unit = 0x3A,
@@ -67,6 +73,7 @@ static const struct snd_audient_ctrl_type {
[SND_AUDIENT_TYPE_MUTE] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.invert = true,
+ .len = 4,
.min.integer = 0,
.max.integer = 1,
.unit = 0x3A,
@@ -75,6 +82,7 @@ static const struct snd_audient_ctrl_type {
[SND_AUDIENT_TYPE_MUTE_OUT] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.invert = true,
+ .len = 4,
.min.integer = 0,
.max.integer = 1,
.unit = 0x3B,
@@ -82,6 +90,8 @@ static const struct snd_audient_ctrl_type {
},
[SND_AUDIENT_TYPE_MIXER] = {
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .flags = SND_AUDIENT_CTYPE_FLAGS_SIGNED,
+ .len = 2,
.min.integer = -128 * 256,
.max.integer = 6 * 256,
.unit = 0x3C,
@@ -236,44 +246,92 @@ static inline u16 snd_audient_kctl_channels(struct snd_kcontrol *kctl)
#define U8_U8(HI, LO) ((((u16)(HI)) << 8) | ((LO) & 0xFF))
static inline int snd_audient_get_cur(struct usb_mixer_interface *mixer,
- const u8 unit, const u8 sel,
- const u8 cidx,
- void *const buf, const size_t len)
+ const struct snd_audient_ctrl_type *const ctype,
+ const u8 cidx, void *const buf)
{
struct usb_device *const dev = mixer->chip->dev;
- const u16 value = U8_U8(sel, cidx);
- const u16 index = U8_U8(unit, mixer->hostif->desc.bInterfaceNumber);
+ const u16 value = U8_U8(ctype->sel, cidx);
+ const u16 index = U8_U8(ctype->unit,
+ mixer->hostif->desc.bInterfaceNumber);
const int ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
UAC2_CS_CUR, AUDIENT_REQTYPE_GET,
- value, index, buf, len);
+ value, index, buf, ctype->len);
- if ((ret >= 0) && (ret != len))
+ if ((ret >= 0) && (ret != ctype->len))
return -EIO;
return ret;
}
static inline int snd_audient_set_cur(struct usb_mixer_interface *mixer,
- const u8 unit, const u8 sel,
- const u8 cidx,
- void *const buf, const size_t len)
+ const struct snd_audient_ctrl_type *const ctype,
+ const u8 cidx, void *const buf)
{
struct usb_device *const dev = mixer->chip->dev;
- const u16 value = U8_U8(sel, cidx);
- const u16 index = U8_U8(unit, mixer->hostif->desc.bInterfaceNumber);
+ const u16 value = U8_U8(ctype->sel, cidx);
+ const u16 index = U8_U8(ctype->unit,
+ mixer->hostif->desc.bInterfaceNumber);
const int ret = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
UAC2_CS_CUR, AUDIENT_REQTYPE_SET,
- value, index, buf, len);
+ value, index, buf, ctype->len);
- if ((ret >= 0) && (ret != len))
+ if ((ret >= 0) && (ret != ctype->len))
return -EIO;
return ret;
}
+static int snd_audient_raw_get_one(struct usb_mixer_interface *const mixer,
+ const struct snd_audient_ctrl_type *const ctype,
+ const u8 cidx, u32 *const value)
+{
+ int ret;
+ s32 buf = 0;
+
+ ret = snd_audient_get_cur(mixer, ctype, cidx, &buf);
+ if (ret < 0)
+ return ret;
+
+ buf = le32_to_cpu(buf);
+
+ if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_SIGNED) {
+ const unsigned int sh = 32 - 8 * ctype->len;
+
+ buf = (buf << sh) >> sh;
+ }
+
+ *value = buf;
+
+ return ret;
+}
+
+static int snd_audient_raw_set_one(struct usb_mixer_interface *const mixer,
+ const struct snd_audient_ctrl_type *const ctype,
+ const u8 cidx, const u32 value)
+{
+ int ret;
+ u32 buf;
+
+ ret = snd_audient_raw_get_one(mixer, ctype, cidx, &buf);
+ if (ret < 0)
+ return ret;
+
+ if (buf != value) {
+ buf = cpu_to_le32(value);
+ ret = snd_audient_set_cur(mixer, ctype, cidx, &buf);
+ if (ret < 0)
+ return ret;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ctl_val)
{
@@ -296,12 +354,11 @@ static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
u32 buf;
int ret;
- ret = snd_audient_get_cur(mixer, ctype->unit, ctype->sel,
- base + i, &buf, sizeof(buf));
+ ret = snd_audient_raw_get_one(mixer, ctype, base + i, &buf);
if (ret < 0)
return ret;
- ctl_val->value.integer.value[i] = le32_to_cpu(buf) ? 1 : 0;
+ ctl_val->value.integer.value[i] = buf ? 1 : 0;
if (ctype->invert)
ctl_val->value.integer.value[i] ^= 0x1;
@@ -334,27 +391,18 @@ static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
return pm.err;
for (i = 0; i < hwchan; i++) {
- u32 rbuf;
- u32 buf = cpu_to_le32(ctl_val->value.integer.value[i] ? 1 : 0);
+ u32 buf = ctl_val->value.integer.value[i] ? 1 : 0;
int ret;
if (ctype->invert)
- buf ^= cpu_to_le32(0x1);
+ buf ^= 0x1;
- ret = snd_audient_get_cur(mixer, ctype->unit, ctype->sel,
- base + i, &rbuf, sizeof(rbuf));
+ ret = snd_audient_raw_set_one(mixer, ctype, base + i, buf);
if (ret < 0)
return ret;
- if (rbuf != buf) {
+ if (ret)
changed = 1;
-
- ret = snd_audient_set_cur(mixer, ctype->unit,
- ctype->sel, base + i,
- &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- }
}
return changed;
@@ -379,16 +427,13 @@ static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
return pm.err;
for (i = 0; i < hwchan; i++) {
- u16 buf;
- s16 val;
+ s32 val;
int ret;
- ret = snd_audient_get_cur(mixer, ctype->unit, ctype->sel,
- base + i, &buf, sizeof(buf));
+ ret = snd_audient_raw_get_one(mixer, ctype, base + i, &val);
if (ret < 0)
return ret;
- val = le16_to_cpu(buf);
ctl_val->value.integer.value[i] = val;
}
@@ -417,24 +462,15 @@ static int snd_ctl_audient_integer_set(struct snd_kcontrol *kctl,
return pm.err;
for (i = 0; i < hwchan; i++) {
- s16 rbuf;
- s16 buf = cpu_to_le16(ctl_val->value.integer.value[i]);
+ s32 val = ctl_val->value.integer.value[i];
int ret;
- ret = snd_audient_get_cur(mixer, ctype->unit, ctype->sel,
- base + i, &rbuf, sizeof(rbuf));
+ ret = snd_audient_raw_set_one(mixer, ctype, base + i, val);
if (ret < 0)
return ret;
- if (rbuf != buf) {
+ if (ret)
changed = 1;
-
- ret = snd_audient_set_cur(mixer, ctype->unit,
- ctype->sel, base + i,
- &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- }
}
return changed;
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC PATCH 06/11] ALSA: mixer_audient: Allow arbitrary boolean control values
2026-11-01 0:00 ` [RFC PATCH 00/11] " Christian Ruppert
` (4 preceding siblings ...)
2026-11-01 0:00 ` [RFC PATCH 05/11] ALSA: mixer_audient: make USB control transfer size configurable Christian Ruppert
@ 2026-11-01 0:00 ` Christian Ruppert
2026-11-01 0:00 ` [RFC PATCH 07/11] ALSA: mixer_audient: Add integer range checks Christian Ruppert
` (5 subsequent siblings)
11 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-01 0:00 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
In preparation of id14 support, allow arbitrary values for true and false
of boolean switches. This also rids us of the tedious invert flag.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 43 ++++++++++++++++++---------------------
1 file changed, 20 insertions(+), 23 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 1438cc192d34..6352aee0fb5e 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -41,13 +41,12 @@ static const struct snd_audient_ctrl_type {
snd_ctl_elem_type_t type;
unsigned long flags;
size_t len;
- bool invert;
union {
int integer;
- } min;
+ } min; /* for booleans holds physical int value for false */
union {
int integer;
- } max;
+ } max; /* for booleans holds physical int value for true */
const unsigned int *tlv;
u8 unit;
u8 sel;
@@ -63,7 +62,6 @@ static const struct snd_audient_ctrl_type {
},
[SND_AUDIENT_TYPE_PHANTOM] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
- .invert = false,
.len = 4,
.min.integer = 0,
.max.integer = 1,
@@ -72,19 +70,17 @@ static const struct snd_audient_ctrl_type {
},
[SND_AUDIENT_TYPE_MUTE] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
- .invert = true,
.len = 4,
- .min.integer = 0,
- .max.integer = 1,
+ .min.integer = 1,
+ .max.integer = 0,
.unit = 0x3A,
.sel = 0x02,
},
[SND_AUDIENT_TYPE_MUTE_OUT] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
- .invert = true,
.len = 4,
- .min.integer = 0,
- .max.integer = 1,
+ .min.integer = 1,
+ .max.integer = 0,
.unit = 0x3B,
.sel = 0x01,
},
@@ -358,16 +354,12 @@ static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
if (ret < 0)
return ret;
- ctl_val->value.integer.value[i] = buf ? 1 : 0;
-
- if (ctype->invert)
- ctl_val->value.integer.value[i] ^= 0x1;
+ ctl_val->value.integer.value[i] = (buf == ctype->min.integer)
+ ? false : true;
}
for (; i < nchan; i++)
- ctl_val->value.integer.value[i] = ctype->invert
- ? ctype->max.integer
- : ctype->min.integer;
+ ctl_val->value.integer.value[i] = 0;
return 0;
}
@@ -391,12 +383,10 @@ static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
return pm.err;
for (i = 0; i < hwchan; i++) {
- u32 buf = ctl_val->value.integer.value[i] ? 1 : 0;
+ u32 buf = ctl_val->value.integer.value[i] ? ctype->max.integer
+ : ctype->min.integer;
int ret;
- if (ctype->invert)
- buf ^= 0x1;
-
ret = snd_audient_raw_set_one(mixer, ctype, base + i, buf);
if (ret < 0)
return ret;
@@ -484,8 +474,15 @@ static int snd_ctl_audient_info(struct snd_kcontrol *kctl,
uinfo->type = ctype->type;
uinfo->count = snd_audient_kctl_channels(kctl);
- uinfo->value.integer.min = ctype->min.integer;
- uinfo->value.integer.max = ctype->max.integer;
+
+ if (ctype->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN) {
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ } else {
+ uinfo->value.integer.min = ctype->min.integer;
+ uinfo->value.integer.max = ctype->max.integer;
+ }
+
return 0;
}
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC PATCH 07/11] ALSA: mixer_audient: Add integer range checks
2026-11-01 0:00 ` [RFC PATCH 00/11] " Christian Ruppert
` (5 preceding siblings ...)
2026-11-01 0:00 ` [RFC PATCH 06/11] ALSA: mixer_audient: Allow arbitrary boolean control values Christian Ruppert
@ 2026-11-01 0:00 ` Christian Ruppert
2026-11-01 0:00 ` [RFC PATCH 08/11] ALSA: mixer_audient: Add control initialization at probe Christian Ruppert
` (4 subsequent siblings)
11 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-01 0:00 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Add range checks to integer parameters to improve error management:
- Reject out-of-range write attempts with -EINVAL
- Prune out-of-range values returned by the device to the range limits
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 6352aee0fb5e..2f1b332859c9 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -42,10 +42,10 @@ static const struct snd_audient_ctrl_type {
unsigned long flags;
size_t len;
union {
- int integer;
+ long integer;
} min; /* for booleans holds physical int value for false */
union {
- int integer;
+ long integer;
} max; /* for booleans holds physical int value for true */
const unsigned int *tlv;
u8 unit;
@@ -424,6 +424,12 @@ static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
if (ret < 0)
return ret;
+ if (val < ctype->min.integer)
+ val = ctype->min.integer;
+
+ if (val > ctype->max.integer)
+ val = ctype->max.integer;
+
ctl_val->value.integer.value[i] = val;
}
@@ -447,6 +453,13 @@ static int snd_ctl_audient_integer_set(struct snd_kcontrol *kctl,
int changed = 0;
int i;
+ for (i = 0; i < hwchan; i++) {
+ long val = ctl_val->value.integer.value[i];
+
+ if ((val < ctype->min.integer) || (val > ctype->max.integer))
+ return -EINVAL;
+ }
+
CLASS(snd_usb_lock, pm)(mixer->chip);
if (pm.err < 0)
return pm.err;
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC PATCH 08/11] ALSA: mixer_audient: Add control initialization at probe
2026-11-01 0:00 ` [RFC PATCH 00/11] " Christian Ruppert
` (6 preceding siblings ...)
2026-11-01 0:00 ` [RFC PATCH 07/11] ALSA: mixer_audient: Add integer range checks Christian Ruppert
@ 2026-11-01 0:00 ` Christian Ruppert
2026-11-01 0:00 ` [RFC PATCH 09/11] ALSA: mixer_audient: Add readback-free parameter set Christian Ruppert
` (3 subsequent siblings)
11 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-01 0:00 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Add the possibility to initialize certain control types to known values at
probe time. This is particularly useful for controls which initially read
wrong values and for controls which cannot be read at all.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 2f1b332859c9..803487ced997 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -36,6 +36,7 @@ enum snd_audient_type {
static const DECLARE_TLV_DB_MINMAX_MUTE(snd_audient_tlv_mixer, -12800, 600);
#define SND_AUDIENT_CTYPE_FLAGS_SIGNED (1L << 0)
+#define SND_AUDIENT_CTYPE_FLAGS_INIT_HW (1L << 1)
static const struct snd_audient_ctrl_type {
snd_ctl_elem_type_t type;
@@ -47,6 +48,7 @@ static const struct snd_audient_ctrl_type {
union {
long integer;
} max; /* for booleans holds physical int value for true */
+ u32 init;
const unsigned int *tlv;
u8 unit;
u8 sel;
@@ -532,6 +534,18 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
return -EINVAL;
}
+ if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_INIT_HW) {
+ int i;
+
+ for (i = 0; i < ctrl->hwchannels; i++) {
+ int ret = snd_audient_raw_set_one(mixer, ctype,
+ ctrl->val_offs + i,
+ ctype->init);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
elem = kzalloc_obj(*elem);
if (!elem)
return -ENOMEM;
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC PATCH 09/11] ALSA: mixer_audient: Add readback-free parameter set
2026-11-01 0:00 ` [RFC PATCH 00/11] " Christian Ruppert
` (7 preceding siblings ...)
2026-11-01 0:00 ` [RFC PATCH 08/11] ALSA: mixer_audient: Add control initialization at probe Christian Ruppert
@ 2026-11-01 0:00 ` Christian Ruppert
2026-11-01 0:00 ` [RFC PATCH 10/11] ALSA: mixer_audient: add control value caching Christian Ruppert
` (2 subsequent siblings)
11 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-01 0:00 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Add a flag to systematically force parameter writing without prior readback
to certain control types. In preparation of the id14 driver.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 803487ced997..7cb8245a64d0 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -37,6 +37,7 @@ static const DECLARE_TLV_DB_MINMAX_MUTE(snd_audient_tlv_mixer, -12800, 600);
#define SND_AUDIENT_CTYPE_FLAGS_SIGNED (1L << 0)
#define SND_AUDIENT_CTYPE_FLAGS_INIT_HW (1L << 1)
+#define SND_AUDIENT_CTYPE_FLAGS_FORCE_SET (1L << 2)
static const struct snd_audient_ctrl_type {
snd_ctl_elem_type_t type;
@@ -313,9 +314,13 @@ static int snd_audient_raw_set_one(struct usb_mixer_interface *const mixer,
int ret;
u32 buf;
- ret = snd_audient_raw_get_one(mixer, ctype, cidx, &buf);
- if (ret < 0)
- return ret;
+ if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET) {
+ buf = !value; /* just make it different */
+ } else {
+ ret = snd_audient_raw_get_one(mixer, ctype, cidx, &buf);
+ if (ret < 0)
+ return ret;
+ }
if (buf != value) {
buf = cpu_to_le32(value);
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC PATCH 10/11] ALSA: mixer_audient: add control value caching
2026-11-01 0:00 ` [RFC PATCH 00/11] " Christian Ruppert
` (8 preceding siblings ...)
2026-11-01 0:00 ` [RFC PATCH 09/11] ALSA: mixer_audient: Add readback-free parameter set Christian Ruppert
@ 2026-11-01 0:00 ` Christian Ruppert
2026-11-01 0:00 ` [RFC PATCH 11/11] ALSA: mixer_audient: Create matrix programmatically Christian Ruppert
2026-11-01 17:35 ` [RFC PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor controls Will Porter
11 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-01 0:00 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
In preparation of id14 support, add control value caching.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 112 +++++++++++++++++++++++++++-----------
1 file changed, 81 insertions(+), 31 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 7cb8245a64d0..24b594fb1bc7 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -38,6 +38,7 @@ static const DECLARE_TLV_DB_MINMAX_MUTE(snd_audient_tlv_mixer, -12800, 600);
#define SND_AUDIENT_CTYPE_FLAGS_SIGNED (1L << 0)
#define SND_AUDIENT_CTYPE_FLAGS_INIT_HW (1L << 1)
#define SND_AUDIENT_CTYPE_FLAGS_FORCE_SET (1L << 2)
+#define SND_AUDIENT_CTYPE_FLAGS_CACHE (1L << 3)
static const struct snd_audient_ctrl_type {
snd_ctl_elem_type_t type;
@@ -49,7 +50,7 @@ static const struct snd_audient_ctrl_type {
union {
long integer;
} max; /* for booleans holds physical int value for true */
- u32 init;
+ u32 init; /* hardware and cache are initialized to this value */
const unsigned int *tlv;
u8 unit;
u8 sel;
@@ -89,7 +90,8 @@ static const struct snd_audient_ctrl_type {
},
[SND_AUDIENT_TYPE_MIXER] = {
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
- .flags = SND_AUDIENT_CTYPE_FLAGS_SIGNED,
+ .flags = SND_AUDIENT_CTYPE_FLAGS_SIGNED
+ | SND_AUDIENT_CTYPE_FLAGS_CACHE,
.len = 2,
.min.integer = -128 * 256,
.max.integer = 6 * 256,
@@ -285,11 +287,18 @@ static inline int snd_audient_set_cur(struct usb_mixer_interface *mixer,
static int snd_audient_raw_get_one(struct usb_mixer_interface *const mixer,
const struct snd_audient_ctrl_type *const ctype,
+ const u32 *const cache,
const u8 cidx, u32 *const value)
{
int ret;
s32 buf = 0;
+ if (cache) {
+ *value = cache[cidx];
+
+ return ctype->len;
+ }
+
ret = snd_audient_get_cur(mixer, ctype, cidx, &buf);
if (ret < 0)
return ret;
@@ -309,15 +318,16 @@ static int snd_audient_raw_get_one(struct usb_mixer_interface *const mixer,
static int snd_audient_raw_set_one(struct usb_mixer_interface *const mixer,
const struct snd_audient_ctrl_type *const ctype,
- const u8 cidx, const u32 value)
+ u32 *const cache, const u8 cidx, const u32 value,
+ bool force)
{
int ret;
u32 buf;
- if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET) {
+ if ((ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET) || force) {
buf = !value; /* just make it different */
} else {
- ret = snd_audient_raw_get_one(mixer, ctype, cidx, &buf);
+ ret = snd_audient_raw_get_one(mixer, ctype, cache, cidx, &buf);
if (ret < 0)
return ret;
}
@@ -328,6 +338,9 @@ static int snd_audient_raw_set_one(struct usb_mixer_interface *const mixer,
if (ret < 0)
return ret;
+ if (cache)
+ cache[cidx] = value;
+
return 1;
}
@@ -338,8 +351,8 @@ static int snd_audient_raw_set_one(struct usb_mixer_interface *const mixer,
static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ctl_val)
{
- struct usb_mixer_elem_list *const list = snd_kcontrol_chip(kctl);
- struct usb_mixer_interface *const mixer = list->mixer;
+ struct usb_mixer_elem_info *const elem = snd_kcontrol_chip(kctl);
+ struct usb_mixer_interface *const mixer = elem->head.mixer;
const unsigned int nchan = snd_audient_kctl_channels(kctl);
const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
@@ -357,7 +370,8 @@ static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
u32 buf;
int ret;
- ret = snd_audient_raw_get_one(mixer, ctype, base + i, &buf);
+ ret = snd_audient_raw_get_one(mixer, ctype, elem->private_data,
+ base + i, &buf);
if (ret < 0)
return ret;
@@ -374,8 +388,8 @@ static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ctl_val)
{
- struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
- struct usb_mixer_interface *mixer = list->mixer;
+ struct usb_mixer_elem_info *const elem = snd_kcontrol_chip(kctl);
+ struct usb_mixer_interface *const mixer = elem->head.mixer;
const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
const struct snd_audient_ctrl_type *const ctype
@@ -394,7 +408,8 @@ static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
: ctype->min.integer;
int ret;
- ret = snd_audient_raw_set_one(mixer, ctype, base + i, buf);
+ ret = snd_audient_raw_set_one(mixer, ctype, elem->private_data,
+ base + i, buf, false);
if (ret < 0)
return ret;
@@ -408,8 +423,8 @@ static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ctl_val)
{
- struct usb_mixer_elem_list *const list = snd_kcontrol_chip(kctl);
- struct usb_mixer_interface *const mixer = list->mixer;
+ struct usb_mixer_elem_info *const elem = snd_kcontrol_chip(kctl);
+ struct usb_mixer_interface *const mixer = elem->head.mixer;
const unsigned int nchan = snd_audient_kctl_channels(kctl);
const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
@@ -427,7 +442,8 @@ static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
s32 val;
int ret;
- ret = snd_audient_raw_get_one(mixer, ctype, base + i, &val);
+ ret = snd_audient_raw_get_one(mixer, ctype, elem->private_data,
+ base + i, &val);
if (ret < 0)
return ret;
@@ -449,8 +465,8 @@ static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
static int snd_ctl_audient_integer_set(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ctl_val)
{
- struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
- struct usb_mixer_interface *mixer = list->mixer;
+ struct usb_mixer_elem_info *const elem = snd_kcontrol_chip(kctl);
+ struct usb_mixer_interface *mixer = elem->head.mixer;
const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
const struct snd_audient_ctrl_type *const ctype
@@ -475,7 +491,8 @@ static int snd_ctl_audient_integer_set(struct snd_kcontrol *kctl,
s32 val = ctl_val->value.integer.value[i];
int ret;
- ret = snd_audient_raw_set_one(mixer, ctype, base + i, val);
+ ret = snd_audient_raw_set_one(mixer, ctype, elem->private_data,
+ base + i, val, false);
if (ret < 0)
return ret;
@@ -516,6 +533,8 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
struct snd_kcontrol *kctl;
struct usb_mixer_elem_info *elem;
+ int ret;
+
knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
knew.name = ctrl->name;
knew.info = snd_ctl_audient_info;
@@ -523,7 +542,9 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
ctrl->hwchannels);
knew.tlv.p = ctype->tlv;
knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE
- | SNDRV_CTL_ELEM_ACCESS_VOLATILE
+ | ((ctype->flags & SND_AUDIENT_CTYPE_FLAGS_CACHE)
+ ? 0
+ : SNDRV_CTL_ELEM_ACCESS_VOLATILE)
| (ctype->tlv ? SNDRV_CTL_ELEM_ACCESS_TLV_READ : 0);
switch (ctype->type) {
@@ -539,34 +560,63 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
return -EINVAL;
}
+ elem = kzalloc(sizeof(*elem)
+ + ((ctype->flags & SND_AUDIENT_CTYPE_FLAGS_CACHE)
+ ? (sizeof(u32) * ctrl->hwchannels)
+ : 0),
+ GFP_KERNEL);
+ if (!elem)
+ return -ENOMEM;
+
+ elem->head.mixer = mixer;
+ elem->channels = ctrl->channels;
+
+ if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_CACHE)
+ elem->private_data = elem + 1;
+
if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_INIT_HW) {
int i;
for (i = 0; i < ctrl->hwchannels; i++) {
- int ret = snd_audient_raw_set_one(mixer, ctype,
- ctrl->val_offs + i,
- ctype->init);
+ ret = snd_audient_raw_set_one(mixer, ctype,
+ elem->private_data,
+ ctrl->val_offs + i,
+ ctype->init, true);
if (ret < 0)
- return ret;
+ goto free_elem;
}
- }
-
- elem = kzalloc_obj(*elem);
- if (!elem)
- return -ENOMEM;
+ } else if (elem->private_data) {
+ u32 *const cache = elem->private_data;
+ int i;
- elem->head.mixer = mixer;
- elem->channels = ctrl->channels;
+ if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET)
+ for (i = 0; i < ctrl->hwchannels; i++)
+ cache[i] = ctype->init;
+ else
+ for (i = 0; i < ctrl->hwchannels; i++) {
+ ret = snd_audient_raw_get_one(
+ mixer, ctype, NULL,
+ ctrl->val_offs + i,
+ cache + i);
+ if (ret < 0)
+ goto free_elem;
+ }
+ }
kctl = snd_ctl_new1(&knew, elem);
if (!kctl) {
- kfree(elem);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto free_elem;
}
kctl->private_free = snd_usb_mixer_elem_free;
return snd_usb_mixer_add_control(&elem->head, kctl);
+
+free_elem:
+ kfree(elem);
+
+ return ret;
}
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC PATCH 11/11] ALSA: mixer_audient: Create matrix programmatically
2026-11-01 0:00 ` [RFC PATCH 00/11] " Christian Ruppert
` (9 preceding siblings ...)
2026-11-01 0:00 ` [RFC PATCH 10/11] ALSA: mixer_audient: add control value caching Christian Ruppert
@ 2026-11-01 0:00 ` Christian Ruppert
2026-11-01 17:35 ` [RFC PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor controls Will Porter
11 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-01 0:00 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Most Audient devices seem to have a similar mixer matrix with many
identical channels. The only difference between devices seems to be
the matrix' dimension. Create it programmatically in a loop rather
than from the controls table.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 75 +++++++++++++++++----------------------
1 file changed, 32 insertions(+), 43 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 24b594fb1bc7..30f2f20b1dbb 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -103,7 +103,7 @@ static const struct snd_audient_ctrl_type {
struct snd_audient_ctrl {
- char *name;
+ const char *const name;
enum snd_audient_type type;
unsigned int channels;
unsigned int hwchannels;
@@ -132,48 +132,6 @@ static const struct snd_audient_ctrl snd_audient_evo4_controls[] = {
.hwchannels = 1,
.val_offs = 0,
},
- {
- .name = "Mic 1 Capture Volume",
- .type = SND_AUDIENT_TYPE_MIXER,
- .channels = 2,
- .hwchannels = 2,
- .val_offs = 0,
- },
- {
- .name = "Mic 2 Capture Volume",
- .type = SND_AUDIENT_TYPE_MIXER,
- .channels = 2,
- .hwchannels = 2,
- .val_offs = 2,
- },
- {
- .name = "Master Left Capture Volume",
- .type = SND_AUDIENT_TYPE_MIXER,
- .channels = 2,
- .hwchannels = 2,
- .val_offs = 4,
- },
- {
- .name = "Master Right Capture Volume",
- .type = SND_AUDIENT_TYPE_MIXER,
- .channels = 2,
- .hwchannels = 2,
- .val_offs = 6,
- },
- {
- .name = "Loopback Left Capture Volume",
- .type = SND_AUDIENT_TYPE_MIXER,
- .channels = 2,
- .hwchannels = 2,
- .val_offs = 8,
- },
- {
- .name = "Loopback Right Capture Volume",
- .type = SND_AUDIENT_TYPE_MIXER,
- .channels = 2,
- .hwchannels = 2,
- .val_offs = 10,
- },
{
.name = "Mic 1 Phantom Capture Switch",
.type = SND_AUDIENT_TYPE_PHANTOM,
@@ -191,13 +149,27 @@ static const struct snd_audient_ctrl snd_audient_evo4_controls[] = {
{ 0 }, /* sentinel */
};
+static const char *const snd_audient_evo4_matrix[] = {
+ "Mic 1 Capture Volume",
+ "Mic 2 Capture Volume",
+ "Master Left Capture Volume",
+ "Master Right Capture Volume",
+ "Loopback Left Capture Volume",
+ "Loopback Right Capture Volume",
+ NULL
+};
+
static const struct audient_devinfo {
u32 usb_id;
const struct snd_audient_ctrl *controls;
+ const char *const *const matrix_names;
+ const unsigned int matrix_channels;
} audient_devinfo[] = {
{
.usb_id = USB_ID(USB_AUDIENT_VID, USB_EVO4_PID),
.controls = snd_audient_evo4_controls,
+ .matrix_names = snd_audient_evo4_matrix,
+ .matrix_channels = 2,
},
{ 0 } /* sentinel */
};
@@ -626,6 +598,8 @@ int snd_audient_controls_create(struct usb_mixer_interface *mixer)
const struct audient_devinfo *info;
const struct snd_audient_ctrl *ctrl;
+ int i;
+
for (info = audient_devinfo; info->controls; info++)
if (info->usb_id == mixer->chip->usb_id)
break;
@@ -640,5 +614,20 @@ int snd_audient_controls_create(struct usb_mixer_interface *mixer)
return ret;
}
+ for (i = 0; info->matrix_names[i]; i++) {
+ const struct snd_audient_ctrl c = {
+ .name = info->matrix_names[i],
+ .type = SND_AUDIENT_TYPE_MIXER,
+ .channels = info->matrix_channels,
+ .hwchannels = info->matrix_channels,
+ .val_offs = i * info->matrix_channels,
+ };
+
+ const int ret = snd_audient_add_ctrl(mixer, &c);
+
+ if (ret < 0)
+ return ret;
+ }
+
return 0;
}
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [RFC PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor controls
2026-11-01 0:00 ` [RFC PATCH 00/11] " Christian Ruppert
` (10 preceding siblings ...)
2026-11-01 0:00 ` [RFC PATCH 11/11] ALSA: mixer_audient: Create matrix programmatically Christian Ruppert
@ 2026-11-01 17:35 ` Will Porter
2026-11-02 0:32 ` [RFC V2 PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor Christian Ruppert
11 siblings, 1 reply; 37+ messages in thread
From: Will Porter @ 2026-11-01 17:35 UTC (permalink / raw)
To: Christian Ruppert
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel, Neil Andrews
Hi Christian,
Thanks for the RFC, and for splitting it into small patches. It made
the review easy, and the cover letter answers my three questions.
I compiled mixer_audient.c after each patch with W=1, out of tree
against a 7.2 kernel's headers: no warnings. checkpatch --strict
gives only CHECK-level style notes. I can't test on an EVO, so the
points below come from reading the code.
1. Patch 10, cache index
The cache has hwchannels entries, and the probe fills it by element
index (cache[i]). But get, put and the INIT_HW write index it by device
channel:
*value = cache[cidx]; /* cidx = val_offs + i */
cache[cidx] = value;
So a nonzero val_offs can access past the allocation. On the EVO4,
"Mic 2 Capture Volume" (val_offs 2) uses cache[2] and cache[3] of a
2-entry array, and "Loopback Right Capture Volume" uses cache[10] and
cache[11]. Those rows would also show other memory, not the value read
at probe. On the iD14 the last matrix element would use cache[54] and
cache[55].
Would indexing relative to the element fix it? The diff at the end of
this mail passes the cache entry of channel i instead of the array, so
that raw_get_one() and raw_set_one() never see cidx for the cache.
2. Probe-time USB errors now fail the whole card
With patch 10 the EVO matrix is read at probe, and with INIT_HW a
control is written at probe. If one of those transfers fails,
snd_audient_controls_create() returns the error, snd_usb_create_mixer()
fails, and the card does not register. As far as I can see,
QUIRK_FLAG_IGNORE_CTL_ERROR does not suppress errors returned by
snd_usb_mixer_apply_create_quirk(). Before this series the quirk sent no
USB traffic at probe, so this is new.
Could we keep the card registering after a failed probe transfer, while
keeping the cache entry invalid until a transfer succeeds?
To save a round, I wrote it out; the diff is at the end of this mail, on
top of your series. Please change or drop anything that doesn't fit your
plans.
It gives each cache entry a valid flag, set when a transfer confirms the
value (a read, or a write that succeeded). A failed probe transfer logs
a warning and keeps the control with an invalid entry. For a cached
write-only control, get still succeeds after a failed probe write, so
that failure alone does not stop alsactl store: boolean and integer
controls return their minimum, and my enum type reports "Unknown". A
readable control retries the GET and returns an error if that retry
fails. Since a get can now fill the cache, the callbacks take
mixer->lock. Control allocation and registration errors still fail the
probe. It contains the point 1 fix as well, since both change the same
cache code.
On the iD14 I tested it with a test-only control that the device
refuses (polarity on Digi 3). The card registered, the log showed one
warning for that control, and get returned off. A put returned -EPIPE;
a subsequent get still returned off.
3. Smaller points
- Patch 05: would sign_extend32(buf, 8 * ctype->len - 1) work in
place of the shift pair?
- Patch 05: the buffers mix u32/s32 with cpu_to_le32()/le32_to_cpu().
Would __le32 buffers keep sparse quiet?
- Patch 05: the transfer buffer is 4 bytes on the stack, but nothing
checks the table's len. Could snd_audient_add_ctrl() return -EINVAL
unless 1 <= len <= 4?
- Patch 09: with FORCE_SET, a put of the cached value still writes
(good) but also reports a change, so each alsactl restore sends a
change event for every such control. Could it write but return 0
when the cache already holds the value?
- Patch 11: a device with no matrix list would dereference NULL in the
loop. Would a check on info->matrix_names be worth adding?
- Patch 01: FYI, the file header still says "Audient EVO driver for
ALSA".
4. iD14 tables on top
I built the iD14 tables on your series, with the cache fix applied.
On an iD14 MkI they passed control read and write checks, the range
checks and a loopback test of the matrix crosspoints. The crosspoints
also kept their values across a module reload and were read back into
the cache.
On top of your patches I needed three generic additions:
- a step size for integer types (res): the iD14 level controls use
s16 in 1/256 dB; I expose them in whole-dB steps, matching the
volume knob. The control counts steps from min, as the generic USB
mixer does with the RANGE resolution;
- an enumerated type with a value list, plus one extra last item that
a get shows for a device value outside the list;
- a per-device create hook, for the FU 12 control that does not fit
the tables (in the same patch as that control).
The matrix uses your loop as it is: with 2 channels per element and the
names in the order Mic 1 Main, Mic 1 Cue, Mic 2 Main, ..., element n
starts at CN 2 * n, which is input * 4 + output on the iD14. Does that
fit what you had in mind?
5. Next steps
Would you prefer to incorporate these changes in an RFC v2, or shall I
fold them into the relevant patches when I carry your series, with
notes in the commit messages?
Thanks,
Will
-- >8 --
From: Will Porter <mrwillporter@gmail.com>
Subject: [RFC PATCH] ALSA: mixer_audient: Fix cache indexing and probe error handling
The cache has hwchannels entries, but get, put and the INIT_HW write
indexed it with the device channel (val_offs + i). Pass the entry of
element channel i instead.
A failed probe transfer made snd_audient_controls_create() fail, so
the card did not register. Give each cache entry a valid flag, set
when a transfer confirms the value: a read, or a write that
succeeded. A failed probe transfer logs a warning and leaves the entry
invalid. A readable control then reads the device on its next get,
and returns an error if that read fails; a write-only (FORCE_SET)
control shows its minimum until a put succeeds. CACHE | FORCE_SET
without INIT_HW keeps the earlier behaviour: the init value is
assumed, without a transfer.
A get can now fill the cache, and the control core runs gets in
parallel, so the callbacks take mixer->lock around the transfers and
the cache.
Control allocation and registration errors still fail the probe.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Will Porter <mrwillporter@gmail.com>
---
Proposed changes to patches 08 and 10.
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 30f2f20..ef8fcdf 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -210,6 +210,26 @@ static inline u16 snd_audient_kctl_channels(struct snd_kcontrol *kctl)
return info->channels;
}
+/*
+ * A cached value is valid once a transfer confirmed it: a read, or a write
+ * that succeeded. A probe transfer that fails leaves it invalid. CACHE |
+ * FORCE_SET without INIT_HW keeps the earlier behaviour: the init value is
+ * assumed, without a transfer.
+ */
+struct snd_audient_cache_entry {
+ u32 value;
+ bool valid;
+};
+
+/* Cache entry of element channel i, or NULL for an uncached control. */
+static inline struct snd_audient_cache_entry *
+snd_audient_cache_slot(struct usb_mixer_elem_info *elem, unsigned int i)
+{
+ struct snd_audient_cache_entry *const cache = elem->private_data;
+
+ return cache ? cache + i : NULL;
+}
+
#define AUDIENT_REQTYPE_SET (USB_DIR_OUT | USB_TYPE_CLASS \
| USB_RECIP_INTERFACE)
@@ -259,18 +279,25 @@ static inline int snd_audient_set_cur(struct usb_mixer_interface *mixer,
static int snd_audient_raw_get_one(struct usb_mixer_interface *const mixer,
const struct snd_audient_ctrl_type *const ctype,
- const u32 *const cache,
+ struct snd_audient_cache_entry *const slot,
const u8 cidx, u32 *const value)
{
int ret;
s32 buf = 0;
- if (cache) {
- *value = cache[cidx];
+ if (slot && slot->valid) {
+ *value = slot->value;
return ctype->len;
}
+ /*
+ * Cached FORCE_SET controls are treated as write-only: an invalid
+ * cache entry does not trigger a GET.
+ */
+ if (slot && (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET))
+ return -ENODATA;
+
ret = snd_audient_get_cur(mixer, ctype, cidx, &buf);
if (ret < 0)
return ret;
@@ -285,12 +312,18 @@ static int snd_audient_raw_get_one(struct usb_mixer_interface *const mixer,
*value = buf;
+ if (slot) {
+ slot->value = buf;
+ slot->valid = true;
+ }
+
return ret;
}
static int snd_audient_raw_set_one(struct usb_mixer_interface *const mixer,
const struct snd_audient_ctrl_type *const ctype,
- u32 *const cache, const u8 cidx, const u32 value,
+ struct snd_audient_cache_entry *const slot,
+ const u8 cidx, const u32 value,
bool force)
{
int ret;
@@ -299,7 +332,7 @@ static int snd_audient_raw_set_one(struct usb_mixer_interface *const mixer,
if ((ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET) || force) {
buf = !value; /* just make it different */
} else {
- ret = snd_audient_raw_get_one(mixer, ctype, cache, cidx, &buf);
+ ret = snd_audient_raw_get_one(mixer, ctype, slot, cidx, &buf);
if (ret < 0)
return ret;
}
@@ -310,8 +343,10 @@ static int snd_audient_raw_set_one(struct usb_mixer_interface *const mixer,
if (ret < 0)
return ret;
- if (cache)
- cache[cidx] = value;
+ if (slot) {
+ slot->value = value;
+ slot->valid = true;
+ }
return 1;
}
@@ -337,14 +372,18 @@ static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
CLASS(snd_usb_lock, pm)(mixer->chip);
if (pm.err < 0)
return pm.err;
+ /* serializes the transfers and the cache, also between gets */
+ guard(mutex)(&mixer->lock);
for (i = 0; i < hwchan; i++) {
u32 buf;
int ret;
- ret = snd_audient_raw_get_one(mixer, ctype, elem->private_data,
+ ret = snd_audient_raw_get_one(mixer, ctype, snd_audient_cache_slot(elem, i),
base + i, &buf);
- if (ret < 0)
+ if (ret == -ENODATA) /* unknown write-only value: off */
+ buf = ctype->min.integer;
+ else if (ret < 0)
return ret;
ctl_val->value.integer.value[i] = (buf == ctype->min.integer)
@@ -374,13 +413,15 @@ static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
CLASS(snd_usb_lock, pm)(mixer->chip);
if (pm.err < 0)
return pm.err;
+ /* serializes the transfers and the cache, also between gets */
+ guard(mutex)(&mixer->lock);
for (i = 0; i < hwchan; i++) {
u32 buf = ctl_val->value.integer.value[i] ? ctype->max.integer
: ctype->min.integer;
int ret;
- ret = snd_audient_raw_set_one(mixer, ctype, elem->private_data,
+ ret = snd_audient_raw_set_one(mixer, ctype, snd_audient_cache_slot(elem, i),
base + i, buf, false);
if (ret < 0)
return ret;
@@ -409,14 +450,18 @@ static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
CLASS(snd_usb_lock, pm)(mixer->chip);
if (pm.err < 0)
return pm.err;
+ /* serializes the transfers and the cache, also between gets */
+ guard(mutex)(&mixer->lock);
for (i = 0; i < hwchan; i++) {
s32 val;
int ret;
- ret = snd_audient_raw_get_one(mixer, ctype, elem->private_data,
+ ret = snd_audient_raw_get_one(mixer, ctype, snd_audient_cache_slot(elem, i),
base + i, &val);
- if (ret < 0)
+ if (ret == -ENODATA) /* unknown write-only value: minimum */
+ val = ctype->min.integer;
+ else if (ret < 0)
return ret;
if (val < ctype->min.integer)
@@ -458,12 +503,14 @@ static int snd_ctl_audient_integer_set(struct snd_kcontrol *kctl,
CLASS(snd_usb_lock, pm)(mixer->chip);
if (pm.err < 0)
return pm.err;
+ /* serializes the transfers and the cache, also between gets */
+ guard(mutex)(&mixer->lock);
for (i = 0; i < hwchan; i++) {
s32 val = ctl_val->value.integer.value[i];
int ret;
- ret = snd_audient_raw_set_one(mixer, ctype, elem->private_data,
+ ret = snd_audient_raw_set_one(mixer, ctype, snd_audient_cache_slot(elem, i),
base + i, val, false);
if (ret < 0)
return ret;
@@ -534,7 +581,8 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
elem = kzalloc(sizeof(*elem)
+ ((ctype->flags & SND_AUDIENT_CTYPE_FLAGS_CACHE)
- ? (sizeof(u32) * ctrl->hwchannels)
+ ? (sizeof(struct snd_audient_cache_entry)
+ * ctrl->hwchannels)
: 0),
GFP_KERNEL);
if (!elem)
@@ -546,33 +594,44 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_CACHE)
elem->private_data = elem + 1;
+ /*
+ * A failed probe transfer must not fail the card: warn and keep the
+ * control. Its cache entry stays invalid, so a readable control reads
+ * the device again on its next get, and a write-only control shows an
+ * unknown value until a put succeeds.
+ */
if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_INIT_HW) {
int i;
for (i = 0; i < ctrl->hwchannels; i++) {
ret = snd_audient_raw_set_one(mixer, ctype,
- elem->private_data,
+ snd_audient_cache_slot(elem, i),
ctrl->val_offs + i,
ctype->init, true);
if (ret < 0)
- goto free_elem;
+ usb_audio_warn(mixer->chip,
+ "%s: cannot set channel %d (%d)\n",
+ ctrl->name, i, ret);
}
} else if (elem->private_data) {
- u32 *const cache = elem->private_data;
+ struct snd_audient_cache_entry *const cache = elem->private_data;
int i;
- if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET)
- for (i = 0; i < ctrl->hwchannels; i++)
- cache[i] = ctype->init;
- else
- for (i = 0; i < ctrl->hwchannels; i++) {
- ret = snd_audient_raw_get_one(
- mixer, ctype, NULL,
- ctrl->val_offs + i,
- cache + i);
- if (ret < 0)
- goto free_elem;
+ for (i = 0; i < ctrl->hwchannels; i++) {
+ if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET) {
+ cache[i].value = ctype->init;
+ cache[i].valid = true;
+ continue;
}
+
+ ret = snd_audient_raw_get_one(mixer, ctype, cache + i,
+ ctrl->val_offs + i,
+ &cache[i].value);
+ if (ret < 0)
+ usb_audio_warn(mixer->chip,
+ "%s: cannot read channel %d (%d)\n",
+ ctrl->name, i, ret);
+ }
}
kctl = snd_ctl_new1(&knew, elem);
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC V2 PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor
2026-11-01 17:35 ` [RFC PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor controls Will Porter
@ 2026-11-02 0:32 ` Christian Ruppert
2026-11-02 0:32 ` [RFC V2 PATCH 01/11] ALSA: rename mixer_evo to mixer_audient Christian Ruppert
` (10 more replies)
0 siblings, 11 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-02 0:32 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Hi Will,
On Sun, Nov 01, 2026 at 11:35:57AM -0600, Will Porter wrote:
> [...]
>
> 1. Patch 10, cache index
>
> The cache has hwchannels entries, and the probe fills it by element
> index (cache[i]). But get, put and the INIT_HW write index it by device
> channel:
> [...]
Good catch and thanks for the fix, applied in RFC V2.
> 2. Probe-time USB errors now fail the whole card
> [...]
> Could we keep the card registering after a failed probe transfer, while
> keeping the cache entry invalid until a transfer succeeds?
>
> To save a round, I wrote it out; the diff is at the end of this mail, on
> top of your series. Please change or drop anything that doesn't fit your
> plans.
> [...]
Fine with me, I suppose the question if individual reads should be allowed
to fail during driver initialization is more philosophical in this case.
I don't see any harm. Patch applied in RFC V2
> 3. Smaller points
>
> - Patch 05: would sign_extend32(buf, 8 * ctype->len - 1) work in
> place of the shift pair?
> - Patch 05: the buffers mix u32/s32 with cpu_to_le32()/le32_to_cpu().
> Would __le32 buffers keep sparse quiet?
OK, these make the code a bit cleaner, added to RFC V2.
> - Patch 05: the transfer buffer is 4 bytes on the stack, but nothing
> checks the table's len. Could snd_audient_add_ctrl() return -EINVAL
> unless 1 <= len <= 4?
I don't think this is necessary as it should be rather obvious to everyone
touching the driver that the u32 argument can't carry more than 4 bytes.
Added a comment to the parameter definition.
> - Patch 09: with FORCE_SET, a put of the cached value still writes
> (good) but also reports a change, so each alsactl restore sends a
> change event for every such control. Could it write but return 0
> when the cache already holds the value?
Makes sense. Added to RFC V2 (in patch 10 where the cache is added). Please
double-check if the logic reflects what you had in mind.
> - Patch 11: a device with no matrix list would dereference NULL in the
> loop. Would a check on info->matrix_names be worth adding?
Makes sense although this could also be held off until somebody actually
adds such a device. Added to RFC V2.
> - Patch 01: FYI, the file header still says "Audient EVO driver for
> ALSA".
Fixed in RFC V2.
> 4. iD14 tables on top
>
> I built the iD14 tables on your series, with the cache fix applied.
> On an iD14 MkI they passed control read and write checks, the range
> checks and a loopback test of the matrix crosspoints. The crosspoints
> also kept their values across a module reload and were read back into
> the cache.
>
> On top of your patches I needed three generic additions:
>
> - a step size for integer types (res): the iD14 level controls use
> s16 in 1/256 dB; I expose them in whole-dB steps, matching the
> volume knob. The control counts steps from min, as the generic USB
> mixer does with the RANGE resolution;
> - an enumerated type with a value list, plus one extra last item that
> a get shows for a device value outside the list;
> - a per-device create hook, for the FU 12 control that does not fit
> the tables (in the same patch as that control).
>
> The matrix uses your loop as it is: with 2 channels per element and the
> names in the order Mic 1 Main, Mic 1 Cue, Mic 2 Main, ..., element n
> starts at CN 2 * n, which is input * 4 + output on the iD14. Does that
> fit what you had in mind?
Yep, sounds good.
> 5. Next steps
>
> Would you prefer to incorporate these changes in an RFC v2, or shall I
> fold them into the relevant patches when I carry your series, with
> notes in the commit messages?
I created RFC V2 with your patch and the fixes you suggested. This will
make things easier to review when we submit the everything, compared to
fixes on top of a long series of patches.
Based on the same commit as before and retested on 7.3.1.
Christian
Christian Ruppert (11):
ALSA: rename mixer_evo to mixer_audient
ALSA: mixer_audient: explicitly define full control names
ALSA: mixer_audient: clarify USB control message definition
ALSA: mixer_audient: Add length check to USB transfer
ALSA: mixer_audient: make USB control transfer size configurable
ALSA: mixer_audient: Allow arbitrary boolean control values
ALSA: mixer_audient: Add integer range checks
ALSA: mixer_audient: Add control initialization at probe
ALSA: mixer_audient: Add readback-free parameter set
ALSA: mixer_audient: add control value caching
ALSA: mixer_audient: Create matrix programmatically
MAINTAINERS | 7 +-
sound/usb/Makefile | 2 +-
sound/usb/mixer_audient.c | 702 ++++++++++++++++++++++++++++++++++++++
sound/usb/mixer_audient.h | 12 +
sound/usb/mixer_evo.c | 526 ----------------------------
sound/usb/mixer_evo.h | 12 -
sound/usb/mixer_quirks.c | 4 +-
7 files changed, 721 insertions(+), 544 deletions(-)
create mode 100644 sound/usb/mixer_audient.c
create mode 100644 sound/usb/mixer_audient.h
delete mode 100644 sound/usb/mixer_evo.c
delete mode 100644 sound/usb/mixer_evo.h
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC V2 PATCH 01/11] ALSA: rename mixer_evo to mixer_audient
2026-11-02 0:32 ` [RFC V2 PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor Christian Ruppert
@ 2026-11-02 0:32 ` Christian Ruppert
2026-11-02 0:32 ` [RFC V2 PATCH 02/11] ALSA: mixer_audient: explicitly define full control names Christian Ruppert
` (9 subsequent siblings)
10 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-02 0:32 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Rename mixer_evo files and functions to *_audient in preparation of a more
generic driver for audient devices.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
MAINTAINERS | 7 +-
sound/usb/Makefile | 2 +-
sound/usb/{mixer_evo.c => mixer_audient.c} | 249 +++++++++++----------
sound/usb/mixer_audient.h | 12 +
sound/usb/mixer_evo.h | 12 -
sound/usb/mixer_quirks.c | 4 +-
6 files changed, 148 insertions(+), 138 deletions(-)
rename sound/usb/{mixer_evo.c => mixer_audient.c} (54%)
create mode 100644 sound/usb/mixer_audient.h
delete mode 100644 sound/usb/mixer_evo.h
diff --git a/MAINTAINERS b/MAINTAINERS
index fee9d090b280..a1139ae3404d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4354,12 +4354,13 @@ F: drivers/net/ieee802154/at86rf230.h
F: drivers/net/ieee802154/atusb.c
F: drivers/net/ieee802154/atusb.h
-AUDIENT EVO MIXER DRIVER
+AUDIENT MIXER DRIVER
M: Christian Ruppert <arc@gmx.li>
+M: Will Porter <mrwillporter@gmail.com>
L: linux-sound@vger.kernel.org
S: Maintained
-F: sound/usb/mixer_evo.c
-F: sound/usb/mixer_evo.h
+F: sound/usb/mixer_audient.c
+F: sound/usb/mixer_audient.h
AUDIT SUBSYSTEM
M: Paul Moore <paul@paul-moore.com>
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index 2e842dcc73de..44f4e4c820fb 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -11,7 +11,7 @@ snd-usb-audio-y := card.o \
helper.o \
implicit.o \
mixer.o \
- mixer_evo.o \
+ mixer_audient.o \
mixer_quirks.o \
mixer_scarlett.o \
mixer_scarlett2.o \
diff --git a/sound/usb/mixer_evo.c b/sound/usb/mixer_audient.c
similarity index 54%
rename from sound/usb/mixer_evo.c
rename to sound/usb/mixer_audient.c
index c1e0332232f2..e2700b08cf80 100644
--- a/sound/usb/mixer_evo.c
+++ b/sound/usb/mixer_audient.c
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Audient EVO driver for ALSA
+ * Audient driver for ALSA
* Copyright (C) 2026 Christian Ruppert <arc@gmx.li>
*
- * Based on the work of Ivan Hazucha for audient-evo-py.
+ * EVO4 support is based on the work of Ivan Hazucha for audient-evo-py.
* https://github.com/vanzaho/audient-evo-py/
*/
@@ -20,22 +20,22 @@
#include "mixer.h"
#include "mixer_quirks.h"
#include "helper.h"
-#include "mixer_evo.h"
+#include "mixer_audient.h"
-enum snd_evo_type {
- SND_EVO_TYPE_MONITOR = 0,
- SND_EVO_TYPE_PHANTOM,
- SND_EVO_TYPE_MUTE,
- SND_EVO_TYPE_MUTE_OUT,
- SND_EVO_TYPE_MIXER,
- SND_EVO_TYPE_NCTYPES
+enum snd_audient_type {
+ SND_AUDIENT_TYPE_MONITOR = 0,
+ SND_AUDIENT_TYPE_PHANTOM,
+ SND_AUDIENT_TYPE_MUTE,
+ SND_AUDIENT_TYPE_MUTE_OUT,
+ SND_AUDIENT_TYPE_MIXER,
+ SND_AUDIENT_TYPE_NCTYPES
};
/* Mixer matrix: -128dB (mute) to +6dB */
-static const DECLARE_TLV_DB_MINMAX_MUTE(snd_evo_tlv_mixer, -12800, 600);
+static const DECLARE_TLV_DB_MINMAX_MUTE(snd_audient_tlv_mixer, -12800, 600);
-static const struct snd_evo_ctrl_type {
+static const struct snd_audient_ctrl_type {
snd_ctl_elem_type_t type;
bool invert;
union {
@@ -47,8 +47,8 @@ static const struct snd_evo_ctrl_type {
const unsigned int *tlv;
u16 wValue;
u16 wIndex;
-} snd_evo_ctypes[SND_EVO_TYPE_NCTYPES] = {
- [SND_EVO_TYPE_MONITOR] = {
+} snd_audient_ctypes[SND_AUDIENT_TYPE_NCTYPES] = {
+ [SND_AUDIENT_TYPE_MONITOR] = {
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.min.integer = 0,
.max.integer = 127,
@@ -56,7 +56,7 @@ static const struct snd_evo_ctrl_type {
.wIndex = 0x3800,
/* no TLV here because we can't describe the cross-fader */
},
- [SND_EVO_TYPE_PHANTOM] = {
+ [SND_AUDIENT_TYPE_PHANTOM] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.invert = false,
.min.integer = 0,
@@ -64,7 +64,7 @@ static const struct snd_evo_ctrl_type {
.wValue = 0x0000,
.wIndex = 0x3A00,
},
- [SND_EVO_TYPE_MUTE] = {
+ [SND_AUDIENT_TYPE_MUTE] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.invert = true,
.min.integer = 0,
@@ -72,7 +72,7 @@ static const struct snd_evo_ctrl_type {
.wValue = 0x0200,
.wIndex = 0x3A00,
},
- [SND_EVO_TYPE_MUTE_OUT] = {
+ [SND_AUDIENT_TYPE_MUTE_OUT] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.invert = true,
.min.integer = 0,
@@ -80,135 +80,135 @@ static const struct snd_evo_ctrl_type {
.wValue = 0x0100,
.wIndex = 0x3B00,
},
- [SND_EVO_TYPE_MIXER] = {
+ [SND_AUDIENT_TYPE_MIXER] = {
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.min.integer = -128 * 256,
.max.integer = 6 * 256,
.wValue = 0x0100,
.wIndex = 0x3C00,
- .tlv = snd_evo_tlv_mixer,
+ .tlv = snd_audient_tlv_mixer,
},
};
-#define SND_EVO_FLAGS_DIRECTION (1L<<0)
-#define SND_EVO_FLAGS_CAPTURE (1L<<0)
-#define SND_EVO_FLAGS_PLAYBACK (0L<<0)
+#define SND_AUDIENT_FLAGS_DIRECTION (1L<<0)
+#define SND_AUDIENT_FLAGS_CAPTURE (1L<<0)
+#define SND_AUDIENT_FLAGS_PLAYBACK (0L<<0)
-struct snd_evo_ctrl {
+struct snd_audient_ctrl {
char *basename;
- enum snd_evo_type type;
+ enum snd_audient_type type;
unsigned int channels;
unsigned int hwchannels;
unsigned int val_offs;
unsigned long flags;
};
-static const struct snd_evo_ctrl evo4_controls[] = {
+static const struct snd_audient_ctrl snd_audient_evo4_controls[] = {
{
.basename = "Master",
- .type = SND_EVO_TYPE_MUTE_OUT,
+ .type = SND_AUDIENT_TYPE_MUTE_OUT,
.channels = 1,
.hwchannels = 1,
.val_offs = 0,
- .flags = SND_EVO_FLAGS_PLAYBACK,
+ .flags = SND_AUDIENT_FLAGS_PLAYBACK,
},
{
.basename = "Mic",
- .type = SND_EVO_TYPE_MUTE,
+ .type = SND_AUDIENT_TYPE_MUTE,
.channels = 4,
.hwchannels = 2,
.val_offs = 0,
- .flags = SND_EVO_FLAGS_PLAYBACK,
+ .flags = SND_AUDIENT_FLAGS_PLAYBACK,
},
{
.basename = "Monitor",
- .type = SND_EVO_TYPE_MONITOR,
+ .type = SND_AUDIENT_TYPE_MONITOR,
.channels = 1,
.hwchannels = 1,
.val_offs = 0,
- .flags = SND_EVO_FLAGS_PLAYBACK,
+ .flags = SND_AUDIENT_FLAGS_PLAYBACK,
},
{
.basename = "Mic 1",
- .type = SND_EVO_TYPE_MIXER,
+ .type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 0,
- .flags = SND_EVO_FLAGS_CAPTURE,
+ .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
.basename = "Mic 2",
- .type = SND_EVO_TYPE_MIXER,
+ .type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 2,
- .flags = SND_EVO_FLAGS_CAPTURE,
+ .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
.basename = "Master Left",
- .type = SND_EVO_TYPE_MIXER,
+ .type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 4,
- .flags = SND_EVO_FLAGS_CAPTURE,
+ .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
.basename = "Master Right",
- .type = SND_EVO_TYPE_MIXER,
+ .type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 6,
- .flags = SND_EVO_FLAGS_CAPTURE,
+ .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
.basename = "Loopback Left",
- .type = SND_EVO_TYPE_MIXER,
+ .type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 8,
- .flags = SND_EVO_FLAGS_CAPTURE,
+ .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
.basename = "Loopback Right",
- .type = SND_EVO_TYPE_MIXER,
+ .type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 10,
- .flags = SND_EVO_FLAGS_CAPTURE,
+ .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
.basename = "Mic 1 Phantom",
- .type = SND_EVO_TYPE_PHANTOM,
+ .type = SND_AUDIENT_TYPE_PHANTOM,
.channels = 1,
.hwchannels = 1,
.val_offs = 0,
- .flags = SND_EVO_FLAGS_CAPTURE,
+ .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
.basename = "Mic 2 Phantom",
- .type = SND_EVO_TYPE_PHANTOM,
+ .type = SND_AUDIENT_TYPE_PHANTOM,
.channels = 1,
.hwchannels = 1,
.val_offs = 1,
- .flags = SND_EVO_FLAGS_CAPTURE,
+ .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{ 0 }, /* sentinel */
};
-static const struct evo_devinfo {
+static const struct audient_devinfo {
u32 usb_id;
- const struct snd_evo_ctrl *controls;
-} evo_devinfo[] = {
+ const struct snd_audient_ctrl *controls;
+} audient_devinfo[] = {
{
.usb_id = USB_ID(USB_AUDIENT_VID, USB_EVO4_PID),
- .controls = evo4_controls,
+ .controls = snd_audient_evo4_controls,
},
{ 0 } /* sentinel */
};
-static inline unsigned long snd_evo_kctl_priv(enum snd_evo_type type,
- u8 valoffs, u8 hwchannels)
+static inline unsigned long snd_audient_kctl_priv(enum snd_audient_type type,
+ u8 valoffs, u8 hwchannels)
{
unsigned long type_l = type & 0xFF;
unsigned long valoffs_l = valoffs & 0xFF;
@@ -218,22 +218,24 @@ static inline unsigned long snd_evo_kctl_priv(enum snd_evo_type type,
}
static __always_inline
-const struct snd_evo_ctrl_type *snd_evo_kctl_type(struct snd_kcontrol *kctl)
+const struct snd_audient_ctrl_type *snd_audient_kctl_type(struct snd_kcontrol
+ *kctl)
{
- return &snd_evo_ctypes[kctl->private_value & 0xFF];
+ return &snd_audient_ctypes[kctl->private_value & 0xFF];
}
-static __always_inline u16 snd_evo_kctl_valoffs(struct snd_kcontrol *kctl)
+static __always_inline u16 snd_audient_kctl_valoffs(struct snd_kcontrol *kctl)
{
return (kctl->private_value >> 8) & 0xFF;
}
-static __always_inline u16 snd_evo_kctl_hwchannels(struct snd_kcontrol *kctl)
+static __always_inline
+u16 snd_audient_kctl_hwchannels(struct snd_kcontrol *kctl)
{
return (kctl->private_value >> 16) & 0xFF;
}
-static inline u16 snd_evo_kctl_channels(struct snd_kcontrol *kctl)
+static inline u16 snd_audient_kctl_channels(struct snd_kcontrol *kctl)
{
struct usb_mixer_elem_info *info = kctl->private_data;
@@ -241,43 +243,45 @@ static inline u16 snd_evo_kctl_channels(struct snd_kcontrol *kctl)
}
-#define EVO_REQ_CUR (0x01)
-#define EVO_REQTYPE_SET (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)
-#define EVO_REQTYPE_GET (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)
+#define AUDIENT_REQTYPE_SET (USB_DIR_OUT | USB_TYPE_CLASS \
+ | USB_RECIP_INTERFACE)
+#define AUDIENT_REQTYPE_GET (USB_DIR_IN | USB_TYPE_CLASS \
+ | USB_RECIP_INTERFACE)
-static inline int snd_evo_get_cur(struct usb_mixer_interface *mixer,
- u16 value, u16 index,
- void *buf, size_t len)
+static inline int snd_audient_get_cur(struct usb_mixer_interface *mixer,
+ u16 value, u16 index,
+ void *buf, size_t len)
{
struct usb_device *const dev = mixer->chip->dev;
return snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
- EVO_REQ_CUR, EVO_REQTYPE_GET,
+ UAC2_CS_CUR, AUDIENT_REQTYPE_GET,
value, index, buf, len);
}
-static inline int snd_evo_set_cur(struct usb_mixer_interface *mixer,
- u16 value, u16 index,
- void *buf, size_t len)
+static inline int snd_audient_set_cur(struct usb_mixer_interface *mixer,
+ u16 value, u16 index,
+ void *buf, size_t len)
{
struct usb_device *const dev = mixer->chip->dev;
return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
- EVO_REQ_CUR, EVO_REQTYPE_SET,
+ UAC2_CS_CUR, AUDIENT_REQTYPE_SET,
value, index, buf, len);
}
-static int snd_ctl_evo_boolean_get(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_value *ctl_val)
+static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ctl_val)
{
struct usb_mixer_elem_list *const list = snd_kcontrol_chip(kctl);
struct usb_mixer_interface *const mixer = list->mixer;
- const unsigned int nchan = snd_evo_kctl_channels(kctl);
- const unsigned int hwchan = snd_evo_kctl_hwchannels(kctl);
- const struct snd_evo_ctrl_type *const ctype = snd_evo_kctl_type(kctl);
- const u16 value = ctype->wValue + snd_evo_kctl_valoffs(kctl);
+ const unsigned int nchan = snd_audient_kctl_channels(kctl);
+ const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
+ const struct snd_audient_ctrl_type *const ctype
+ = snd_audient_kctl_type(kctl);
+ const u16 value = ctype->wValue + snd_audient_kctl_valoffs(kctl);
const u16 index = ctype->wIndex;
int i;
@@ -290,8 +294,8 @@ static int snd_ctl_evo_boolean_get(struct snd_kcontrol *kctl,
u32 buf;
int ret;
- ret = snd_evo_get_cur(mixer, value + i, index,
- &buf, sizeof(buf));
+ ret = snd_audient_get_cur(mixer, value + i, index,
+ &buf, sizeof(buf));
if (ret < 0)
return ret;
@@ -309,15 +313,16 @@ static int snd_ctl_evo_boolean_get(struct snd_kcontrol *kctl,
return 0;
}
-static int snd_ctl_evo_boolean_set(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_value *ctl_val)
+static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ctl_val)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
struct usb_mixer_interface *mixer = list->mixer;
- const unsigned int hwchan = snd_evo_kctl_hwchannels(kctl);
- const struct snd_evo_ctrl_type *const ctype = snd_evo_kctl_type(kctl);
- const u16 value = ctype->wValue + snd_evo_kctl_valoffs(kctl);
+ const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
+ const struct snd_audient_ctrl_type *const ctype
+ = snd_audient_kctl_type(kctl);
+ const u16 value = ctype->wValue + snd_audient_kctl_valoffs(kctl);
const u16 index = ctype->wIndex;
int changed = 0;
@@ -335,7 +340,7 @@ static int snd_ctl_evo_boolean_set(struct snd_kcontrol *kctl,
if (ctype->invert)
buf ^= cpu_to_le32(0x1);
- ret = snd_evo_get_cur(mixer, value + i, index,
+ ret = snd_audient_get_cur(mixer, value + i, index,
&rbuf, sizeof(rbuf));
if (ret < 0)
return ret;
@@ -343,8 +348,8 @@ static int snd_ctl_evo_boolean_set(struct snd_kcontrol *kctl,
if (rbuf != buf) {
changed = 1;
- ret = snd_evo_set_cur(mixer, value + i, index,
- &buf, sizeof(buf));
+ ret = snd_audient_set_cur(mixer, value + i, index,
+ &buf, sizeof(buf));
if (ret < 0)
return ret;
}
@@ -353,16 +358,17 @@ static int snd_ctl_evo_boolean_set(struct snd_kcontrol *kctl,
return changed;
}
-static int snd_ctl_evo_integer_get(struct snd_kcontrol *kctl,
+static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ctl_val)
{
struct usb_mixer_elem_list *const list = snd_kcontrol_chip(kctl);
struct usb_mixer_interface *const mixer = list->mixer;
- const unsigned int nchan = snd_evo_kctl_channels(kctl);
- const unsigned int hwchan = snd_evo_kctl_hwchannels(kctl);
- const struct snd_evo_ctrl_type *const ctype = snd_evo_kctl_type(kctl);
- const u16 value = ctype->wValue + snd_evo_kctl_valoffs(kctl);
+ const unsigned int nchan = snd_audient_kctl_channels(kctl);
+ const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
+ const struct snd_audient_ctrl_type *const ctype
+ = snd_audient_kctl_type(kctl);
+ const u16 value = ctype->wValue + snd_audient_kctl_valoffs(kctl);
const u16 index = ctype->wIndex;
int i;
@@ -376,8 +382,8 @@ static int snd_ctl_evo_integer_get(struct snd_kcontrol *kctl,
s16 val;
int ret;
- ret = snd_evo_get_cur(mixer, value + i, index,
- &buf, sizeof(buf));
+ ret = snd_audient_get_cur(mixer, value + i, index,
+ &buf, sizeof(buf));
if (ret < 0)
return ret;
@@ -391,15 +397,16 @@ static int snd_ctl_evo_integer_get(struct snd_kcontrol *kctl,
return 0;
}
-static int snd_ctl_evo_integer_set(struct snd_kcontrol *kctl,
+static int snd_ctl_audient_integer_set(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ctl_val)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
struct usb_mixer_interface *mixer = list->mixer;
- const unsigned int hwchan = snd_evo_kctl_hwchannels(kctl);
- const struct snd_evo_ctrl_type *const ctype = snd_evo_kctl_type(kctl);
- const u16 value = ctype->wValue + snd_evo_kctl_valoffs(kctl);
+ const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
+ const struct snd_audient_ctrl_type *const ctype
+ = snd_audient_kctl_type(kctl);
+ const u16 value = ctype->wValue + snd_audient_kctl_valoffs(kctl);
const u16 index = ctype->wIndex;
int changed = 0;
@@ -414,16 +421,16 @@ static int snd_ctl_evo_integer_set(struct snd_kcontrol *kctl,
s16 buf = cpu_to_le16(ctl_val->value.integer.value[i]);
int ret;
- ret = snd_evo_get_cur(mixer, value + i, index,
- &rbuf, sizeof(rbuf));
+ ret = snd_audient_get_cur(mixer, value + i, index,
+ &rbuf, sizeof(rbuf));
if (ret < 0)
return ret;
if (rbuf != buf) {
changed = 1;
- ret = snd_evo_set_cur(mixer, value + i, index,
- &buf, sizeof(buf));
+ ret = snd_audient_set_cur(mixer, value + i, index,
+ &buf, sizeof(buf));
if (ret < 0)
return ret;
}
@@ -432,23 +439,25 @@ static int snd_ctl_evo_integer_set(struct snd_kcontrol *kctl,
return changed;
}
-static int snd_ctl_evo_info(struct snd_kcontrol *kctl,
+static int snd_ctl_audient_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *uinfo)
{
- const struct snd_evo_ctrl_type *const ctype = snd_evo_kctl_type(kctl);
+ const struct snd_audient_ctrl_type *const ctype
+ = snd_audient_kctl_type(kctl);
uinfo->type = ctype->type;
- uinfo->count = snd_evo_kctl_channels(kctl);
+ uinfo->count = snd_audient_kctl_channels(kctl);
uinfo->value.integer.min = ctype->min.integer;
uinfo->value.integer.max = ctype->max.integer;
return 0;
}
-static int snd_evo_add_ctrl(struct usb_mixer_interface *mixer,
- const struct snd_evo_ctrl *ctrl)
+static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
+ const struct snd_audient_ctrl *ctrl)
{
- const struct snd_evo_ctrl_type *ctype = &snd_evo_ctypes[ctrl->type];
+ const struct snd_audient_ctrl_type *ctype
+ = &snd_audient_ctypes[ctrl->type];
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
struct snd_kcontrol_new knew = { 0 };
struct snd_kcontrol *kctl;
@@ -456,9 +465,9 @@ static int snd_evo_add_ctrl(struct usb_mixer_interface *mixer,
knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
knew.name = name;
- knew.info = snd_ctl_evo_info;
- knew.private_value = snd_evo_kctl_priv(ctrl->type, ctrl->val_offs,
- ctrl->hwchannels);
+ knew.info = snd_ctl_audient_info;
+ knew.private_value = snd_audient_kctl_priv(ctrl->type, ctrl->val_offs,
+ ctrl->hwchannels);
knew.tlv.p = ctype->tlv;
knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE
| SNDRV_CTL_ELEM_ACCESS_VOLATILE
@@ -466,20 +475,20 @@ static int snd_evo_add_ctrl(struct usb_mixer_interface *mixer,
switch (ctype->type) {
case SNDRV_CTL_ELEM_TYPE_INTEGER:
- knew.get = snd_ctl_evo_integer_get;
- knew.put = snd_ctl_evo_integer_set;
+ knew.get = snd_ctl_audient_integer_get;
+ knew.put = snd_ctl_audient_integer_set;
break;
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
- knew.get = snd_ctl_evo_boolean_get;
- knew.put = snd_ctl_evo_boolean_set;
+ knew.get = snd_ctl_audient_boolean_get;
+ knew.put = snd_ctl_audient_boolean_set;
break;
default:
return -EINVAL;
}
snprintf(name, sizeof(name), "%s %s %s", ctrl->basename,
- ((ctrl->flags & SND_EVO_FLAGS_DIRECTION)
- == SND_EVO_FLAGS_CAPTURE) ? "Capture" : "Playback",
+ ((ctrl->flags & SND_AUDIENT_FLAGS_DIRECTION)
+ == SND_AUDIENT_FLAGS_CAPTURE) ? "Capture" : "Playback",
(ctype->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN)
? "Switch" : "Volume");
@@ -503,12 +512,12 @@ static int snd_evo_add_ctrl(struct usb_mixer_interface *mixer,
/* called from mixer_quirks.c */
-int snd_evo_controls_create(struct usb_mixer_interface *mixer)
+int snd_audient_controls_create(struct usb_mixer_interface *mixer)
{
- const struct evo_devinfo *info;
- const struct snd_evo_ctrl *ctrl;
+ const struct audient_devinfo *info;
+ const struct snd_audient_ctrl *ctrl;
- for (info = evo_devinfo; info->controls; info++)
+ for (info = audient_devinfo; info->controls; info++)
if (info->usb_id == mixer->chip->usb_id)
break;
@@ -516,7 +525,7 @@ int snd_evo_controls_create(struct usb_mixer_interface *mixer)
return -ENODEV;
for (ctrl = info->controls; ctrl->basename; ctrl++) {
- const int ret = snd_evo_add_ctrl(mixer, ctrl);
+ const int ret = snd_audient_add_ctrl(mixer, ctrl);
if (ret < 0)
return ret;
diff --git a/sound/usb/mixer_audient.h b/sound/usb/mixer_audient.h
new file mode 100644
index 000000000000..db4f31e02b5e
--- /dev/null
+++ b/sound/usb/mixer_audient.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __USB_MIXER_AUDIENT_H
+#define __USB_MIXER_AUDIENT_H
+
+#include "mixer.h"
+
+#define USB_AUDIENT_VID (0x2708)
+#define USB_EVO4_PID (0x0006)
+
+int snd_audient_controls_create(struct usb_mixer_interface *mixer);
+
+#endif /* __USB_MIXER_AUDIENT_H */
diff --git a/sound/usb/mixer_evo.h b/sound/usb/mixer_evo.h
deleted file mode 100644
index 7fe7e197185c..000000000000
--- a/sound/usb/mixer_evo.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __USB_MIXER_EVO_H
-#define __USB_MIXER_EVO_H
-
-#include "mixer.h"
-
-#define USB_AUDIENT_VID (0x2708)
-#define USB_EVO4_PID (0x0006)
-
-int snd_evo_controls_create(struct usb_mixer_interface *mixer);
-
-#endif /* __USB_MIXER_EVO_H */
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 01210cc3fae7..af53c5c20e65 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -38,7 +38,7 @@
#include "mixer_scarlett2.h"
#include "mixer_us16x08.h"
#include "mixer_s1810c.h"
-#include "mixer_evo.h"
+#include "mixer_audient.h"
#include "helper.h"
#include "fcp.h"
@@ -4541,7 +4541,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
break;
case USB_ID(USB_AUDIENT_VID, USB_EVO4_PID): /* Audient EVO 4 */
- err = snd_evo_controls_create(mixer);
+ err = snd_audient_controls_create(mixer);
break;
case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC V2 PATCH 02/11] ALSA: mixer_audient: explicitly define full control names
2026-11-02 0:32 ` [RFC V2 PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor Christian Ruppert
2026-11-02 0:32 ` [RFC V2 PATCH 01/11] ALSA: rename mixer_evo to mixer_audient Christian Ruppert
@ 2026-11-02 0:32 ` Christian Ruppert
2026-11-02 0:32 ` [RFC V2 PATCH 03/11] ALSA: mixer_audient: clarify USB control message definition Christian Ruppert
` (8 subsequent siblings)
10 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-02 0:32 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Previously, control names were dynamically created depending if the
control was an input or an output, a volume or a switch. Get rid of this
logic by simply defining the full name in the control structure.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 50 +++++++++++----------------------------
1 file changed, 14 insertions(+), 36 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index e2700b08cf80..2afef52a12d9 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -90,107 +90,92 @@ static const struct snd_audient_ctrl_type {
},
};
-#define SND_AUDIENT_FLAGS_DIRECTION (1L<<0)
-#define SND_AUDIENT_FLAGS_CAPTURE (1L<<0)
-#define SND_AUDIENT_FLAGS_PLAYBACK (0L<<0)
struct snd_audient_ctrl {
- char *basename;
+ char *name;
enum snd_audient_type type;
unsigned int channels;
unsigned int hwchannels;
unsigned int val_offs;
- unsigned long flags;
};
static const struct snd_audient_ctrl snd_audient_evo4_controls[] = {
{
- .basename = "Master",
+ .name = "Master Playback Switch",
.type = SND_AUDIENT_TYPE_MUTE_OUT,
.channels = 1,
.hwchannels = 1,
.val_offs = 0,
- .flags = SND_AUDIENT_FLAGS_PLAYBACK,
},
{
- .basename = "Mic",
+ .name = "Mic Playback Switch",
.type = SND_AUDIENT_TYPE_MUTE,
.channels = 4,
.hwchannels = 2,
.val_offs = 0,
- .flags = SND_AUDIENT_FLAGS_PLAYBACK,
},
{
- .basename = "Monitor",
+ .name = "Monitor Playback Volume",
.type = SND_AUDIENT_TYPE_MONITOR,
.channels = 1,
.hwchannels = 1,
.val_offs = 0,
- .flags = SND_AUDIENT_FLAGS_PLAYBACK,
},
{
- .basename = "Mic 1",
+ .name = "Mic 1 Capture Volume",
.type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 0,
- .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
- .basename = "Mic 2",
+ .name = "Mic 2 Capture Volume",
.type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 2,
- .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
- .basename = "Master Left",
+ .name = "Master Left Capture Volume",
.type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 4,
- .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
- .basename = "Master Right",
+ .name = "Master Right Capture Volume",
.type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 6,
- .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
- .basename = "Loopback Left",
+ .name = "Loopback Left Capture Volume",
.type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 8,
- .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
- .basename = "Loopback Right",
+ .name = "Loopback Right Capture Volume",
.type = SND_AUDIENT_TYPE_MIXER,
.channels = 2,
.hwchannels = 2,
.val_offs = 10,
- .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
- .basename = "Mic 1 Phantom",
+ .name = "Mic 1 Phantom Capture Switch",
.type = SND_AUDIENT_TYPE_PHANTOM,
.channels = 1,
.hwchannels = 1,
.val_offs = 0,
- .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{
- .basename = "Mic 2 Phantom",
+ .name = "Mic 2 Phantom Capture Switch",
.type = SND_AUDIENT_TYPE_PHANTOM,
.channels = 1,
.hwchannels = 1,
.val_offs = 1,
- .flags = SND_AUDIENT_FLAGS_CAPTURE,
},
{ 0 }, /* sentinel */
};
@@ -458,13 +443,12 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
{
const struct snd_audient_ctrl_type *ctype
= &snd_audient_ctypes[ctrl->type];
- char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
struct snd_kcontrol_new knew = { 0 };
struct snd_kcontrol *kctl;
struct usb_mixer_elem_info *elem;
knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- knew.name = name;
+ knew.name = ctrl->name;
knew.info = snd_ctl_audient_info;
knew.private_value = snd_audient_kctl_priv(ctrl->type, ctrl->val_offs,
ctrl->hwchannels);
@@ -486,12 +470,6 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
return -EINVAL;
}
- snprintf(name, sizeof(name), "%s %s %s", ctrl->basename,
- ((ctrl->flags & SND_AUDIENT_FLAGS_DIRECTION)
- == SND_AUDIENT_FLAGS_CAPTURE) ? "Capture" : "Playback",
- (ctype->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN)
- ? "Switch" : "Volume");
-
elem = kzalloc_obj(*elem);
if (!elem)
return -ENOMEM;
@@ -524,7 +502,7 @@ int snd_audient_controls_create(struct usb_mixer_interface *mixer)
if (!info->controls)
return -ENODEV;
- for (ctrl = info->controls; ctrl->basename; ctrl++) {
+ for (ctrl = info->controls; ctrl->name; ctrl++) {
const int ret = snd_audient_add_ctrl(mixer, ctrl);
if (ret < 0)
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC V2 PATCH 03/11] ALSA: mixer_audient: clarify USB control message definition
2026-11-02 0:32 ` [RFC V2 PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor Christian Ruppert
2026-11-02 0:32 ` [RFC V2 PATCH 01/11] ALSA: rename mixer_evo to mixer_audient Christian Ruppert
2026-11-02 0:32 ` [RFC V2 PATCH 02/11] ALSA: mixer_audient: explicitly define full control names Christian Ruppert
@ 2026-11-02 0:32 ` Christian Ruppert
2026-11-02 0:32 ` [RFC V2 PATCH 04/11] ALSA: mixer_audient: Add length check to USB transfer Christian Ruppert
` (7 subsequent siblings)
10 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-02 0:32 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Previously, base values for wIndex and wValue were directly given in the
control type. Construct them from a unit ID and selector for clarity.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 76 +++++++++++++++++++++------------------
1 file changed, 41 insertions(+), 35 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 2afef52a12d9..63714d00592c 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -45,15 +45,15 @@ static const struct snd_audient_ctrl_type {
int integer;
} max;
const unsigned int *tlv;
- u16 wValue;
- u16 wIndex;
+ u8 unit;
+ u8 sel;
} snd_audient_ctypes[SND_AUDIENT_TYPE_NCTYPES] = {
[SND_AUDIENT_TYPE_MONITOR] = {
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.min.integer = 0,
.max.integer = 127,
- .wValue = 0x0000,
- .wIndex = 0x3800,
+ .unit = 0x38,
+ .sel = 0x00,
/* no TLV here because we can't describe the cross-fader */
},
[SND_AUDIENT_TYPE_PHANTOM] = {
@@ -61,31 +61,31 @@ static const struct snd_audient_ctrl_type {
.invert = false,
.min.integer = 0,
.max.integer = 1,
- .wValue = 0x0000,
- .wIndex = 0x3A00,
+ .unit = 0x3A,
+ .sel = 0x00,
},
[SND_AUDIENT_TYPE_MUTE] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.invert = true,
.min.integer = 0,
.max.integer = 1,
- .wValue = 0x0200,
- .wIndex = 0x3A00,
+ .unit = 0x3A,
+ .sel = 0x02,
},
[SND_AUDIENT_TYPE_MUTE_OUT] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.invert = true,
.min.integer = 0,
.max.integer = 1,
- .wValue = 0x0100,
- .wIndex = 0x3B00,
+ .unit = 0x3B,
+ .sel = 0x01,
},
[SND_AUDIENT_TYPE_MIXER] = {
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.min.integer = -128 * 256,
.max.integer = 6 * 256,
- .wValue = 0x0100,
- .wIndex = 0x3C00,
+ .unit = 0x3C,
+ .sel = 0x01,
.tlv = snd_audient_tlv_mixer,
},
};
@@ -209,7 +209,7 @@ const struct snd_audient_ctrl_type *snd_audient_kctl_type(struct snd_kcontrol
return &snd_audient_ctypes[kctl->private_value & 0xFF];
}
-static __always_inline u16 snd_audient_kctl_valoffs(struct snd_kcontrol *kctl)
+static __always_inline u16 snd_audient_kctl_cidxoffs(struct snd_kcontrol *kctl)
{
return (kctl->private_value >> 8) & 0xFF;
}
@@ -233,11 +233,16 @@ static inline u16 snd_audient_kctl_channels(struct snd_kcontrol *kctl)
#define AUDIENT_REQTYPE_GET (USB_DIR_IN | USB_TYPE_CLASS \
| USB_RECIP_INTERFACE)
+#define U8_U8(HI, LO) ((((u16)(HI)) << 8) | ((LO) & 0xFF))
+
static inline int snd_audient_get_cur(struct usb_mixer_interface *mixer,
- u16 value, u16 index,
- void *buf, size_t len)
+ const u8 unit, const u8 sel,
+ const u8 cidx,
+ void *const buf, const size_t len)
{
struct usb_device *const dev = mixer->chip->dev;
+ const u16 value = U8_U8(sel, cidx);
+ const u16 index = U8_U8(unit, mixer->hostif->desc.bInterfaceNumber);
return snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
UAC2_CS_CUR, AUDIENT_REQTYPE_GET,
@@ -245,10 +250,13 @@ static inline int snd_audient_get_cur(struct usb_mixer_interface *mixer,
}
static inline int snd_audient_set_cur(struct usb_mixer_interface *mixer,
- u16 value, u16 index,
- void *buf, size_t len)
+ const u8 unit, const u8 sel,
+ const u8 cidx,
+ void *const buf, const size_t len)
{
struct usb_device *const dev = mixer->chip->dev;
+ const u16 value = U8_U8(sel, cidx);
+ const u16 index = U8_U8(unit, mixer->hostif->desc.bInterfaceNumber);
return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
UAC2_CS_CUR, AUDIENT_REQTYPE_SET,
@@ -266,8 +274,7 @@ static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
const struct snd_audient_ctrl_type *const ctype
= snd_audient_kctl_type(kctl);
- const u16 value = ctype->wValue + snd_audient_kctl_valoffs(kctl);
- const u16 index = ctype->wIndex;
+ const u8 base = snd_audient_kctl_cidxoffs(kctl);
int i;
@@ -279,8 +286,8 @@ static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
u32 buf;
int ret;
- ret = snd_audient_get_cur(mixer, value + i, index,
- &buf, sizeof(buf));
+ ret = snd_audient_get_cur(mixer, ctype->unit, ctype->sel,
+ base + i, &buf, sizeof(buf));
if (ret < 0)
return ret;
@@ -307,8 +314,7 @@ static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
const struct snd_audient_ctrl_type *const ctype
= snd_audient_kctl_type(kctl);
- const u16 value = ctype->wValue + snd_audient_kctl_valoffs(kctl);
- const u16 index = ctype->wIndex;
+ const u8 base = snd_audient_kctl_cidxoffs(kctl);
int changed = 0;
int i;
@@ -325,15 +331,16 @@ static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
if (ctype->invert)
buf ^= cpu_to_le32(0x1);
- ret = snd_audient_get_cur(mixer, value + i, index,
- &rbuf, sizeof(rbuf));
+ ret = snd_audient_get_cur(mixer, ctype->unit, ctype->sel,
+ base + i, &rbuf, sizeof(rbuf));
if (ret < 0)
return ret;
if (rbuf != buf) {
changed = 1;
- ret = snd_audient_set_cur(mixer, value + i, index,
+ ret = snd_audient_set_cur(mixer, ctype->unit,
+ ctype->sel, base + i,
&buf, sizeof(buf));
if (ret < 0)
return ret;
@@ -353,8 +360,7 @@ static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
const struct snd_audient_ctrl_type *const ctype
= snd_audient_kctl_type(kctl);
- const u16 value = ctype->wValue + snd_audient_kctl_valoffs(kctl);
- const u16 index = ctype->wIndex;
+ const u8 base = snd_audient_kctl_cidxoffs(kctl);
int i;
@@ -367,8 +373,8 @@ static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
s16 val;
int ret;
- ret = snd_audient_get_cur(mixer, value + i, index,
- &buf, sizeof(buf));
+ ret = snd_audient_get_cur(mixer, ctype->unit, ctype->sel,
+ base + i, &buf, sizeof(buf));
if (ret < 0)
return ret;
@@ -391,8 +397,7 @@ static int snd_ctl_audient_integer_set(struct snd_kcontrol *kctl,
const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
const struct snd_audient_ctrl_type *const ctype
= snd_audient_kctl_type(kctl);
- const u16 value = ctype->wValue + snd_audient_kctl_valoffs(kctl);
- const u16 index = ctype->wIndex;
+ const u8 base = snd_audient_kctl_cidxoffs(kctl);
int changed = 0;
int i;
@@ -406,15 +411,16 @@ static int snd_ctl_audient_integer_set(struct snd_kcontrol *kctl,
s16 buf = cpu_to_le16(ctl_val->value.integer.value[i]);
int ret;
- ret = snd_audient_get_cur(mixer, value + i, index,
- &rbuf, sizeof(rbuf));
+ ret = snd_audient_get_cur(mixer, ctype->unit, ctype->sel,
+ base + i, &rbuf, sizeof(rbuf));
if (ret < 0)
return ret;
if (rbuf != buf) {
changed = 1;
- ret = snd_audient_set_cur(mixer, value + i, index,
+ ret = snd_audient_set_cur(mixer, ctype->unit,
+ ctype->sel, base + i,
&buf, sizeof(buf));
if (ret < 0)
return ret;
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC V2 PATCH 04/11] ALSA: mixer_audient: Add length check to USB transfer
2026-11-02 0:32 ` [RFC V2 PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor Christian Ruppert
` (2 preceding siblings ...)
2026-11-02 0:32 ` [RFC V2 PATCH 03/11] ALSA: mixer_audient: clarify USB control message definition Christian Ruppert
@ 2026-11-02 0:32 ` Christian Ruppert
2026-11-02 0:32 ` [RFC V2 PATCH 05/11] ALSA: mixer_audient: make USB control transfer size configurable Christian Ruppert
` (6 subsequent siblings)
10 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-02 0:32 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Check USB control transfers not only for errors but also for expected
length and return -EIO in case of error.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 63714d00592c..1f3744a7c10a 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -244,9 +244,14 @@ static inline int snd_audient_get_cur(struct usb_mixer_interface *mixer,
const u16 value = U8_U8(sel, cidx);
const u16 index = U8_U8(unit, mixer->hostif->desc.bInterfaceNumber);
- return snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
- UAC2_CS_CUR, AUDIENT_REQTYPE_GET,
- value, index, buf, len);
+ const int ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
+ UAC2_CS_CUR, AUDIENT_REQTYPE_GET,
+ value, index, buf, len);
+
+ if ((ret >= 0) && (ret != len))
+ return -EIO;
+
+ return ret;
}
static inline int snd_audient_set_cur(struct usb_mixer_interface *mixer,
@@ -258,9 +263,14 @@ static inline int snd_audient_set_cur(struct usb_mixer_interface *mixer,
const u16 value = U8_U8(sel, cidx);
const u16 index = U8_U8(unit, mixer->hostif->desc.bInterfaceNumber);
- return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
- UAC2_CS_CUR, AUDIENT_REQTYPE_SET,
- value, index, buf, len);
+ const int ret = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
+ UAC2_CS_CUR, AUDIENT_REQTYPE_SET,
+ value, index, buf, len);
+
+ if ((ret >= 0) && (ret != len))
+ return -EIO;
+
+ return ret;
}
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC V2 PATCH 05/11] ALSA: mixer_audient: make USB control transfer size configurable
2026-11-02 0:32 ` [RFC V2 PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor Christian Ruppert
` (3 preceding siblings ...)
2026-11-02 0:32 ` [RFC V2 PATCH 04/11] ALSA: mixer_audient: Add length check to USB transfer Christian Ruppert
@ 2026-11-02 0:32 ` Christian Ruppert
2026-11-02 0:32 ` [RFC V2 PATCH 06/11] ALSA: mixer_audient: Allow arbitrary boolean control values Christian Ruppert
` (5 subsequent siblings)
10 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-02 0:32 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Previously, the USB transfer size was hardcoded in the datatype get/set
functions. Make it configurable in the ctype structure in preparation of
new control types for the id14.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 131 ++++++++++++++++++++++++--------------
1 file changed, 84 insertions(+), 47 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 1f3744a7c10a..57d856d25271 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -12,6 +12,8 @@
#include <linux/usb.h>
#include <linux/usb/audio-v2.h>
#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tlv.h>
@@ -35,8 +37,12 @@ enum snd_audient_type {
/* Mixer matrix: -128dB (mute) to +6dB */
static const DECLARE_TLV_DB_MINMAX_MUTE(snd_audient_tlv_mixer, -12800, 600);
+#define SND_AUDIENT_CTYPE_FLAGS_SIGNED BIT(0)
+
static const struct snd_audient_ctrl_type {
snd_ctl_elem_type_t type;
+ unsigned long flags;
+ size_t len; /* datatype size, can be 1, 2 or 4 bytes */
bool invert;
union {
int integer;
@@ -50,6 +56,7 @@ static const struct snd_audient_ctrl_type {
} snd_audient_ctypes[SND_AUDIENT_TYPE_NCTYPES] = {
[SND_AUDIENT_TYPE_MONITOR] = {
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .len = 2,
.min.integer = 0,
.max.integer = 127,
.unit = 0x38,
@@ -59,6 +66,7 @@ static const struct snd_audient_ctrl_type {
[SND_AUDIENT_TYPE_PHANTOM] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.invert = false,
+ .len = 4,
.min.integer = 0,
.max.integer = 1,
.unit = 0x3A,
@@ -67,6 +75,7 @@ static const struct snd_audient_ctrl_type {
[SND_AUDIENT_TYPE_MUTE] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.invert = true,
+ .len = 4,
.min.integer = 0,
.max.integer = 1,
.unit = 0x3A,
@@ -75,6 +84,7 @@ static const struct snd_audient_ctrl_type {
[SND_AUDIENT_TYPE_MUTE_OUT] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.invert = true,
+ .len = 4,
.min.integer = 0,
.max.integer = 1,
.unit = 0x3B,
@@ -82,6 +92,8 @@ static const struct snd_audient_ctrl_type {
},
[SND_AUDIENT_TYPE_MIXER] = {
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .flags = SND_AUDIENT_CTYPE_FLAGS_SIGNED,
+ .len = 2,
.min.integer = -128 * 256,
.max.integer = 6 * 256,
.unit = 0x3C,
@@ -236,44 +248,91 @@ static inline u16 snd_audient_kctl_channels(struct snd_kcontrol *kctl)
#define U8_U8(HI, LO) ((((u16)(HI)) << 8) | ((LO) & 0xFF))
static inline int snd_audient_get_cur(struct usb_mixer_interface *mixer,
- const u8 unit, const u8 sel,
- const u8 cidx,
- void *const buf, const size_t len)
+ const struct snd_audient_ctrl_type *const ctype,
+ const u8 cidx, void *const buf)
{
struct usb_device *const dev = mixer->chip->dev;
- const u16 value = U8_U8(sel, cidx);
- const u16 index = U8_U8(unit, mixer->hostif->desc.bInterfaceNumber);
+ const u16 value = U8_U8(ctype->sel, cidx);
+ const u16 index = U8_U8(ctype->unit,
+ mixer->hostif->desc.bInterfaceNumber);
const int ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
UAC2_CS_CUR, AUDIENT_REQTYPE_GET,
- value, index, buf, len);
+ value, index, buf, ctype->len);
- if ((ret >= 0) && (ret != len))
+ if ((ret >= 0) && (ret != ctype->len))
return -EIO;
return ret;
}
static inline int snd_audient_set_cur(struct usb_mixer_interface *mixer,
- const u8 unit, const u8 sel,
- const u8 cidx,
- void *const buf, const size_t len)
+ const struct snd_audient_ctrl_type *const ctype,
+ const u8 cidx, void *const buf)
{
struct usb_device *const dev = mixer->chip->dev;
- const u16 value = U8_U8(sel, cidx);
- const u16 index = U8_U8(unit, mixer->hostif->desc.bInterfaceNumber);
+ const u16 value = U8_U8(ctype->sel, cidx);
+ const u16 index = U8_U8(ctype->unit,
+ mixer->hostif->desc.bInterfaceNumber);
const int ret = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
UAC2_CS_CUR, AUDIENT_REQTYPE_SET,
- value, index, buf, len);
+ value, index, buf, ctype->len);
- if ((ret >= 0) && (ret != len))
+ if ((ret >= 0) && (ret != ctype->len))
return -EIO;
return ret;
}
+static int snd_audient_raw_get_one(struct usb_mixer_interface *const mixer,
+ const struct snd_audient_ctrl_type *const ctype,
+ const u8 cidx, u32 *const value)
+{
+ int ret;
+ __le32 buf = 0;
+ s32 val;
+
+ ret = snd_audient_get_cur(mixer, ctype, cidx, &buf);
+ if (ret < 0)
+ return ret;
+
+ val = le32_to_cpu(buf);
+
+ if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_SIGNED)
+ val = sign_extend32(val, 8 * ctype->len - 1);
+
+ *value = val;
+
+ return ret;
+}
+
+static int snd_audient_raw_set_one(struct usb_mixer_interface *const mixer,
+ const struct snd_audient_ctrl_type *const ctype,
+ const u8 cidx, const u32 value)
+{
+ int ret;
+ u32 val;
+
+ ret = snd_audient_raw_get_one(mixer, ctype, cidx, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val != value) {
+ __le32 buf = cpu_to_le32(value);
+
+ ret = snd_audient_set_cur(mixer, ctype, cidx, &buf);
+ if (ret < 0)
+ return ret;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ctl_val)
{
@@ -296,12 +355,11 @@ static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
u32 buf;
int ret;
- ret = snd_audient_get_cur(mixer, ctype->unit, ctype->sel,
- base + i, &buf, sizeof(buf));
+ ret = snd_audient_raw_get_one(mixer, ctype, base + i, &buf);
if (ret < 0)
return ret;
- ctl_val->value.integer.value[i] = le32_to_cpu(buf) ? 1 : 0;
+ ctl_val->value.integer.value[i] = buf ? 1 : 0;
if (ctype->invert)
ctl_val->value.integer.value[i] ^= 0x1;
@@ -334,27 +392,18 @@ static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
return pm.err;
for (i = 0; i < hwchan; i++) {
- u32 rbuf;
- u32 buf = cpu_to_le32(ctl_val->value.integer.value[i] ? 1 : 0);
+ u32 buf = ctl_val->value.integer.value[i] ? 1 : 0;
int ret;
if (ctype->invert)
- buf ^= cpu_to_le32(0x1);
+ buf ^= 0x1;
- ret = snd_audient_get_cur(mixer, ctype->unit, ctype->sel,
- base + i, &rbuf, sizeof(rbuf));
+ ret = snd_audient_raw_set_one(mixer, ctype, base + i, buf);
if (ret < 0)
return ret;
- if (rbuf != buf) {
+ if (ret)
changed = 1;
-
- ret = snd_audient_set_cur(mixer, ctype->unit,
- ctype->sel, base + i,
- &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- }
}
return changed;
@@ -379,16 +428,13 @@ static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
return pm.err;
for (i = 0; i < hwchan; i++) {
- u16 buf;
- s16 val;
+ s32 val;
int ret;
- ret = snd_audient_get_cur(mixer, ctype->unit, ctype->sel,
- base + i, &buf, sizeof(buf));
+ ret = snd_audient_raw_get_one(mixer, ctype, base + i, &val);
if (ret < 0)
return ret;
- val = le16_to_cpu(buf);
ctl_val->value.integer.value[i] = val;
}
@@ -417,24 +463,15 @@ static int snd_ctl_audient_integer_set(struct snd_kcontrol *kctl,
return pm.err;
for (i = 0; i < hwchan; i++) {
- s16 rbuf;
- s16 buf = cpu_to_le16(ctl_val->value.integer.value[i]);
+ s32 val = ctl_val->value.integer.value[i];
int ret;
- ret = snd_audient_get_cur(mixer, ctype->unit, ctype->sel,
- base + i, &rbuf, sizeof(rbuf));
+ ret = snd_audient_raw_set_one(mixer, ctype, base + i, val);
if (ret < 0)
return ret;
- if (rbuf != buf) {
+ if (ret)
changed = 1;
-
- ret = snd_audient_set_cur(mixer, ctype->unit,
- ctype->sel, base + i,
- &buf, sizeof(buf));
- if (ret < 0)
- return ret;
- }
}
return changed;
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC V2 PATCH 06/11] ALSA: mixer_audient: Allow arbitrary boolean control values
2026-11-02 0:32 ` [RFC V2 PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor Christian Ruppert
` (4 preceding siblings ...)
2026-11-02 0:32 ` [RFC V2 PATCH 05/11] ALSA: mixer_audient: make USB control transfer size configurable Christian Ruppert
@ 2026-11-02 0:32 ` Christian Ruppert
2026-11-02 0:32 ` [RFC V2 PATCH 07/11] ALSA: mixer_audient: Add integer range checks Christian Ruppert
` (4 subsequent siblings)
10 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-02 0:32 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
In preparation of id14 support, allow arbitrary values for true and false
of boolean switches. This also rids us of the tedious invert flag.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 43 ++++++++++++++++++---------------------
1 file changed, 20 insertions(+), 23 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 57d856d25271..4b20db9a82e2 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -43,13 +43,12 @@ static const struct snd_audient_ctrl_type {
snd_ctl_elem_type_t type;
unsigned long flags;
size_t len; /* datatype size, can be 1, 2 or 4 bytes */
- bool invert;
union {
int integer;
- } min;
+ } min; /* for booleans holds physical int value for false */
union {
int integer;
- } max;
+ } max; /* for booleans holds physical int value for true */
const unsigned int *tlv;
u8 unit;
u8 sel;
@@ -65,7 +64,6 @@ static const struct snd_audient_ctrl_type {
},
[SND_AUDIENT_TYPE_PHANTOM] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
- .invert = false,
.len = 4,
.min.integer = 0,
.max.integer = 1,
@@ -74,19 +72,17 @@ static const struct snd_audient_ctrl_type {
},
[SND_AUDIENT_TYPE_MUTE] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
- .invert = true,
.len = 4,
- .min.integer = 0,
- .max.integer = 1,
+ .min.integer = 1,
+ .max.integer = 0,
.unit = 0x3A,
.sel = 0x02,
},
[SND_AUDIENT_TYPE_MUTE_OUT] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
- .invert = true,
.len = 4,
- .min.integer = 0,
- .max.integer = 1,
+ .min.integer = 1,
+ .max.integer = 0,
.unit = 0x3B,
.sel = 0x01,
},
@@ -359,16 +355,12 @@ static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
if (ret < 0)
return ret;
- ctl_val->value.integer.value[i] = buf ? 1 : 0;
-
- if (ctype->invert)
- ctl_val->value.integer.value[i] ^= 0x1;
+ ctl_val->value.integer.value[i] = (buf == ctype->min.integer)
+ ? false : true;
}
for (; i < nchan; i++)
- ctl_val->value.integer.value[i] = ctype->invert
- ? ctype->max.integer
- : ctype->min.integer;
+ ctl_val->value.integer.value[i] = 0;
return 0;
}
@@ -392,12 +384,10 @@ static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
return pm.err;
for (i = 0; i < hwchan; i++) {
- u32 buf = ctl_val->value.integer.value[i] ? 1 : 0;
+ u32 buf = ctl_val->value.integer.value[i] ? ctype->max.integer
+ : ctype->min.integer;
int ret;
- if (ctype->invert)
- buf ^= 0x1;
-
ret = snd_audient_raw_set_one(mixer, ctype, base + i, buf);
if (ret < 0)
return ret;
@@ -485,8 +475,15 @@ static int snd_ctl_audient_info(struct snd_kcontrol *kctl,
uinfo->type = ctype->type;
uinfo->count = snd_audient_kctl_channels(kctl);
- uinfo->value.integer.min = ctype->min.integer;
- uinfo->value.integer.max = ctype->max.integer;
+
+ if (ctype->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN) {
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ } else {
+ uinfo->value.integer.min = ctype->min.integer;
+ uinfo->value.integer.max = ctype->max.integer;
+ }
+
return 0;
}
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC V2 PATCH 07/11] ALSA: mixer_audient: Add integer range checks
2026-11-02 0:32 ` [RFC V2 PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor Christian Ruppert
` (5 preceding siblings ...)
2026-11-02 0:32 ` [RFC V2 PATCH 06/11] ALSA: mixer_audient: Allow arbitrary boolean control values Christian Ruppert
@ 2026-11-02 0:32 ` Christian Ruppert
2026-11-02 0:32 ` [RFC V2 PATCH 08/11] ALSA: mixer_audient: Add control initialization at probe Christian Ruppert
` (3 subsequent siblings)
10 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-02 0:32 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Add range checks to integer parameters to improve error management:
- Reject out-of-range write attempts with -EINVAL
- Prune out-of-range values returned by the device to the range limits
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 4b20db9a82e2..433bb5479d8e 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -44,10 +44,10 @@ static const struct snd_audient_ctrl_type {
unsigned long flags;
size_t len; /* datatype size, can be 1, 2 or 4 bytes */
union {
- int integer;
+ long integer;
} min; /* for booleans holds physical int value for false */
union {
- int integer;
+ long integer;
} max; /* for booleans holds physical int value for true */
const unsigned int *tlv;
u8 unit;
@@ -425,6 +425,12 @@ static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
if (ret < 0)
return ret;
+ if (val < ctype->min.integer)
+ val = ctype->min.integer;
+
+ if (val > ctype->max.integer)
+ val = ctype->max.integer;
+
ctl_val->value.integer.value[i] = val;
}
@@ -448,6 +454,13 @@ static int snd_ctl_audient_integer_set(struct snd_kcontrol *kctl,
int changed = 0;
int i;
+ for (i = 0; i < hwchan; i++) {
+ long val = ctl_val->value.integer.value[i];
+
+ if ((val < ctype->min.integer) || (val > ctype->max.integer))
+ return -EINVAL;
+ }
+
CLASS(snd_usb_lock, pm)(mixer->chip);
if (pm.err < 0)
return pm.err;
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC V2 PATCH 08/11] ALSA: mixer_audient: Add control initialization at probe
2026-11-02 0:32 ` [RFC V2 PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor Christian Ruppert
` (6 preceding siblings ...)
2026-11-02 0:32 ` [RFC V2 PATCH 07/11] ALSA: mixer_audient: Add integer range checks Christian Ruppert
@ 2026-11-02 0:32 ` Christian Ruppert
2026-11-02 0:32 ` [RFC V2 PATCH 09/11] ALSA: mixer_audient: Add readback-free parameter set Christian Ruppert
` (2 subsequent siblings)
10 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-02 0:32 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Add the possibility to initialize certain control types to known values at
probe time. This is particularly useful for controls which initially read
wrong values and for controls which cannot be read at all.
Signed-off-by: Christian Ruppert <arc@gmx.li>
Co-developed-by: Will Porter <mrwillporter@gmail.com>
Signed-off-by: Will Porter <mrwillporter@gmail.com>
Assisted-by: Claude:claude-opus-5-5
---
sound/usb/mixer_audient.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 433bb5479d8e..70ec3274a5e4 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -38,6 +38,7 @@ enum snd_audient_type {
static const DECLARE_TLV_DB_MINMAX_MUTE(snd_audient_tlv_mixer, -12800, 600);
#define SND_AUDIENT_CTYPE_FLAGS_SIGNED BIT(0)
+#define SND_AUDIENT_CTYPE_FLAGS_INIT_HW BIT(1)
static const struct snd_audient_ctrl_type {
snd_ctl_elem_type_t type;
@@ -49,6 +50,7 @@ static const struct snd_audient_ctrl_type {
union {
long integer;
} max; /* for booleans holds physical int value for true */
+ u32 init;
const unsigned int *tlv;
u8 unit;
u8 sel;
@@ -533,6 +535,20 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
return -EINVAL;
}
+ if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_INIT_HW) {
+ int i;
+
+ for (i = 0; i < ctrl->hwchannels; i++) {
+ int ret = snd_audient_raw_set_one(mixer, ctype,
+ ctrl->val_offs + i,
+ ctype->init);
+ if (ret < 0)
+ usb_audio_warn(mixer->chip,
+ "%s: cannot set channel %d (%d)\n",
+ ctrl->name, i, ret);
+ }
+ }
+
elem = kzalloc_obj(*elem);
if (!elem)
return -ENOMEM;
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC V2 PATCH 09/11] ALSA: mixer_audient: Add readback-free parameter set
2026-11-02 0:32 ` [RFC V2 PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor Christian Ruppert
` (7 preceding siblings ...)
2026-11-02 0:32 ` [RFC V2 PATCH 08/11] ALSA: mixer_audient: Add control initialization at probe Christian Ruppert
@ 2026-11-02 0:32 ` Christian Ruppert
2026-11-02 0:32 ` [RFC V2 PATCH 10/11] ALSA: mixer_audient: add control value caching Christian Ruppert
2026-11-02 0:32 ` [RFC V2 PATCH 11/11] ALSA: mixer_audient: Create matrix programmatically Christian Ruppert
10 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-02 0:32 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Add a flag to systematically force parameter writing without prior readback
to certain control types. In preparation of the id14 driver.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_audient.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 70ec3274a5e4..f61f9fe61345 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -39,6 +39,7 @@ static const DECLARE_TLV_DB_MINMAX_MUTE(snd_audient_tlv_mixer, -12800, 600);
#define SND_AUDIENT_CTYPE_FLAGS_SIGNED BIT(0)
#define SND_AUDIENT_CTYPE_FLAGS_INIT_HW BIT(1)
+#define SND_AUDIENT_CTYPE_FLAGS_FORCE_SET BIT(2)
static const struct snd_audient_ctrl_type {
snd_ctl_elem_type_t type;
@@ -313,9 +314,13 @@ static int snd_audient_raw_set_one(struct usb_mixer_interface *const mixer,
int ret;
u32 val;
- ret = snd_audient_raw_get_one(mixer, ctype, cidx, &val);
- if (ret < 0)
- return ret;
+ if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET) {
+ val = !value; /* just make it different */
+ } else {
+ ret = snd_audient_raw_get_one(mixer, ctype, cidx, &val);
+ if (ret < 0)
+ return ret;
+ }
if (val != value) {
__le32 buf = cpu_to_le32(value);
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC V2 PATCH 10/11] ALSA: mixer_audient: add control value caching
2026-11-02 0:32 ` [RFC V2 PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor Christian Ruppert
` (8 preceding siblings ...)
2026-11-02 0:32 ` [RFC V2 PATCH 09/11] ALSA: mixer_audient: Add readback-free parameter set Christian Ruppert
@ 2026-11-02 0:32 ` Christian Ruppert
2026-11-02 0:32 ` [RFC V2 PATCH 11/11] ALSA: mixer_audient: Create matrix programmatically Christian Ruppert
10 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-02 0:32 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
In preparation of id14 support, add control value caching.
Signed-off-by: Christian Ruppert <arc@gmx.li>
Co-developed-by: Will Porter <mrwillporter@gmail.com>
Signed-off-by: Will Porter <mrwillporter@gmail.com>
Assisted-by: Claude:claude-opus-5-5
---
sound/usb/mixer_audient.c | 186 ++++++++++++++++++++++++++++++--------
1 file changed, 150 insertions(+), 36 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index f61f9fe61345..856fb669af3f 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -40,6 +40,7 @@ static const DECLARE_TLV_DB_MINMAX_MUTE(snd_audient_tlv_mixer, -12800, 600);
#define SND_AUDIENT_CTYPE_FLAGS_SIGNED BIT(0)
#define SND_AUDIENT_CTYPE_FLAGS_INIT_HW BIT(1)
#define SND_AUDIENT_CTYPE_FLAGS_FORCE_SET BIT(2)
+#define SND_AUDIENT_CTYPE_FLAGS_CACHE BIT(3)
static const struct snd_audient_ctrl_type {
snd_ctl_elem_type_t type;
@@ -51,7 +52,7 @@ static const struct snd_audient_ctrl_type {
union {
long integer;
} max; /* for booleans holds physical int value for true */
- u32 init;
+ u32 init; /* hardware and cache are initialized to this value */
const unsigned int *tlv;
u8 unit;
u8 sel;
@@ -91,7 +92,8 @@ static const struct snd_audient_ctrl_type {
},
[SND_AUDIENT_TYPE_MIXER] = {
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
- .flags = SND_AUDIENT_CTYPE_FLAGS_SIGNED,
+ .flags = SND_AUDIENT_CTYPE_FLAGS_SIGNED
+ | SND_AUDIENT_CTYPE_FLAGS_CACHE,
.len = 2,
.min.integer = -128 * 256,
.max.integer = 6 * 256,
@@ -238,6 +240,26 @@ static inline u16 snd_audient_kctl_channels(struct snd_kcontrol *kctl)
return info->channels;
}
+/*
+ * A cached value is valid once a transfer confirmed it: a read, or a write
+ * that succeeded. A probe transfer that fails leaves it invalid. CACHE |
+ * FORCE_SET without INIT_HW keeps the earlier behaviour: the init value is
+ * assumed, without a transfer.
+ */
+struct snd_audient_cache_entry {
+ u32 value;
+ bool valid;
+};
+
+/* Cache entry of element channel i, or NULL for an uncached control. */
+static inline struct snd_audient_cache_entry *
+snd_audient_cache_slot(struct usb_mixer_elem_info *elem, unsigned int i)
+{
+ struct snd_audient_cache_entry *const cache = elem->private_data;
+
+ return cache ? cache + i : NULL;
+}
+
#define AUDIENT_REQTYPE_SET (USB_DIR_OUT | USB_TYPE_CLASS \
| USB_RECIP_INTERFACE)
@@ -287,12 +309,26 @@ static inline int snd_audient_set_cur(struct usb_mixer_interface *mixer,
static int snd_audient_raw_get_one(struct usb_mixer_interface *const mixer,
const struct snd_audient_ctrl_type *const ctype,
+ struct snd_audient_cache_entry *const slot,
const u8 cidx, u32 *const value)
{
int ret;
__le32 buf = 0;
s32 val;
+ if (slot && slot->valid) {
+ *value = slot->value;
+
+ return ctype->len;
+ }
+
+ /*
+ * Cached FORCE_SET controls are treated as write-only: an invalid
+ * cache entry does not trigger a GET.
+ */
+ if (slot && (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET))
+ return -ENODATA;
+
ret = snd_audient_get_cur(mixer, ctype, cidx, &buf);
if (ret < 0)
return ret;
@@ -304,43 +340,58 @@ static int snd_audient_raw_get_one(struct usb_mixer_interface *const mixer,
*value = val;
+ if (slot) {
+ slot->value = val;
+ slot->valid = true;
+ }
+
return ret;
}
static int snd_audient_raw_set_one(struct usb_mixer_interface *const mixer,
const struct snd_audient_ctrl_type *const ctype,
- const u8 cidx, const u32 value)
+ struct snd_audient_cache_entry *const slot,
+ const u8 cidx, const u32 value,
+ bool force)
{
int ret;
u32 val;
- if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET) {
+ if (force) {
val = !value; /* just make it different */
} else {
- ret = snd_audient_raw_get_one(mixer, ctype, cidx, &val);
- if (ret < 0)
+ ret = snd_audient_raw_get_one(mixer, ctype, slot, cidx, &val);
+ if (ret == -ENODATA) /* FORCED_SET and not cached yet */
+ val = !value; /* just make it different */
+ else if (ret < 0)
return ret;
}
- if (val != value) {
+ force = force || (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET);
+
+ if (force || (val != value)) {
__le32 buf = cpu_to_le32(value);
ret = snd_audient_set_cur(mixer, ctype, cidx, &buf);
if (ret < 0)
return ret;
- return 1;
+ if (slot) {
+ slot->value = value;
+ slot->valid = true;
+ }
+
}
- return 0;
+ return val != value;
}
static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ctl_val)
{
- struct usb_mixer_elem_list *const list = snd_kcontrol_chip(kctl);
- struct usb_mixer_interface *const mixer = list->mixer;
+ struct usb_mixer_elem_info *const elem = snd_kcontrol_chip(kctl);
+ struct usb_mixer_interface *const mixer = elem->head.mixer;
const unsigned int nchan = snd_audient_kctl_channels(kctl);
const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
@@ -353,13 +404,19 @@ static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
CLASS(snd_usb_lock, pm)(mixer->chip);
if (pm.err < 0)
return pm.err;
+ /* serializes the transfers and the cache, also between gets */
+ guard(mutex)(&mixer->lock);
for (i = 0; i < hwchan; i++) {
u32 buf;
int ret;
- ret = snd_audient_raw_get_one(mixer, ctype, base + i, &buf);
- if (ret < 0)
+ ret = snd_audient_raw_get_one(mixer, ctype,
+ snd_audient_cache_slot(elem, i),
+ base + i, &buf);
+ if (ret == -ENODATA) /* unknown write-only value: off */
+ buf = ctype->min.integer;
+ else if (ret < 0)
return ret;
ctl_val->value.integer.value[i] = (buf == ctype->min.integer)
@@ -375,8 +432,8 @@ static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ctl_val)
{
- struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
- struct usb_mixer_interface *mixer = list->mixer;
+ struct usb_mixer_elem_info *const elem = snd_kcontrol_chip(kctl);
+ struct usb_mixer_interface *const mixer = elem->head.mixer;
const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
const struct snd_audient_ctrl_type *const ctype
@@ -389,13 +446,17 @@ static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
CLASS(snd_usb_lock, pm)(mixer->chip);
if (pm.err < 0)
return pm.err;
+ /* serializes the transfers and the cache, also between gets */
+ guard(mutex)(&mixer->lock);
for (i = 0; i < hwchan; i++) {
u32 buf = ctl_val->value.integer.value[i] ? ctype->max.integer
: ctype->min.integer;
int ret;
- ret = snd_audient_raw_set_one(mixer, ctype, base + i, buf);
+ ret = snd_audient_raw_set_one(mixer, ctype,
+ snd_audient_cache_slot(elem, i),
+ base + i, buf, false);
if (ret < 0)
return ret;
@@ -409,8 +470,8 @@ static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ctl_val)
{
- struct usb_mixer_elem_list *const list = snd_kcontrol_chip(kctl);
- struct usb_mixer_interface *const mixer = list->mixer;
+ struct usb_mixer_elem_info *const elem = snd_kcontrol_chip(kctl);
+ struct usb_mixer_interface *const mixer = elem->head.mixer;
const unsigned int nchan = snd_audient_kctl_channels(kctl);
const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
@@ -423,13 +484,19 @@ static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
CLASS(snd_usb_lock, pm)(mixer->chip);
if (pm.err < 0)
return pm.err;
+ /* serializes the transfers and the cache, also between gets */
+ guard(mutex)(&mixer->lock);
for (i = 0; i < hwchan; i++) {
s32 val;
int ret;
- ret = snd_audient_raw_get_one(mixer, ctype, base + i, &val);
- if (ret < 0)
+ ret = snd_audient_raw_get_one(mixer, ctype,
+ snd_audient_cache_slot(elem, i),
+ base + i, &val);
+ if (ret == -ENODATA) /* unknown write-only value: minimum */
+ val = ctype->min.integer;
+ else if (ret < 0)
return ret;
if (val < ctype->min.integer)
@@ -450,8 +517,8 @@ static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
static int snd_ctl_audient_integer_set(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ctl_val)
{
- struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
- struct usb_mixer_interface *mixer = list->mixer;
+ struct usb_mixer_elem_info *const elem = snd_kcontrol_chip(kctl);
+ struct usb_mixer_interface *mixer = elem->head.mixer;
const unsigned int hwchan = snd_audient_kctl_hwchannels(kctl);
const struct snd_audient_ctrl_type *const ctype
@@ -471,12 +538,16 @@ static int snd_ctl_audient_integer_set(struct snd_kcontrol *kctl,
CLASS(snd_usb_lock, pm)(mixer->chip);
if (pm.err < 0)
return pm.err;
+ /* serializes the transfers and the cache, also between gets */
+ guard(mutex)(&mixer->lock);
for (i = 0; i < hwchan; i++) {
s32 val = ctl_val->value.integer.value[i];
int ret;
- ret = snd_audient_raw_set_one(mixer, ctype, base + i, val);
+ ret = snd_audient_raw_set_one(mixer, ctype,
+ snd_audient_cache_slot(elem, i),
+ base + i, val, false);
if (ret < 0)
return ret;
@@ -517,6 +588,8 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
struct snd_kcontrol *kctl;
struct usb_mixer_elem_info *elem;
+ int ret;
+
knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
knew.name = ctrl->name;
knew.info = snd_ctl_audient_info;
@@ -524,7 +597,9 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
ctrl->hwchannels);
knew.tlv.p = ctype->tlv;
knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE
- | SNDRV_CTL_ELEM_ACCESS_VOLATILE
+ | ((ctype->flags & SND_AUDIENT_CTYPE_FLAGS_CACHE)
+ ? 0
+ : SNDRV_CTL_ELEM_ACCESS_VOLATILE)
| (ctype->tlv ? SNDRV_CTL_ELEM_ACCESS_TLV_READ : 0);
switch (ctype->type) {
@@ -540,36 +615,75 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
return -EINVAL;
}
+ elem = kzalloc(sizeof(*elem)
+ + ((ctype->flags & SND_AUDIENT_CTYPE_FLAGS_CACHE)
+ ? (sizeof(struct snd_audient_cache_entry)
+ * ctrl->hwchannels)
+ : 0),
+ GFP_KERNEL);
+ if (!elem)
+ return -ENOMEM;
+
+ elem->head.mixer = mixer;
+ elem->channels = ctrl->channels;
+
+ if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_CACHE)
+ elem->private_data = elem + 1;
+
+ /*
+ * A failed probe transfer must not fail the card: warn and keep the
+ * control. Its cache entry stays invalid, so a readable control reads
+ * the device again on its next get, and a write-only control shows an
+ * unknown value until a put succeeds.
+ */
if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_INIT_HW) {
int i;
for (i = 0; i < ctrl->hwchannels; i++) {
- int ret = snd_audient_raw_set_one(mixer, ctype,
- ctrl->val_offs + i,
- ctype->init);
+ ret = snd_audient_raw_set_one(mixer, ctype,
+ snd_audient_cache_slot(elem, i),
+ ctrl->val_offs + i,
+ ctype->init, true);
if (ret < 0)
usb_audio_warn(mixer->chip,
"%s: cannot set channel %d (%d)\n",
ctrl->name, i, ret);
}
- }
-
- elem = kzalloc_obj(*elem);
- if (!elem)
- return -ENOMEM;
+ } else if (elem->private_data) {
+ struct snd_audient_cache_entry *const cache = elem->private_data;
+ int i;
- elem->head.mixer = mixer;
- elem->channels = ctrl->channels;
+ for (i = 0; i < ctrl->hwchannels; i++) {
+ if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET) {
+ cache[i].value = ctype->init;
+ cache[i].valid = true;
+ continue;
+ }
+
+ ret = snd_audient_raw_get_one(mixer, ctype, cache + i,
+ ctrl->val_offs + i,
+ &cache[i].value);
+ if (ret < 0)
+ usb_audio_warn(mixer->chip,
+ "%s: cannot read channel %d (%d)\n",
+ ctrl->name, i, ret);
+ }
+ }
kctl = snd_ctl_new1(&knew, elem);
if (!kctl) {
- kfree(elem);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto free_elem;
}
kctl->private_free = snd_usb_mixer_elem_free;
return snd_usb_mixer_add_control(&elem->head, kctl);
+
+free_elem:
+ kfree(elem);
+
+ return ret;
}
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread* [RFC V2 PATCH 11/11] ALSA: mixer_audient: Create matrix programmatically
2026-11-02 0:32 ` [RFC V2 PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor Christian Ruppert
` (9 preceding siblings ...)
2026-11-02 0:32 ` [RFC V2 PATCH 10/11] ALSA: mixer_audient: add control value caching Christian Ruppert
@ 2026-11-02 0:32 ` Christian Ruppert
10 siblings, 0 replies; 37+ messages in thread
From: Christian Ruppert @ 2026-11-02 0:32 UTC (permalink / raw)
To: Will Porter
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel,
Neil Andrews, Christian Ruppert
Most Audient devices seem to have a similar mixer matrix with many
identical channels. The only difference between devices seems to be
the matrix' dimension. Create it programmatically in a loop rather
than from the controls table.
Signed-off-by: Christian Ruppert <arc@gmx.li>
Co-developed-by: Will Porter <mrwillporter@gmail.com>
Signed-off-by: Will Porter <mrwillporter@gmail.com>
Assisted-by: Claude:claude-opus-5-5
---
sound/usb/mixer_audient.c | 77 +++++++++++++++++----------------------
1 file changed, 34 insertions(+), 43 deletions(-)
diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index 856fb669af3f..483ab8fe6109 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -105,7 +105,7 @@ static const struct snd_audient_ctrl_type {
struct snd_audient_ctrl {
- char *name;
+ const char *const name;
enum snd_audient_type type;
unsigned int channels;
unsigned int hwchannels;
@@ -134,48 +134,6 @@ static const struct snd_audient_ctrl snd_audient_evo4_controls[] = {
.hwchannels = 1,
.val_offs = 0,
},
- {
- .name = "Mic 1 Capture Volume",
- .type = SND_AUDIENT_TYPE_MIXER,
- .channels = 2,
- .hwchannels = 2,
- .val_offs = 0,
- },
- {
- .name = "Mic 2 Capture Volume",
- .type = SND_AUDIENT_TYPE_MIXER,
- .channels = 2,
- .hwchannels = 2,
- .val_offs = 2,
- },
- {
- .name = "Master Left Capture Volume",
- .type = SND_AUDIENT_TYPE_MIXER,
- .channels = 2,
- .hwchannels = 2,
- .val_offs = 4,
- },
- {
- .name = "Master Right Capture Volume",
- .type = SND_AUDIENT_TYPE_MIXER,
- .channels = 2,
- .hwchannels = 2,
- .val_offs = 6,
- },
- {
- .name = "Loopback Left Capture Volume",
- .type = SND_AUDIENT_TYPE_MIXER,
- .channels = 2,
- .hwchannels = 2,
- .val_offs = 8,
- },
- {
- .name = "Loopback Right Capture Volume",
- .type = SND_AUDIENT_TYPE_MIXER,
- .channels = 2,
- .hwchannels = 2,
- .val_offs = 10,
- },
{
.name = "Mic 1 Phantom Capture Switch",
.type = SND_AUDIENT_TYPE_PHANTOM,
@@ -193,13 +151,27 @@ static const struct snd_audient_ctrl snd_audient_evo4_controls[] = {
{ 0 }, /* sentinel */
};
+static const char *const snd_audient_evo4_matrix[] = {
+ "Mic 1 Capture Volume",
+ "Mic 2 Capture Volume",
+ "Master Left Capture Volume",
+ "Master Right Capture Volume",
+ "Loopback Left Capture Volume",
+ "Loopback Right Capture Volume",
+ NULL
+};
+
static const struct audient_devinfo {
u32 usb_id;
const struct snd_audient_ctrl *controls;
+ const char *const *const matrix_names;
+ const unsigned int matrix_channels;
} audient_devinfo[] = {
{
.usb_id = USB_ID(USB_AUDIENT_VID, USB_EVO4_PID),
.controls = snd_audient_evo4_controls,
+ .matrix_names = snd_audient_evo4_matrix,
+ .matrix_channels = 2,
},
{ 0 } /* sentinel */
};
@@ -693,6 +665,8 @@ int snd_audient_controls_create(struct usb_mixer_interface *mixer)
const struct audient_devinfo *info;
const struct snd_audient_ctrl *ctrl;
+ int i;
+
for (info = audient_devinfo; info->controls; info++)
if (info->usb_id == mixer->chip->usb_id)
break;
@@ -707,5 +681,22 @@ int snd_audient_controls_create(struct usb_mixer_interface *mixer)
return ret;
}
+ if (info->matrix_names) {
+ for (i = 0; info->matrix_names[i]; i++) {
+ const struct snd_audient_ctrl c = {
+ .name = info->matrix_names[i],
+ .type = SND_AUDIENT_TYPE_MIXER,
+ .channels = info->matrix_channels,
+ .hwchannels = info->matrix_channels,
+ .val_offs = i * info->matrix_channels,
+ };
+
+ const int ret = snd_audient_add_ctrl(mixer, &c);
+
+ if (ret < 0)
+ return ret;
+ }
+ }
+
return 0;
}
--
2.56.0
^ permalink raw reply [flat|nested] 37+ messages in thread