From 1b96bc58cb4ce0ce8f90ec26c97437aaed994001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Sun, 13 Dec 2020 18:53:33 +0200 Subject: alsa: Add udev support Instead of polling for new devices once per second ask udev to send us events. Avoids waking up the CPU all the time and wasting power. We keep the timeout source as a fallback when udev support is not enabled, or the udev setup fails for whatever reason. The logic for udev_event_ok() was snooped from pulseaudio. --- backends/alsa/Makefile.am | 2 + backends/alsa/alsa-backend.c | 136 +++++++++++++++++++++++++++++++++++++++++-- configure.ac | 29 ++++++++- 3 files changed, 161 insertions(+), 6 deletions(-) diff --git a/backends/alsa/Makefile.am b/backends/alsa/Makefile.am index 06e04a7..798ce8b 100644 --- a/backends/alsa/Makefile.am +++ b/backends/alsa/Makefile.am @@ -8,6 +8,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir) \ -DG_LOG_DOMAIN=\"libmatemixer-alsa\" \ $(GLIB_CFLAGS) \ + $(UDEV_CFLAGS) \ $(ALSA_CFLAGS) \ $(NULL) @@ -43,6 +44,7 @@ libmatemixer_alsa_la_SOURCES = \ libmatemixer_alsa_la_LIBADD = \ $(GLIB_LIBS) \ + $(UDEV_LIBS) \ $(ALSA_LIBS) libmatemixer_alsa_la_LDFLAGS = \ diff --git a/backends/alsa/alsa-backend.c b/backends/alsa/alsa-backend.c index 39f67cc..2475385 100644 --- a/backends/alsa/alsa-backend.c +++ b/backends/alsa/alsa-backend.c @@ -15,9 +15,15 @@ * License along with this library; if not, see . */ +#include "config.h" + #include #include #include +#ifdef HAVE_UDEV +#include +#include +#endif #include #include @@ -45,6 +51,15 @@ struct _AlsaBackendPrivate GList *streams; GList *devices; GHashTable *devices_ids; + +#ifdef HAVE_UDEV + struct { + struct udev *udev; + struct udev_monitor *monitor; + guint fd_source; + int fd; + } udev; +#endif }; static void alsa_backend_dispose (GObject *object); @@ -169,15 +184,99 @@ alsa_backend_finalize (GObject *object) G_OBJECT_CLASS (alsa_backend_parent_class)->finalize (object); } +#ifdef HAVE_UDEV +static gboolean udev_event_ok (struct udev_device *dev) +{ + const char *action = udev_device_get_action (dev); + + if (action && !strcmp (action, "remove")) + return TRUE; + + /* + * See /lib/udev/rules.d/78-sound-card.rules + * why "add" events are not handled here. + */ + if ((!action || !strcmp (action, "change")) && + udev_device_get_property_value (dev, "SOUND_INITIALIZED")) + return TRUE; + + return FALSE; +} + static gboolean -alsa_backend_open (MateMixerBackend *backend) +udev_monitor_cb (gint fd, + GIOCondition condition, + gpointer user_data) { - AlsaBackend *alsa; + AlsaBackend *alsa = user_data; + struct udev_device *dev; - g_return_val_if_fail (ALSA_IS_BACKEND (backend), FALSE); + dev = udev_monitor_receive_device (alsa->priv->udev.monitor); + if (!dev) + return TRUE; - alsa = ALSA_BACKEND (backend); + if (!udev_event_ok (dev)) { + udev_device_unref (dev); + return TRUE; + } + + read_devices (alsa); + + udev_device_unref (dev); + + return TRUE; +} + +static gboolean +udev_monitor_setup (AlsaBackend *alsa) +{ + alsa->priv->udev.udev = udev_new (); + if (!alsa->priv->udev.udev) + return FALSE; + + alsa->priv->udev.monitor = udev_monitor_new_from_netlink (alsa->priv->udev.udev, "udev"); + if (!alsa->priv->udev.monitor) { + udev_unref (alsa->priv->udev.udev); + alsa->priv->udev.udev = NULL; + return FALSE; + } + + if (udev_monitor_filter_add_match_subsystem_devtype (alsa->priv->udev.monitor, "sound", NULL) < 0 || + udev_monitor_enable_receiving (alsa->priv->udev.monitor) < 0) { + udev_monitor_unref (alsa->priv->udev.monitor); + udev_unref (alsa->priv->udev.udev); + alsa->priv->udev.udev = NULL; + return FALSE; + } + + alsa->priv->udev.fd = udev_monitor_get_fd (alsa->priv->udev.monitor); + if (alsa->priv->udev.fd < 0) { + udev_monitor_unref (alsa->priv->udev.monitor); + udev_unref (alsa->priv->udev.udev); + alsa->priv->udev.udev = NULL; + return FALSE; + } + alsa->priv->udev.fd_source = g_unix_fd_add (alsa->priv->udev.fd, G_IO_IN, udev_monitor_cb, alsa); + + return TRUE; +} + +static void +udev_monitor_cleanup (AlsaBackend *alsa) +{ + if (!alsa->priv->udev.udev) + return; + + g_source_remove (alsa->priv->udev.fd_source); + udev_monitor_unref (alsa->priv->udev.monitor); + udev_unref (alsa->priv->udev.udev); +} +#endif + +static void +timeout_source_setup (AlsaBackend *alsa) +{ /* Poll ALSA for changes every second, this only discovers added or removed * sound cards, sound card related events are handled by AlsaDevices */ alsa->priv->timeout_source = g_timeout_source_new_seconds (1); @@ -187,6 +286,30 @@ alsa_backend_open (MateMixerBackend *backend) NULL); g_source_attach (alsa->priv->timeout_source, g_main_context_get_thread_default ()); +} + +static void +timeout_source_cleanup (AlsaBackend *alsa) +{ + if (!alsa->priv->timeout_source) + return; + + g_source_destroy (alsa->priv->timeout_source); +} + +static gboolean +alsa_backend_open (MateMixerBackend *backend) +{ + AlsaBackend *alsa; + + g_return_val_if_fail (ALSA_IS_BACKEND (backend), FALSE); + + alsa = ALSA_BACKEND (backend); + +#ifdef HAVE_UDEV + if (!udev_monitor_setup (alsa)) +#endif + timeout_source_setup (alsa); /* Read the initial list of devices so we have some starting point, there * isn't really a way to detect errors here, failing to add a device may @@ -206,7 +329,10 @@ alsa_backend_close (MateMixerBackend *backend) alsa = ALSA_BACKEND (backend); - g_source_destroy (alsa->priv->timeout_source); + timeout_source_cleanup (alsa); +#ifdef HAVE_UDEV + udev_monitor_cleanup (alsa); +#endif if (alsa->priv->devices != NULL) { g_list_free_full (alsa->priv->devices, g_object_unref); diff --git a/configure.ac b/configure.ac index 3de0a6b..6081590 100644 --- a/configure.ac +++ b/configure.ac @@ -184,11 +184,38 @@ if test "x$enable_alsa" != "xno"; then fi fi +AC_ARG_ENABLE([udev], + AS_HELP_STRING([--enable-udev], + [Enable UDEV support @<:@default=auto@:>@]), + enable_udev=$enableval, + enable_udev=auto) + +have_udev=no +if test "x$enable_udev" != "xno" -a \ + "x$have_alsa" = "xyes"; then + PKG_CHECK_MODULES(UDEV, [libudev], + have_udev=yes, + have_udev=no) + if test "x$have_udev" = "xyes"; then + AC_DEFINE(HAVE_UDEV, [], [Define if we have UDEV support]) + else + if test "x$enable_udev" = "xyes"; then + AC_MSG_ERROR([UDEV support explicitly requested but dependencies not found]) + else + AC_MSG_NOTICE([UDEV dependencies not found, the module will not be built]) + fi + fi +fi + AM_CONDITIONAL(HAVE_ALSA, test "x$have_alsa" = "xyes") +AM_CONDITIONAL(HAVE_UDEV, test "x$have_udev" = "xyes") AC_SUBST(HAVE_ALSA) AC_SUBST(ALSA_CFLAGS) AC_SUBST(ALSA_LIBS) +AC_SUBST(HAVE_UDEV) +AC_SUBST(UDEV_CFLAGS) +AC_SUBST(UDEV_LIBS) # ----------------------------------------------------------------------- # OSS @@ -269,6 +296,6 @@ echo " Build Null module: $have_null Build PulseAudio module: $have_pulseaudio - Build ALSA module: $have_alsa + Build ALSA module: $have_alsa (udev: $have_udev) Build OSS module: $have_oss " -- cgit v1.2.1