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

Reply via email to