From: Rong Zhang Date: Fri, 10 Apr 2026 17:49:04 +0000 (+0800) Subject: ALSA: usb-audio: Do not expose sticky mixers X-Git-Tag: v7.1-rc1~166^2~9^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=86aa1ea1f15ce6b56ac1b4c0d9b88a07a5b9bf03;p=thirdparty%2Fkernel%2Flinux.git ALSA: usb-audio: Do not expose sticky mixers 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 Link: https://patch.msgid.link/20260411-uac-sticky-mixer-v1-3-29d62717befd@rong.moe Signed-off-by: Takashi Iwai --- diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index e77c2d78a782a..d4ef45bf53d77 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -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); }