]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ALSA: usb-audio: Do not expose sticky mixers
authorRong Zhang <i@rong.moe>
Fri, 10 Apr 2026 17:49:04 +0000 (01:49 +0800)
committerTakashi Iwai <tiwai@suse.de>
Sat, 11 Apr 2026 08:03:14 +0000 (10:03 +0200)
Some devices' mixers are sticky, which accept SET_CUR but do absolutely
nothing. Registering these mixers confuses userspace and results in
ineffective volume control.

Check if a mixer is sticky by setting the volume to the maximum or
minimum value and checking for effectiveness afterward. Prevent the
mixer from being registered if it turns out to be sticky.

Quirky device sample:

  usb 7-1: New USB device found, idVendor=0e0b, idProduct=fa01, bcdDevice= 1.00
  usb 7-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
  usb 7-1: Product: Feaulle Rainbow
  usb 7-1: Manufacturer: Generic
  usb 7-1: SerialNumber: 20210726905926
  (Mic Capture Volume)

Signed-off-by: Rong Zhang <i@rong.moe>
Link: https://patch.msgid.link/20260411-uac-sticky-mixer-v1-3-29d62717befd@rong.moe
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/usb/mixer.c

index e77c2d78a782a3950cc20197b78aecf50b1958b4..d4ef45bf53d77c14d925066e6276aee27f71b79b 100644 (file)
@@ -1232,6 +1232,41 @@ static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx)
        snd_usb_set_cur_mix_value(cval, ch, idx, cval->min);
 }
 
+/*
+ * Additional checks for sticky mixers
+ *
+ * Some devices' volume control mixers are sticky, which accept SET_CUR but
+ * do absolutely nothing.
+ *
+ * Prevent sticky mixers from being registered, otherwise they confuses
+ * userspace and results in ineffective volume control.
+ */
+static int check_sticky_volume_control(struct usb_mixer_elem_info *cval,
+                                      int channel, int saved)
+{
+       int sticky_test_values[] = { cval->min, cval->max };
+       int test, check, i;
+
+       for (i = 0; i < ARRAY_SIZE(sticky_test_values); i++) {
+               test = sticky_test_values[i];
+               if (test == saved)
+                       continue;
+
+               /* Assume non-sticky on failure. */
+               if (snd_usb_set_cur_mix_value(cval, channel, 0, test) ||
+                   get_cur_mix_raw(cval, channel, &check) ||
+                   check != saved) /* SET_CUR effective, non-sticky. */
+                       return 0;
+       }
+
+       usb_audio_err(cval->head.mixer->chip,
+                     "%d:%d: sticky mixer values (%d/%d/%d => %d), disabling\n",
+                     cval->head.id, mixer_ctrl_intf(cval->head.mixer),
+                     cval->min, cval->max, cval->res, saved);
+
+       return -ENODEV;
+}
+
 /*
  * Additional checks for the proper resolution
  *
@@ -1270,7 +1305,7 @@ static void check_volume_control_res(struct usb_mixer_elem_info *cval,
 static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
                                   int default_min, struct snd_kcontrol *kctl)
 {
-       int i, idx;
+       int i, idx, ret;
 
        /* for failsafe */
        cval->min = default_min;
@@ -1319,13 +1354,20 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
                if (cval->res == 0)
                        cval->res = 1;
 
-               if (cval->min + cval->res < cval->max) {
+               if (cval->min < cval->max) {
                        int saved;
 
                        if (get_cur_mix_raw(cval, minchn, &saved) < 0)
                                goto no_checks;
 
-                       check_volume_control_res(cval, minchn, saved);
+                       ret = check_sticky_volume_control(cval, minchn, saved);
+                       if (ret < 0) {
+                               snd_usb_set_cur_mix_value(cval, minchn, 0, saved);
+                               return ret;
+                       }
+
+                       if (cval->min + cval->res < cval->max)
+                               check_volume_control_res(cval, minchn, saved);
 
                        snd_usb_set_cur_mix_value(cval, minchn, 0, saved);
                }