[PATCH 1/2] ALSA: usb-audio: Roll back quirk control caches on write errors

From: Cássio Gabriel

Date: Wed Apr 29 2026 - 09:25:12 EST


Several mixer quirk callbacks cache the requested
control value in kcontrol->private_value before
issuing a single vendor or class write.

Their paired get and resume paths consume that cache
directly, so a failed write currently leaves software
state changed even though the update did not succeed.
That can make later reads report a value the device
never accepted and can replay the stale cache on resume.

Restore the previous cached value on failure in
the Audigy2NX LED, Emu0204 channel switch,
Xonar U1 output switch, Native Instruments controls,
FTU effect program switch, and Sound Blaster E1 input source switch.

Fixes: 9cf3689bfe07 ("ALSA: usb-audio: Add audigy2nx resume support")
Fixes: 5f503ee9e270 ("ALSA: usb-audio: Add Emu0204 channel switch resume support")
Fixes: 2bfb14c3b8fb ("ALSA: usb-audio: Add Xonar U1 resume support")
Fixes: da6d276957ea ("ALSA: usb-audio: Add resume support for Native Instruments controls")
Fixes: 0b4e9cfcef05 ("ALSA: usb-audio: Add resume support for FTU controls")
Fixes: 388fdb8f882a ("ALSA: usb-audio: Support changing input on Sound Blaster E1")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@xxxxxxxxx>
---
sound/usb/mixer_quirks.c | 45 +++++++++++++++++++++++++++++++++++++--------
1 file changed, 37 insertions(+), 8 deletions(-)

diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 1bdaa46d4fe1..229be55e9158 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -333,6 +333,7 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol,
int index = kcontrol->private_value & 0xff;
unsigned int value = ucontrol->value.integer.value[0];
int old_value = kcontrol->private_value >> 8;
+ unsigned long old_pval = kcontrol->private_value;
int err;

if (value > 1)
@@ -341,7 +342,11 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol,
return 0;
kcontrol->private_value = (value << 8) | index;
err = snd_audigy2nx_led_update(mixer, value, index);
- return err < 0 ? err : 1;
+ if (err < 0) {
+ kcontrol->private_value = old_pval;
+ return err;
+ }
+ return 1;
}

static int snd_audigy2nx_led_resume(struct usb_mixer_elem_list *list)
@@ -487,6 +492,7 @@ static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol,
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
struct usb_mixer_interface *mixer = list->mixer;
unsigned int value = ucontrol->value.enumerated.item[0];
+ unsigned long old_pval = kcontrol->private_value;
int err;

if (value > 1)
@@ -497,7 +503,11 @@ static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol,

kcontrol->private_value = value;
err = snd_emu0204_ch_switch_update(mixer, value);
- return err < 0 ? err : 1;
+ if (err < 0) {
+ kcontrol->private_value = old_pval;
+ return err;
+ }
+ return 1;
}

static int snd_emu0204_ch_switch_resume(struct usb_mixer_elem_list *list)
@@ -821,7 +831,11 @@ static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,

kcontrol->private_value = new_status;
err = snd_xonar_u1_switch_update(list->mixer, new_status);
- return err < 0 ? err : 1;
+ if (err < 0) {
+ kcontrol->private_value = old_status;
+ return err;
+ }
+ return 1;
}

static int snd_xonar_u1_switch_resume(struct usb_mixer_elem_list *list)
@@ -1159,7 +1173,8 @@ static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
- u8 oldval = (kcontrol->private_value >> 24) & 0xff;
+ unsigned long old_pval = kcontrol->private_value;
+ u8 oldval = (old_pval >> 24) & 0xff;
u8 newval = ucontrol->value.integer.value[0];
int err;

@@ -1169,7 +1184,11 @@ static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol,
kcontrol->private_value &= ~(0xff << 24);
kcontrol->private_value |= (unsigned int)newval << 24;
err = snd_ni_update_cur_val(list);
- return err < 0 ? err : 1;
+ if (err < 0) {
+ kcontrol->private_value = old_pval;
+ return err;
+ }
+ return 1;
}

static const struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = {
@@ -1324,7 +1343,8 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
- unsigned int pval = list->kctl->private_value;
+ unsigned long old_pval = list->kctl->private_value;
+ unsigned int pval = old_pval;
int cur_val, err, new_val;

cur_val = pval >> 24;
@@ -1335,7 +1355,11 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
kctl->private_value &= ~(0xff << 24);
kctl->private_value |= new_val << 24;
err = snd_ftu_eff_switch_update(list);
- return err < 0 ? err : 1;
+ if (err < 0) {
+ kctl->private_value = old_pval;
+ return err;
+ }
+ return 1;
}

static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
@@ -2114,13 +2138,18 @@ static int snd_soundblaster_e1_switch_put(struct snd_kcontrol *kcontrol,
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
unsigned char value = !!ucontrol->value.integer.value[0];
+ unsigned long old_pval = kcontrol->private_value;
int err;

if (kcontrol->private_value == value)
return 0;
kcontrol->private_value = value;
err = snd_soundblaster_e1_switch_update(list->mixer, value);
- return err < 0 ? err : 1;
+ if (err < 0) {
+ kcontrol->private_value = old_pval;
+ return err;
+ }
+ return 1;
}

static int snd_soundblaster_e1_switch_resume(struct usb_mixer_elem_list *list)

--
2.54.0