summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVille Syrjälä <[email protected]>2020-12-13 18:53:33 +0200
committerraveit65 <[email protected]>2020-12-26 23:36:11 +0100
commit1b96bc58cb4ce0ce8f90ec26c97437aaed994001 (patch)
treef601dd7d828d6067f3a1ccb2a88cff1026cc1fbe
parent31da9e1847a5da12c9d81386a29c647908974227 (diff)
downloadlibmatemixer-1b96bc58cb4ce0ce8f90ec26c97437aaed994001.tar.bz2
libmatemixer-1b96bc58cb4ce0ce8f90ec26c97437aaed994001.tar.xz
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.
-rw-r--r--backends/alsa/Makefile.am2
-rw-r--r--backends/alsa/alsa-backend.c136
-rw-r--r--configure.ac29
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 <http://www.gnu.org/licenses/>.
*/
+#include "config.h"
+
#include <glib.h>
#include <glib-object.h>
#include <alsa/asoundlib.h>
+#ifdef HAVE_UDEV
+#include <libudev.h>
+#include <glib-unix.h>
+#endif
#include <libmatemixer/matemixer.h>
#include <libmatemixer/matemixer-private.h>
@@ -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
"