[PATCH 2/3] ALSA: usb-audio: Fix UAC2 mixer unit request handling
From: Takashi Iwai
Date: Thu Sep 17 2026 - 03:49:19 EST
For a request for a Mixer Unit on UAC2 (also UAC3), the wValue is
different from UAC1 and an incompatible value must be passed.
Namely, UAC1 takes a word consisting of 1-based input channel in the
high byte and 1-based output channel in the low byte.
Meanwhile, UAC2/3 takes UAC2_MU_MIXER in the high byte and a MCN
(0-based bit position of input/output channels) in the low byte.
The current driver implementation blindly assumes the UAC1 way, hence
it would cause a firmware error.
This patch attempts to implement the conversion to UAC2 MCN at
get_ctl_value_v2() and snd_usb_mixer_set_ctl_value() for mixer units.
At the points above, the old wValue containing ICN and OCN is
converted to the corresponding MCN, and it's used as the proper
wValue.
Reported-by: Zipdox <zipdox@xxxxxxxxxx>
Closes: https://lore.kernel.org/d46fcac6-bd7e-4fc4-95e1-4e8d39f92ad3@xxxxxxxxxx
Fixes: 23caaf19b11e ("ALSA: usb-mixer: Add support for Audio Class v2.0")
Signed-off-by: Takashi Iwai <tiwai@xxxxxxx>
---
sound/usb/mixer.c | 22 ++++++++++++++++++++++
sound/usb/mixer.h | 2 ++
2 files changed, 24 insertions(+)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index ddfd7e01a3ef..54a512fb5fa8 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -335,6 +335,16 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request,
return -EINVAL;
}
+/* convert the given UAC1 wValue (ICN|OCN) to UAC2 MCN */
+static unsigned int to_mcn(const struct usb_mixer_elem_info *cval,
+ unsigned int validx)
+{
+ unsigned char m = (validx >> 8) & 0xff; /* 1-based input channel */
+ unsigned char v = validx & 0xff; /* 1-based output channel */
+
+ return (m - 1) * cval->num_outputs + (v - 1);
+}
+
static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
int validx, int *value_ret)
{
@@ -347,6 +357,10 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
val_size = uac2_ctl_value_size(cval->val_type);
+ /* correct wValue for UAC2 mixer control with MCN */
+ if (cval->v2_mixer)
+ validx = (UAC2_MU_MIXER << 8) | to_mcn(cval, validx);
+
if (request == UAC_GET_CUR) {
bRequest = UAC2_CS_CUR;
size = val_size;
@@ -478,6 +492,10 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
}
request = UAC2_CS_CUR;
+
+ /* correct wValue for UAC2 mixer control with MCN */
+ if (cval->v2_mixer)
+ validx = (UAC2_MU_MIXER << 8) | to_mcn(cval, validx);
}
value_set = convert_bytes_value(cval, value_set);
@@ -2345,6 +2363,10 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid);
cval->control = in_ch + 1; /* based on 1 */
+ if (state->mixer->protocol == UAC_VERSION_2 ||
+ state->mixer->protocol == UAC_VERSION_3)
+ cval->v2_mixer = true;
+ cval->num_outputs = num_outs;
cval->val_type = USB_MIXER_S16;
for (i = 0; i < num_outs; i++) {
__u8 *c = uac_mixer_unit_bmControls(desc, state->mixer->protocol);
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index 037b446d8b6f..cf45c39cbccc 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -97,6 +97,8 @@ struct usb_mixer_elem_info {
u8 initialized;
u8 min_mute;
u8 get_cur_broken;
+ u8 num_outputs;
+ bool v2_mixer;
void *private_data;
};
--
2.55.0
--Multipart_Thu_Sep_17_16:49:35_2026-1
Content-Type: application/octet-stream; type=patch; name="0003-ALSA-usb-audio-Optimize-min-max-res-parse-for-UAC2.patch"
Content-Disposition: attachment; filename="0003-ALSA-usb-audio-Optimize-min-max-res-parse-for-UAC2.patch"
Content-Transfer-Encoding: 7bit