NetBSD Problem Report #59712
From www@netbsd.org Sun Oct 19 13:13:06 2025
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (2048 bits)
client-signature RSA-PSS (2048 bits))
(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
by mollari.NetBSD.org (Postfix) with ESMTPS id 67CDD1A9239
for <gnats-bugs@gnats.NetBSD.org>; Sun, 19 Oct 2025 13:13:06 +0000 (UTC)
Message-Id: <20251019131305.358511A923A@mollari.NetBSD.org>
Date: Sun, 19 Oct 2025 13:13:05 +0000 (UTC)
From: nia@pkgsrc.org
Reply-To: nia@pkgsrc.org
To: gnats-bugs@NetBSD.org
Subject: hdaudio driver exposes nonexistent volume controls
X-Send-Pr-Version: www-1.0
>Number: 59712
>Category: kern
>Synopsis: hdaudio driver exposes nonexistent volume controls
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: kern-bug-people
>State: analyzed
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sun Oct 19 13:15:00 +0000 2025
>Closed-Date:
>Last-Modified: Tue Oct 28 13:35:01 +0000 2025
>Originator: nia
>Release: any with hdaudio
>Organization:
The SliderBSD Foundation
>Environment:
>Description:
My audio chipset (and indeed any realtek chipset that i've seen)
has mute-only output amplifier controls:
[ 1.044129] hdaudio1 at pci7 dev 0 function 4: HD Audio Controller
[ 1.044129] hdaudio1: interrupting at msi6 vec 0
[ 1.044129] hdaudio1: HDA ver. 1.0, OSS 4, ISS 4, BSS 0, SDO 1, 64-bit
[ 1.044129] hdafg1 at hdaudio1: Realtek ALC897
[ 1.044129] add ctrl outamp mute=0 step=87 size=2 offset=87
[ 1.044129] add ctrl outamp mute=0 step=87 size=2 offset=87
[ 1.044129] add ctrl outamp mute=0 step=87 size=2 offset=87
[ 1.044129] add ctrl outamp mute=0 step=87 size=2 offset=87
[ 1.044129] add ctrl outamp mute=1 step=0 size=0 offset=0
[ 1.044129] add ctrl outamp mute=1 step=0 size=0 offset=0
[ 1.044129] add ctrl outamp mute=1 step=0 size=0 offset=0
[ 1.044129] add ctrl outamp mute=1 step=0 size=0 offset=0
[ 1.044129] add ctrl outamp mute=1 step=0 size=0 offset=0
[ 1.044129] add ctrl outamp mute=1 step=0 size=0 offset=0
[ 1.044129] add ctrl outamp mute=1 step=0 size=0 offset=0
[ 1.044129] add ctrl outamp mute=1 step=0 size=0 offset=0
[ 1.044129] add ctrl outamp mute=0 step=87 size=2 offset=87
These are the ones with step=0 and size=0.
Currently the way the driver tries to handle these is quite odd.
It creates a normal mute control, but also creates a volume control
with delta=256 (i.e. not possible to modify). This clutters up
mixer interfaces and i've had to put a workaround for this in aiomixer.
The code contains some hacky workarounds for these fake controls,
e.g.:
if (ctl->ctl_step == 0)
divisor = 128; /* ??? - just avoid div by 0 */
else
divisor = 255 / ctl->ctl_step;
hdafg_control_amp_set(ctl, HDAUDIO_AMP_MUTE_NONE,
mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] / divisor,
mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] / divisor);
>How-To-Repeat:
run mixerctl -la on a machine with realtek audio hardware
>Fix:
The way that `hdafg_build_mixers` is written means that volume-only
controls (with no mute) are possible, but mute-only controls with
no volume are not possible. This restriction does not come from any
particular API or specification, it's just a quirk of the driver.
The driver should be modified to build only mute enums for those
mute-only controls.
I should get around to this.
>Release-Note:
>Audit-Trail:
From: nia <nia@NetBSD.org>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: kern/59712: hdaudio driver exposes nonexistent volume controls
Date: Sun, 19 Oct 2025 16:23:03 +0000
--gTKzjOIvDZugVxqG
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
And here is a patch.
--gTKzjOIvDZugVxqG
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment; filename=hdafg.patch
hdaudio(4): Do not expose non-functional volume controls.
The old code supported "volume without mute", but not "mute
without volume", which are widely exposed by hdaudio devices.
This resulted in redundant volume controls with delta=256
(not allowing them to be set).
While here, classify PC speaker and phone out controls as
output controls.
Index: sys/dev/hdaudio/hdafg.c
===================================================================
RCS file: /cvsroot/src/sys/dev/hdaudio/hdafg.c,v
retrieving revision 1.32
diff -u -p -r1.32 hdafg.c
--- sys/dev/hdaudio/hdafg.c 29 Jan 2024 18:58:54 -0000 1.32
+++ sys/dev/hdaudio/hdafg.c 19 Oct 2025 16:19:36 -0000
@@ -2820,7 +2820,8 @@ hdafg_build_mixers(struct hdafg_softc *s
ctl->ctl_audiomask == 0)
continue;
audiomask |= ctl->ctl_audiomask;
- ++nmixers;
+ if (ctl->ctl_step > 0)
+ ++nmixers;
if (ctl->ctl_mute)
++nmixers;
}
@@ -2831,7 +2832,8 @@ hdafg_build_mixers(struct hdafg_softc *s
HDAUDIO_MASK(PCM)) {
audiomask |= HDAUDIO_MASK(VOLUME);
for (i = 0; i < sc->sc_nctls; i++) {
- if (sc->sc_ctls[i].ctl_audiomask == HDAUDIO_MASK(PCM)) {
+ if (sc->sc_ctls[i].ctl_audiomask == HDAUDIO_MASK(PCM) &&
+ sc->sc_ctls[i].ctl_step > 0) {
masterctl = &sc->sc_ctls[i];
++nmixers;
if (masterctl->ctl_mute)
@@ -2874,7 +2876,8 @@ hdafg_build_mixers(struct hdafg_softc *s
mx[index].mx_di.index = index;
mx[index].mx_di.type = AUDIO_MIXER_CLASS;
mx[index].mx_di.mixer_class = i;
- mx[index].mx_di.next = mx[index].mx_di.prev = AUDIO_MIXER_LAST;
+ mx[index].mx_di.prev = index > 0 ? index - 1 : 0;
+ mx[index].mx_di.next = index + 1;
switch (i) {
case HDAUDIO_MIXER_CLASS_OUTPUTS:
strcpy(mx[index].mx_di.label.name, AudioCoutputs);
@@ -2891,24 +2894,32 @@ hdafg_build_mixers(struct hdafg_softc *s
/* Shadow master control */
if (masterctl != NULL) {
- mx[index].mx_ctl = masterctl;
- mx[index].mx_di.index = index;
- mx[index].mx_di.type = AUDIO_MIXER_VALUE;
- mx[index].mx_di.prev = mx[index].mx_di.next = AUDIO_MIXER_LAST;
- mx[index].mx_di.un.v.num_channels = 2; /* XXX */
- mx[index].mx_di.mixer_class = HDAUDIO_MIXER_CLASS_OUTPUTS;
- mx[index].mx_di.un.v.delta = 256 /
- (masterctl->ctl_step ? masterctl->ctl_step : 1);
- strcpy(mx[index].mx_di.label.name, AudioNmaster);
- strcpy(mx[index].mx_di.un.v.units.name, AudioNvolume);
- hda_trace(sc, " adding outputs.%s\n",
- mx[index].mx_di.label.name);
- ++index;
+ if (masterctl->ctl_step > 0) {
+ mx[index].mx_ctl = masterctl;
+ mx[index].mx_di.index = index;
+ mx[index].mx_di.type = AUDIO_MIXER_VALUE;
+ mx[index].mx_di.prev = index > 0 ? index - 1 : 0;
+ mx[index].mx_di.next = index + 1;
+ mx[index].mx_di.un.v.num_channels = 2; /* XXX */
+ mx[index].mx_di.mixer_class = HDAUDIO_MIXER_CLASS_OUTPUTS;
+ mx[index].mx_di.un.v.delta = 256 / masterctl->ctl_step;
+ strcpy(mx[index].mx_di.label.name, AudioNmaster);
+ strcpy(mx[index].mx_di.un.v.units.name, AudioNvolume);
+ hda_trace(sc, " adding outputs.%s\n",
+ mx[index].mx_di.label.name);
+ ++index;
+ }
if (masterctl->ctl_mute) {
- mx[index] = mx[index - 1];
+ if (masterctl->ctl_step > 0) {
+ mx[index - 1].mx_di.next = index;
+ mx[index] = mx[index - 1];
+ mx[index].mx_di.prev = index;
+ }
+ mx[index].mx_ctl = masterctl;
mx[index].mx_di.index = index;
mx[index].mx_di.type = AUDIO_MIXER_ENUM;
- mx[index].mx_di.prev = mx[index].mx_di.next = AUDIO_MIXER_LAST;
+ mx[index].mx_di.prev = index > 0 ? index - 1 : 0;
+ mx[index].mx_di.next = index + 1;
strcpy(mx[index].mx_di.label.name, AudioNmaster "." AudioNmute);
mx[index].mx_di.un.e.num_mem = 2;
strcpy(mx[index].mx_di.un.e.member[0].label.name, AudioNoff);
@@ -2928,66 +2939,84 @@ hdafg_build_mixers(struct hdafg_softc *s
ctl->ctl_audiomask == 0)
continue;
audiodev = ffs(ctl->ctl_audiomask) - 1;
- mx[index].mx_ctl = ctl;
- mx[index].mx_di.index = index;
- mx[index].mx_di.type = AUDIO_MIXER_VALUE;
- mx[index].mx_di.prev = mx[index].mx_di.next = AUDIO_MIXER_LAST;
- mx[index].mx_di.un.v.num_channels = 2; /* XXX */
- mx[index].mx_di.un.v.delta = 256 /
- (ctl->ctl_step ? ctl->ctl_step : 1);
- if (ctrlcnt[audiodev] > 0)
- snprintf(mx[index].mx_di.label.name,
- sizeof(mx[index].mx_di.label.name),
- "%s%d",
- hdafg_mixer_names[audiodev],
- ctrlcnt[audiodev] + 1);
- else
- strcpy(mx[index].mx_di.label.name,
- hdafg_mixer_names[audiodev]);
- ctrlcnt[audiodev]++;
switch (audiodev) {
case HDAUDIO_MIXER_VOLUME:
case HDAUDIO_MIXER_BASS:
case HDAUDIO_MIXER_TREBLE:
case HDAUDIO_MIXER_OGAIN:
+ case HDAUDIO_MIXER_SPEAKER:
+ case HDAUDIO_MIXER_PHONEOUT:
mx[index].mx_di.mixer_class =
HDAUDIO_MIXER_CLASS_OUTPUTS;
- hda_trace(sc, " adding outputs.%s\n",
- mx[index].mx_di.label.name);
break;
case HDAUDIO_MIXER_MIC:
case HDAUDIO_MIXER_MONITOR:
mx[index].mx_di.mixer_class =
HDAUDIO_MIXER_CLASS_RECORD;
- hda_trace(sc, " adding record.%s\n",
- mx[index].mx_di.label.name);
break;
default:
mx[index].mx_di.mixer_class =
HDAUDIO_MIXER_CLASS_INPUTS;
- hda_trace(sc, " adding inputs.%s\n",
- mx[index].mx_di.label.name);
break;
}
- strcpy(mx[index].mx_di.un.v.units.name, AudioNvolume);
- ++index;
+ if (ctl->ctl_step > 0) {
+ if (ctrlcnt[audiodev] > 0)
+ snprintf(mx[index].mx_di.label.name,
+ sizeof(mx[index].mx_di.label.name),
+ "%s%d",
+ hdafg_mixer_names[audiodev],
+ ctrlcnt[audiodev] + 1);
+ else
+ strcpy(mx[index].mx_di.label.name,
+ hdafg_mixer_names[audiodev]);
+ ctrlcnt[audiodev]++;
+ mx[index].mx_ctl = ctl;
+ mx[index].mx_di.index = index;
+ mx[index].mx_di.type = AUDIO_MIXER_VALUE;
+ mx[index].mx_di.prev = mx[index].mx_di.next = AUDIO_MIXER_LAST;
+ mx[index].mx_di.un.v.num_channels = 2; /* XXX */
+ mx[index].mx_di.un.v.delta = 256 / ctl->ctl_step;
+ strcpy(mx[index].mx_di.un.v.units.name, AudioNvolume);
+ hda_trace(sc, " adding vol control %s idx %d\n",
+ mx[index].mx_di.label.name, index);
+ ++index;
+ }
if (ctl->ctl_mute) {
- mx[index] = mx[index - 1];
+ if (ctl->ctl_step > 0) {
+ /* there is a parent vol control */
+ mx[index] = mx[index - 1];
+ snprintf(mx[index].mx_di.label.name,
+ sizeof(mx[index].mx_di.label.name),
+ "%s." AudioNmute,
+ mx[index - 1].mx_di.label.name);
+ } else {
+ if (ctrlcnt[audiodev] > 0)
+ snprintf(mx[index].mx_di.label.name,
+ sizeof(mx[index].mx_di.label.name),
+ "%s%d." AudioNmute,
+ hdafg_mixer_names[audiodev],
+ ctrlcnt[audiodev] + 1);
+ else
+ snprintf(mx[index].mx_di.label.name,
+ sizeof(mx[index].mx_di.label.name),
+ "%s." AudioNmute,
+ hdafg_mixer_names[audiodev]);
+ ctrlcnt[audiodev]++;
+ }
+ mx[index].mx_ctl = ctl;
mx[index].mx_di.index = index;
mx[index].mx_di.type = AUDIO_MIXER_ENUM;
mx[index].mx_di.prev = mx[index].mx_di.next = AUDIO_MIXER_LAST;
- snprintf(mx[index].mx_di.label.name,
- sizeof(mx[index].mx_di.label.name),
- "%s." AudioNmute,
- mx[index - 1].mx_di.label.name);
mx[index].mx_di.un.e.num_mem = 2;
strcpy(mx[index].mx_di.un.e.member[0].label.name, AudioNoff);
mx[index].mx_di.un.e.member[0].ord = 0;
strcpy(mx[index].mx_di.un.e.member[1].label.name, AudioNon);
mx[index].mx_di.un.e.member[1].ord = 1;
+ hda_trace(sc, " adding mute control %s idx %d\n",
+ mx[index].mx_di.label.name, index);
++index;
}
}
@@ -4161,11 +4190,7 @@ hdafg_set_port(void *opaque, mixer_ctrl_
switch (mx->mx_di.type) {
case AUDIO_MIXER_VALUE:
- if (ctl->ctl_step == 0)
- divisor = 128; /* ??? - just avoid div by 0 */
- else
- divisor = 255 / ctl->ctl_step;
-
+ divisor = 255 / ctl->ctl_step;
hdafg_control_amp_set(ctl, HDAUDIO_AMP_MUTE_NONE,
mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] / divisor,
mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] / divisor);
@@ -4222,11 +4247,7 @@ hdafg_get_port(void *opaque, mixer_ctrl_
switch (mx->mx_di.type) {
case AUDIO_MIXER_VALUE:
- if (ctl->ctl_step == 0)
- factor = 128; /* ??? - just avoid div by 0 */
- else
- factor = 255 / ctl->ctl_step;
-
+ factor = 255 / ctl->ctl_step;
mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = ctl->ctl_left * factor;
mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = ctl->ctl_right * factor;
break;
--gTKzjOIvDZugVxqG--
State-Changed-From-To: open->analyzed
State-Changed-By: nia@NetBSD.org
State-Changed-When: Sun, 19 Oct 2025 16:42:48 +0000
State-Changed-Why:
From: "Nia Alarie" <nia@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/59712 CVS commit: src/sys/dev/hdaudio
Date: Sun, 19 Oct 2025 17:08:55 +0000
Module Name: src
Committed By: nia
Date: Sun Oct 19 17:08:55 UTC 2025
Modified Files:
src/sys/dev/hdaudio: hdafg.c
Log Message:
hdaudio(4): Do not expose non-functional volume controls.
The old code supported "volume without mute", but not "mute
without volume", which are widely exposed by hdaudio devices.
This resulted in redundant volume controls with delta=256
(not allowing them to be set).
While here, classify PC speaker and phone out controls as
output controls.
"ship it" jmcneill@
PR kern/59712 hdaudio driver exposes nonexistent volume controls
To generate a diff of this commit:
cvs rdiff -u -r1.32 -r1.33 src/sys/dev/hdaudio/hdafg.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/59712 CVS commit: [netbsd-11] src/sys/dev/hdaudio
Date: Tue, 28 Oct 2025 13:33:07 +0000
Module Name: src
Committed By: martin
Date: Tue Oct 28 13:33:07 UTC 2025
Modified Files:
src/sys/dev/hdaudio [netbsd-11]: hdafg.c
Log Message:
Pull up following revision(s) (requested by nia in ticket #76):
sys/dev/hdaudio/hdafg.c: revision 1.33
hdaudio(4): Do not expose non-functional volume controls.
The old code supported "volume without mute", but not "mute
without volume", which are widely exposed by hdaudio devices.
This resulted in redundant volume controls with delta=256
(not allowing them to be set).
While here, classify PC speaker and phone out controls as
output controls.
"ship it" jmcneill@
PR kern/59712 hdaudio driver exposes nonexistent volume controls
To generate a diff of this commit:
cvs rdiff -u -r1.32 -r1.32.4.1 src/sys/dev/hdaudio/hdafg.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
>Unformatted:
(Contact us)
$NetBSD: query-full-pr,v 1.51 2026/08/10 02:28:17 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.