On Tue, Sep 13, 2016 at 10:48:26AM -0600, Theo de Raadt wrote: > > Anton Lindqvist wrote: > > > I'm trying to fix a minor annoyance on my x240: the speaker mute key > > > LED-state is not respected at boot. Pressing the mute key will mute the > > > speaker while the expected behavior is to unmute. The LED-state will > > > remain out-of-sync until I run `mixerctl -t outputs.master.mute`. > > > > > > I've managed to determine if the speaker is muted in the acpithinkpad(4) > > > attach function by reading from the embedded controller. However, > > > calling wskbd_set_mixervolume at this stage returns ENODEV. I assume the > > > audio device has not been attached yet. I'm new to kernel development > > > and therefore wonder if this approach makes sense. If true, is it > > > possible to postpone a task to run once a certain device has attached? > > > > The function you're looking for is startuphook_establish. > > > > But this requires crazy amounts of testing, since many thinkpads will be > > different. It's well known that the mute button, hardware state, and > > software > > state can became desynced, but the specifics vary by model. > > > > Actually, I think Anton might be onto something here. The desyncronization > may be due to the audio driver + BIOS not recognizing the other's state.
By using startuphook_establish, as proposed by tedu@, I managed to get it working. Booting with the volume muted before reboot and the volume not muted before reboot both behaves correctly. A few comments regarding the patch: - At first, I tried sticking all code in the startuphook function because it felt like a good approach to keep all the logic in one place and reduce the number of ifdef-conditionals. However, that caused my kernel to halt since the EC is busy at this point and the assertion in acpiec.c:337 failed. The only other usage of acpiec_read I can find is related to polling the sensors. I therefore kept volume reading from the EC inside the attached function and moved it prior attaching the sensors. The kernel then continued successfully, but I'm still worried a that potential race still can occur. Or is the action taken enough to make the volume EC read atomic? - I didn't bother checking the return value of startuphook_establish since I couldn't find any other invocation in the kernel doing so. - The VOLUME_MUTE_MASK is taken from the FreeBSD ThinkPad ACPI driver. If the patch looks good, then I would like to add some logging prior asking people to try it out. Index: acpithinkpad.c =================================================================== RCS file: /cvs/src/sys/dev/acpi/acpithinkpad.c,v retrieving revision 1.52 diff -u -p -r1.52 acpithinkpad.c --- acpithinkpad.c 5 May 2016 05:12:49 -0000 1.52 +++ acpithinkpad.c 14 Sep 2016 07:38:08 -0000 @@ -16,6 +16,7 @@ */ #include <sys/param.h> +#include <sys/types.h> #include <sys/systm.h> #include <dev/acpi/acpireg.h> @@ -105,6 +106,7 @@ #define THINKPAD_NSENSORS 9 #define THINKPAD_NTEMPSENSORS 8 +#define THINKPAD_ECOFFSET_VOLUME 0x30 #define THINKPAD_ECOFFSET_FANLO 0x84 #define THINKPAD_ECOFFSET_FANHI 0x85 @@ -163,6 +165,7 @@ void thinkpad_sensor_attach(struct ac void thinkpad_sensor_refresh(void *); #if NAUDIO > 0 && NWSKBD > 0 +void thinkpad_attach_deferred(void *); extern int wskbd_set_mixervolume(long, long); #endif @@ -252,12 +255,24 @@ thinkpad_attach(struct device *parent, s { struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self; struct acpi_attach_args *aa = aux; + uint8_t vol = 0; sc->sc_acpi = (struct acpi_softc *)parent; sc->sc_devnode = aa->aaa_node; printf("\n"); +#if NAUDIO > 0 && NWSKBD > 0 + if (sc->sc_acpi->sc_ec != NULL) { + acpiec_read(sc->sc_acpi->sc_ec, THINKPAD_ECOFFSET_VOLUME, + 1, &vol); +#define VOLUME_MUTE_MASK 0x40 + if ((vol & VOLUME_MUTE_MASK) == VOLUME_MUTE_MASK) + /* Defer speaker mute */ + startuphook_establish(thinkpad_attach_deferred, sc); + } +#endif + /* Set event mask to receive everything */ thinkpad_enable_events(sc); thinkpad_sensor_attach(sc); @@ -722,3 +737,11 @@ thinkpad_set_param(struct wsdisplay_para return -1; } } + +#if NAUDIO > 0 && NWSKBD > 0 +void +thinkpad_attach_deferred(void *v __unused) +{ + wskbd_set_mixervolume(0, 1); +} +#endif