libudev-zero: enumerar devices de CLASE para una lista ACOTADA de subsistemas. EL HUECO, medido en las fuentes de los dos lados. `udev_enumerate_scan_devices` recorre **exclusivamente** `/sys/dev/block` y `/sys/dev/char` (udev_enumerate.c:281), o sea sólo devices que tienen `major:minor` y por tanto nodo en /dev. El udev real recorre `/sys/class` entero. La diferencia es invisible para casi todos los consumidores y letal para los que piden un device de CLASE. CÓMO SE DESTAPÓ: el audio. `spa/plugins/alsa/alsa-udev.c:178-183` de PipeWire pide el device **card** —`/sys/devices/.../sound/card0`—, que es de clase: sin major:minor, sin nodo. Con ALSA encendido en el kernel, `/dev/snd/controlC0` presente y `/sys/class/sound` listando `card0`, `wpctl status` daba `Devices:` vacío **porque card0 nunca entraba en la enumeración**. No era una propiedad que faltara: faltaba la mitad del espacio de búsqueda. ⚠ POR QUÉ UNA LISTA Y NO `/sys/class` ENTERO — se probó global y ROMPIÓ EL VÍDEO La primera versión de este parche recorría todo `/sys/class`. El audio pasó a funcionar (`48. HDA Intel [alsa]`, sink y source reales en vez de `auto_null`) **y la pantalla se puso negra**: libmutter-WARNING: Failed to use linear monitor configuration: No available CRTC for monitor 'RHT QEMU Monitor' not found libmutter-WARNING: Page flip failed: drmModeAtomicCommit: Invalid argument (en bucle) `/sys/class/drm` no contiene sólo las tarjetas: contiene los CONECTORES (`card0-Virtual-1`, `card1-eDP-1`…) y un fichero `version`, ninguno con nodo. Al aparecer en la enumeración, mutter los toma como candidatos y no encuentra CRTC. **El udev real también los enumera** — pero además les adjunta las propiedades por las que mutter los descarta, y ésas libudev-zero no las sintetiza (su tabla es fija y está orientada a input: `ID_INPUT*`, `EV`, `KEY`, `ABS`, `REL`). La lección, que vale para cualquier reimplementación parcial de una API: **ensanchar la enumeración expone los huecos de PROPIEDADES a más consumidores.** Los dos lados del contrato tienen que crecer juntos. Así que se agrega subsistema por subsistema, con un caso de uso medido detrás de cada uno. Hoy: `sound`. Agregar otro es una línea en `class_subsystems[]` **y una prueba de que el consumidor lo necesita y no se rompe**. POR QUÉ ESTO Y NO eudev, y acá el repo ya tenía la respuesta escrita: empaquetar eudev se probó en otro frente y **rompió el input** — su `libudev.so.1` pisa el de libudev-zero y, SIN udevd corriendo, eudev no expone `ID_INPUT`, así que libinput ignora TODOS los dispositivos EN SILENCIO (`udev_assign_seat` devuelve OK con 0 devices). Se resolvió restaurando libudev-zero, con verificación A/B en QEMU: eudev = 0 «New device», libudev-zero = 5. Ver [[mirada-usb-nvidia]]. eudev sin udevd es PEOR, y correr udevd contradice la arquitectura (arje-zero es PID1, sin udevd en el camino). ⚠ COSTO: `yupana radio libudev-zero` = **51 artefactos sellados cayendo a deuda** en las cinco imágenes (9 en el cierre GNOME). Una variante NO era opción: dos `libudev.so.1` en el mismo rootfs es exactamente el choque del episodio de eudev. diff -Naur a/udev_enumerate.c b/udev_enumerate.c --- a/udev_enumerate.c +++ b/udev_enumerate.c @@ -276,6 +276,37 @@ return 1; } +/* Enumera devices de CLASE (los que no tienen major:minor y por tanto no aparecen bajo + * /sys/dev/{block,char}) para una LISTA ACOTADA de subsistemas. + * + * Por que una lista y no /sys/class entero: se probo global y ROMPIO EL VIDEO. /sys/class/drm no + * contiene solo las tarjetas: contiene los CONECTORES (card0-Virtual-1, card1-eDP-1...) y un fichero + * `version`, ninguno con nodo. Al aparecer en la enumeracion, mutter los trata como candidatos y + * termina en `No available CRTC for monitor` + `Page flip failed: drmModeAtomicCommit: Invalid + * argument` en bucle: pantalla negra con el compositor vivo. El udev real tambien los enumera, pero + * ademas les adjunta las propiedades por las que mutter los descarta, y esas propiedades libudev-zero + * no las sintetiza. O sea: ensanchar la enumeracion expone los huecos de PROPIEDADES a mas + * consumidores. + * + * Asi que se agrega subsistema por subsistema, con un caso de uso medido detras de cada uno: + * sound -> PipeWire pide el device `card` de la tarjeta (alsa-udev.c:178-183), que no tiene nodo. + * Sin esto `wpctl status` da `Devices:` vacio con ALSA funcionando y /dev/snd poblado. + */ +static const char *const class_subsystems[] = { "sound", NULL }; + +static int scan_class(struct udev_enumerate *udev_enumerate) +{ + char subsystem_path[PATH_MAX]; + int i; + + for (i = 0; class_subsystems[i]; i++) { + snprintf(subsystem_path, sizeof(subsystem_path), "/sys/class/%s", class_subsystems[i]); + scan_devices(udev_enumerate, subsystem_path); + } + + return 1; +} + int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) { const char *path[] = { "/sys/dev/block", "/sys/dev/char", NULL }; @@ -291,6 +322,8 @@ } } + scan_class(udev_enumerate); + return 0; }