[REGRESSION] ALSA: usb-audio: Logitech PRO X Wireless 046d:0aba playback volume rejected as sticky
"Alexander N." <[email protected]>
| Newsgroups | org.kernel.vger.linux-sound,dev.linux.lists.regressions |
|---|---|
| Message-ID | <[email protected]> |
Hello,
I found a regression in snd-usb-audio affecting the Logitech PRO X
Wireless Gaming Headset (USB ID 046d:0aba).
The device has a valid UAC1 playback volume control, but the sticky mixer
detection introduced by commit 86aa1ea1f15c ("ALSA: usb-audio: Do not
expose sticky mixers") rejects it.
Hardware
========
Logitech PRO X Wireless Gaming Headset
USB ID: 046d:0aba
Observed kernel
===============
7.1.5-ogc5.1.fc44.x86_64
Bazzite/Fedora 44 OGC kernel
The OGC patch for this kernel does not modify sound/usb, and the relevant
mixer.c code matches upstream Linux 7.1.5.
I have not hardware-tested current mainline 7.2-rc7, but current upstream
mixer.c still contains the same immediate SET_CUR -> GET_CUR sticky mixer
test and I could not find a device quirk for 046d:0aba.
Symptom
=======
Without a workaround, ALSA only exposes:
Simple mixer control 'PCM',0
Capabilities: pswitch pswitch-joined
There is no PCM Playback Volume control.
The kernel logs:
usb 1-10: 2:0: sticky mixer values (-16384/0/256 => -3840), disabling
I have also observed the same message ending in "=> 0", depending on the
hardware volume at probe time.
Because the playback volume control is removed, the physical volume wheel
changes the headset's hardware volume independently of the PipeWire/KDE
system volume.
USB Audio control behavior
==========================
The device is UAC1.
Feature Unit 2 exposes a master playback volume control.
Direct control requests, after temporarily unbinding AudioControl
interface 0 from snd-usb-audio, report:
GET_MIN = -16384 (-64.00 dB)
GET_MAX = 0 ( 0.00 dB)
GET_RES = 256 ( 1.00 dB)
GET_CUR and SET_CUR both work, but the device has two quirks relevant to
the new sticky detection.
1. GET_CUR reflects normal SET_CUR changes with a delay of roughly 50 ms.
Measured from 0 dB:
target result first visible GET_CUR change
-1 dB OK 81.3 ms
-2 dB OK 51.9 ms
-4 dB OK 47.3 ms
-8 dB OK 47.1 ms
-16 dB OK 51.7 ms
-32 dB OK 47.2 ms
2. The advertised minimum value -64 dB is not functional.
A direct SET_CUR to -64 dB returns success, but GET_CUR remains at 0 dB
even after 1000 ms.
This appears to cause a false positive in check_sticky_volume_control():
- cval->min is -16384 (-64 dB)
- cval->max is 0
- if the saved value is 0, max is skipped
- SET_CUR(min) returns success
- an immediate GET_CUR still returns the saved value
- the mixer is classified as sticky and is not registered
mixer_get_cur_broken is not appropriate
=======================================
I tested the mixer_get_cur_broken quirk.
GET_CUR on this device is not broken or constant. It correctly reports
host SET_CUR changes after the device delay, and it also reports volume
changes caused by the physical headset wheel.
Using an internal-only cached value would therefore lose useful hardware
state.
Local proof-of-concept fix
==========================
I built a local snd-usb-audio.ko against the running 7.1.5 kernel.
For this specific device, Feature Unit 2, UAC_FU_VOLUME, I:
- clamp the unusable minimum from -64 dB to -63 dB
- skip the probe-time sticky/resolution checks for this control
With that module, ALSA exposes:
Simple mixer control 'PCM',0
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
Playback channels: Mono
Limits: Playback 0 - 63
/proc/asound/card*/usbmixer shows:
Unit: 2
Control: name="PCM Playback Volume", index=0
Info: id=2, control=2, cmask=0x0, channels=1, type="S16"
Volume: min=-16128, max=0, dBmin=-6300, dBmax=0
Most importantly, no userspace workaround is required once the mixer
control is restored.
Example before rotating the physical headset wheel:
ALSA: -27 dB / 57%
PipeWire: 0.35
After rotating the headset wheel:
ALSA: -11 dB / 83%
PipeWire: 0.65
KDE system volume follows the physical wheel as expected.
I attached the proof-of-concept diff and the measured results. I am happy
to test a maintainer-preferred implementation or additional diagnostics.
My suspicion is that this device exposes two assumptions in the sticky
mixer probe that are not universally safe:
1. GET_CUR is assumed to reflect SET_CUR immediately.
2. advertised min/max values are assumed to be usable test values.
Thanks.
#regzbot introduced: 86aa1ea1f15ce6b56ac1b4c0d9b88a07a5b9bf03
measurements.txt
(text/plain, 1.6 KB)
Logitech PRO X Wireless Gaming Headset USB ID: 046d:0aba UAC1 playback control --------------------- Feature Unit: 2 Control: UAC_FU_VOLUME Channel: master (0) GET_MIN: -16384 = -64.00 dB GET_MAX: 0 = 0.00 dB GET_RES: 256 = 1.00 dB SET_CUR / GET_CUR timing ------------------------ Start: 0 dB Target Result First visible GET_CUR change -1 dB OK 81.3 ms -2 dB OK 51.9 ms -4 dB OK 47.3 ms -8 dB OK 47.1 ms -16 dB OK 51.7 ms -32 dB OK 47.2 ms -64 dB FAIL GET_CUR stayed at 0 dB after 1000 ms Observed stock kernel message ----------------------------- usb 1-10: 2:0: sticky mixer values (-16384/0/256 => -3840), disabling Also observed depending on current hardware volume: usb 1-10: 2:0: sticky mixer values (-16384/0/256 => 0), disabling Stock ALSA mixer ---------------- Simple mixer control 'PCM',0 Capabilities: pswitch pswitch-joined Playback channels: Mono Mono: Playback [on] Patched ALSA mixer ------------------ Simple mixer control 'PCM',0 Capabilities: pvolume pvolume-joined pswitch pswitch-joined Playback channels: Mono Limits: Playback 0 - 63 /proc/asound/card*/usbmixer: Unit: 2 Control: name="PCM Playback Volume", index=0 Info: id=2, control=2, cmask=0x0, channels=1, type="S16" Volume: min=-16128, max=0, dBmin=-6300, dBmax=0 Physical volume wheel integration after PoC fix ----------------------------------------------- Before: ALSA: -27 dB / 57% PipeWire: 0.35 After wheel rotation: ALSA: -11 dB / 83% PipeWire: 0.65 KDE follows the headset wheel after the control is restored.
poc-fix.diff
(text/x-patch, 888 B)
Proof-of-concept only; not proposed as the final upstream implementation.
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1,3 +1,3 @@
@@
if (cval->res == 0)
cval->res = 1;
+ /*
+ * Logitech PRO X Wireless volume quirk (046d:0aba)
+ *
+ * The device advertises -64 dB as minimum playback volume, but
+ * SET_CUR(-64 dB) is ineffective. Working values are reflected by
+ * GET_CUR only after roughly 50 ms, so the probe-time sticky and
+ * resolution tests can mis-detect this valid control.
+ */
+ if (cval->head.mixer->chip->usb_id == USB_ID(0x046d, 0x0aba) &&
+ cval->head.id == 2 &&
+ cval->control == UAC_FU_VOLUME) {
+ usb_audio_info(cval->head.mixer->chip,
+ "Logitech PRO X Wireless: applying playback volume quirk\n");
+ cval->min = -16128; /* -63 dB */
+ goto no_checks;
+ }
+
if (cval->min < cval->max) {
int saved;