perri.to: Un mejunje de cosas

Linux en una ThinkPad X1 Gen4

  2023-12-09


Conseguí una nueva laptop vieja

Esta laptop es bastante “vieja” comparada con el hardware nuevo y reluciente de hoy, pero cumple con todo lo que necesito.

  • Era barata (para mí, era excedente de una empresa)
  • Tiene un lindo procesador 11th Gen Intel(R) Core(TM) i7-11850H @ 2.50GHz
  • Tiene un montón de RAM (16G y un slot libre)
  • Este modelo, o uno bastante parecido, está certificado para Ubuntu (esto significa que un equipo en Ubuntu se tomó el tiempo de verificar que cada versión de su sistema funcione en estas máquinas e incluso hizo cambios en su kernel oem para acomodar las rarezas que trae que Lenovo sea demasiado liberal con la interpretación de los estándares)

Live USB

La imagen Live de Ubuntu es hermosa y funciona de una con todo lo que mi laptop tiene para ofrecer (23.10 al momento de escribir esto). Ahora, acá es donde empieza la diversión.

Mi laptop tiene una tecla de silencio que tiene un led; el led se enciende cuando la laptop está silenciada, y tiene lo mismo en la de silenciar el micrófono.

Esto funciona sin fallas en la imagen live, pero una vez instalada solo funciona la luz de silencio del micrófono.

Ahora bien: sí que le saqué snapd a Ubuntu, así que puede que haya alterado el flujo normal de las cosas.

Después de leer un poco, no pude entender por qué esto no funcionaba; el mecanismo del kernel que lo hace funcionar está ahí e incluso puedo disparar el led a mano:

# This turns on the led
echo 1 | sudo tee /sys/class/leds/platform\:\:mute/brightness

Pasé unas horas probando varios arreglos e intentando entender qué era distinto en la imagen live, sin éxito; la gente que arma esa imagen es mucho más inteligente que yo.

La solución barata

Resulta que te podés suscribir a pulse audio y escuchar eventos, así que escribí un pequeño daemon de systemd para encender/apagar el led.

Para esto primero tuve que agregar una regla de udev para hacerlo escribible sin sudo.

Coloqué este archivo en /etc/udev/rules.d/99-mute_led.rules

ACTION=="add", SUBSYSTEM=="leds", KERNEL=="platform::mute", RUN+="/bin/chmod a+w /sys/class/leds/%k/brightness"

Después escribí un script de python que hacía la suscripción a pulse; usé una biblioteca práctica

requirements.txt

pulsectl==23.5.2
#!/usr/bin/env python

import time
from pulsectl import Pulse, PulseLoopStop

# This is the name for my laptop, a Thinkpad X1 Extreme Gen 4
SINK_NAME="alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__hw_sofhdadsp__sink"

def get_sink_number(pulse):
    """
    return the index for the sink we care about
    """
    return pulse.get_sink_by_name(SINK_NAME).index

def set_led_brightness(sink_number, is_muted):
    """
    set the status of the led according to the mute status
    """
    # Determine the LED brightness value based on mute status
    brightness = 0 if not is_muted else 1

    with open('/sys/class/leds/platform::mute/brightness', 'w') as led:
        led.write(f'{brightness}')

def main():
    with Pulse("mute-led-listener") as pulse:
        sink_number = get_sink_number(pulse)

        def mute_button(ev):
            """
            callback to be invoked every time an event matches the set match
            we only raise a loop stop exception to trigger the event loop to
            exit, we cannot perform pulse operations while the loop is running.

            """
            if ev.index == sink_number:
                raise PulseLoopStop


        pulse.event_mask_set("sink")
        pulse.event_callback_set(mute_button)
        while True:
            # Lock until we have an event match.
            pulse.event_listen()
            # Get mute status for our sink
            is_muted = pulse.get_sink_by_name(SINK_NAME).mute == 1

            # Set LED brightness based on mute status
            set_led_brightness(sink_number, is_muted)
            # Then we will begin the listener again and only trigger next time button is pressed



if __name__ == "__main__":
    main()

Nota: Verificá si el nombre del dispositivo te sirve; podés chequear esto usando la herramienta pactl.

Y até todo junto con un daemon de systemd

$HOME/.local/share/systemd/user/led-control.service

# $HOME/.local/share/systemd/user/led-control.service

[Unit]
Description=LED Control Service

[Service]
Type=simple
ExecStart=/home/hduran/vimconfig/thinkpad/venv/bin/python /home/hduran/vimconfig/thinkpad/mute_led_helper.py

[Install]
WantedBy=default.target

Nota: La ruta de python es un poco rara porque creé un virtualenv para esto (deberías hacer lo mismo si querés instalar este paquete, no instalarlo a nivel del sistema)

Después de esto necesitás correr:

systemctl --user enable led-control.service systemctl --user start led-control.service