From: Alexander Niemeyer <adventureFAN@gmx.de>
To: Rong Zhang <i@rong.moe>, Jaroslav Kysela <perex@perex.cz>,
Takashi Iwai <tiwai@suse.com>
Cc: Takashi Iwai <tiwai@suse.de>,
linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH] ALSA: usb-audio: Check sticky mixers precisely
Date: Sun, 16 Aug 2026 07:14:28 +0200 [thread overview]
Message-ID: <d62a54f6-c3dc-4502-b61c-6cdc49fe40f0@gmx.de> (raw)
In-Reply-To: <20260816-uac-precise-sticky-check-v1-1-00798373dc3c@rong.moe>
Hi Rong,
I tested the sticky-check part of your patch on the Logitech PRO X
Wireless (046d:0aba) on Fedora 44, kernel 7.1.8-200.fc44.x86_64.
Since your patch is based on a newer tree, I used a minimal backport of
the new ~16-value / 10 ms sticky-check logic to the 7.1.8 code. The
GET_CUR-broken handling from the newer tree was not included; GET_CUR
itself succeeds on this device.
Unfortunately, the playback control is still classified as sticky:
2:0: sticky mixer values (-16384/0/256 => -3840), disabling
I then instrumented the check and tried an additional diagnostic: after
every successful SET_CUR, wait 100 ms and perform another GET_CUR before
issuing the next SET_CUR.
For the playback volume, the saved value was -3840 and GET_CUR remained
at -3840 for every tested value, even after 100 ms, for example:
test=-15104 immediate=-3840 after100ms=-3840
test=-13824 immediate=-3840 after100ms=-3840
test=-3584 immediate=-3840 after100ms=-3840
test=-2304 immediate=-3840 after100ms=-3840
test=-1024 immediate=-3840 after100ms=-3840
test=0 immediate=-3840 after100ms=-3840
So in this case the issue does not appear to be simply that the
accumulated 10 ms sleeps are too short. During the probe-time sticky
check, SET_CUR succeeds but GET_CUR for the playback control remains
unchanged even when each SET_CUR is given 100 ms before the next one.
This differs from my previous direct libusb tests with the AudioControl
interface unbound, where valid SET_CUR values became visible through
GET_CUR after roughly 47–81 ms.
The first debug line I saw with |saved=0| was from the Mic Capture
Volume control; that control changed immediately and returned as
non-sticky. The sequence above with |saved=-3840| is the problematic PCM
Playback Volume control.
I'd be happy to test another version or run additional diagnostics if
useful.
Best regards,
Alexander
Am 15.08.2026 um 23:47 schrieb Rong Zhang:
> Some mixers are asynchronous, and some have broken min/max. They are
> mistakenly considered sticky due to how the check is implemented.
>
> Check sticky mixers more precisely by checking approximately 16 values
> and adding a msleep(10) between each check, so that asynchronous mixers
> have enough time to change the value and mixers with broken min/max are
> checked properly. Additionally, mark GET_CUR as broken when
> get_cur_mix_raw() fails, instead of returning successfully.
>
> Reported-by: Alexander Niemeyer <adventureFAN@gmx.de>
> Closes: https://lore.kernel.org/r/6262cbbd-d1f2-4c9d-a1c7-9c5d12636f4b@gmx.de
> Signed-off-by: Rong Zhang <i@rong.moe>
> ---
> sound/usb/mixer.c | 51 ++++++++++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 44 insertions(+), 7 deletions(-)
>
> diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
> index 703c118f9d4e..3d0f97730a06 100644
> --- a/sound/usb/mixer.c
> +++ b/sound/usb/mixer.c
> @@ -1256,22 +1256,59 @@ static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx)
> 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;
> + int test, check, res;
> +
> + /*
> + * Check approximately 16 values (15 intervals).
> + * If the resolution is not fine enough, check fewer values.
> + */
> + res = DIV_ROUND_UP(cval->max - cval->min, 15);
> + res = res ? roundup(res, cval->res) : cval->res;
> +
> + /*
> + * If (cval->max - cval->min) is not a multiple of cval->res, we still
> + * want to test cval->max anyway.
> + */
> + for (test = cval->min; test < cval->max + res; test += res) {
> + if (test > cval->max)
> + test = cval->max;
>
> - 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. */
> + if (snd_usb_set_cur_mix_value(cval, channel, 0, test))
> + return 0;
> +
> + if (get_cur_mix_raw(cval, channel, &check))
> + goto get_cur_broken;
> + if (check != saved) /* SET_CUR effective, non-sticky. */
> return 0;
> +
> + /*
> + * Leave some time for asynchronous mixers to change the value.
> + *
> + * Note that there is no need to wait between SET_CUR and
> + * GET_CUR, as we don't care whether the GET_CUR value matches
> + * the SET_CUR one. IOW, what we expect is just a GET_CUR value
> + * differing from the saved one.
> + *
> + * Mixers of most devices are synchronous. The should have
> + * returned early without extra sleep. Asynchronous mixers will
> + * return once the accumulated time is enough for them to change
> + * the value.
> + */
> + msleep(10);
> }
>
> + /* Check again after the last msleep(). */
> + if (get_cur_mix_raw(cval, channel, &check))
> + goto get_cur_broken;
> + if (check != saved)
> + return 0;
> +
> if (cval->head.mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_GET_CUR_BROKEN) {
> +get_cur_broken:
> usb_audio_info(cval->head.mixer->chip,
> "%d:%d: broken mixer GET_CUR (%d/%d/%d => %d)\n",
> cval->head.id, mixer_ctrl_intf(cval->head.mixer),
>
> ---
> base-commit: 3eb40771c00a8488fa6ed2cc1fe203477908bf38
> change-id: 74676fce-uac-precise-sticky-check-94474a22b57d
>
> Thanks,
> Rong
>
next prev parent reply other threads:[~2026-08-16 5:14 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-15 21:47 Rong Zhang
2026-08-16 5:14 ` Alexander Niemeyer [this message]
2026-08-16 13:50 ` Rong Zhang
[not found] ` <74ca2e17-8fb8-4ede-8e7e-441be815b5b6@gmx.de>
2026-08-16 15:08 ` Rong Zhang
2026-08-18 14:41 ` Alexander Niemeyer
2026-08-18 15:24 ` Alexander Niemeyer
2026-08-19 16:39 ` Rong Zhang
2026-08-19 18:37 ` Alexander Niemeyer
2026-08-20 12:51 ` Rong Zhang
2026-08-20 15:01 ` Alexander Niemeyer
2026-08-20 16:29 ` Rong Zhang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=d62a54f6-c3dc-4502-b61c-6cdc49fe40f0@gmx.de \
--to=adventurefan@gmx.de \
--cc=i@rong.moe \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sound@vger.kernel.org \
--cc=perex@perex.cz \
--cc=tiwai@suse.com \
--cc=tiwai@suse.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®