From f66610d26395de15e3928cf4284573ca9cfabf26 Mon Sep 17 00:00:00 2001
From: Arnaud Ferraris <arnaud.ferraris@collabora.com>
Date: Fri, 6 Mar 2020 02:01:52 +0100
Subject: [PATCH] Simplify daemon to only switch card profiles

As a PoC for the PinePhone, we want to only switch card profiles:
- when a call starts, use the "Voice Call" profile: audio is output on
the earpiece, and the internal microphone is unmuted
- when a call is terminated, switch back to the default "HiFi" profile,
using the main speaker

This should fully cover the most basic use-case. Headset will not work,
and there is also no support for a "silent mode", but this is enough to
demonstrate voice calls for the PinePhone.
---
 machine-check/blacklist                       |    1 -
 machine-conf/Purism Librem 5 devkit/codec     |    1 -
 machine-conf/Purism Librem 5 devkit/modem     |    1 -
 machine-conf/Purism Librem 5/codec            |    1 -
 machine-conf/Purism Librem 5/modem            |    1 -
 meson.build                                   |   16 -
 src/main.c                                    |  363 ++---
 src/meson.build                               |   10 -
 src/wys-audio.c                               | 1259 -----------------
 src/wys-audio.h                               |   47 -
 src/wys-direction.c                           |   40 -
 src/wys-direction.h                           |   42 -
 src/wys-modem.c                               |  138 +-
 .../libmachine-check/mchk-machine-check.c     |  328 -----
 .../libmachine-check/mchk-machine-check.h     |   38 -
 subprojects/libmachine-check/meson.build      |   81 --
 18 files changed, 146 insertions(+), 2228 deletions(-)
 delete mode 100644 machine-check/blacklist
 delete mode 100644 machine-conf/Purism Librem 5 devkit/codec
 delete mode 100644 machine-conf/Purism Librem 5 devkit/modem
 delete mode 100644 machine-conf/Purism Librem 5/codec
 delete mode 100644 machine-conf/Purism Librem 5/modem
 delete mode 100644 src/wys-audio.c
 delete mode 100644 src/wys-audio.h
 delete mode 100644 src/wys-direction.c
 delete mode 100644 src/wys-direction.h
 delete mode 100644 subprojects/libmachine-check/mchk-machine-check.c
 delete mode 100644 subprojects/libmachine-check/mchk-machine-check.h
 delete mode 100644 subprojects/libmachine-check/meson.build

--- a/machine-check/blacklist
+++ /dev/null
@@ -1 +0,0 @@
-Purism Librem 5 devkit
diff --git a/machine-conf/Purism Librem 5 devkit/codec b/machine-conf/Purism Librem 5 devkit/codec
deleted file mode 100644
index 7af7303..0000000
--- a/machine-conf/Purism Librem 5 devkit/codec	
+++ /dev/null
@@ -1 +0,0 @@
-sgtl5000
diff --git a/machine-conf/Purism Librem 5 devkit/modem b/machine-conf/Purism Librem 5 devkit/modem
deleted file mode 100644
index b10552d..0000000
--- a/machine-conf/Purism Librem 5 devkit/modem	
+++ /dev/null
@@ -1 +0,0 @@
-SIMCom SIM7100
diff --git a/machine-conf/Purism Librem 5/codec b/machine-conf/Purism Librem 5/codec
deleted file mode 100644
index 6d39251..0000000
--- a/machine-conf/Purism Librem 5/codec	
+++ /dev/null
@@ -1 +0,0 @@
-wm8962
diff --git a/machine-conf/Purism Librem 5/modem b/machine-conf/Purism Librem 5/modem
deleted file mode 100644
index d9b0f2a..0000000
--- a/machine-conf/Purism Librem 5/modem	
+++ /dev/null
@@ -1 +0,0 @@
-MODEM
diff --git a/meson.build b/meson.build
index 5938cec..7516208 100644
--- a/meson.build
+++ b/meson.build
@@ -33,11 +33,6 @@ project (
     ],
 )
 
-
-libmchk_proj = subproject('libmachine-check')
-libmchk_dep = libmchk_proj.get_variable('libmachine_check_dep')
-
-
 app_name = meson.project_name()
 
 prefix = get_option('prefix')
@@ -63,14 +58,3 @@ config_data.set_quoted('DATADIR', full_datadir)
 config_data.set_quoted('SYSCONFDIR', full_sysconfdir)
 
 subdir('src')
-
-install_subdir (
-  'machine-conf',
-  install_dir : join_paths(datadir, app_name)
-)
-
-install_subdir (
-  'machine-check',
-  install_dir : join_paths(datadir, 'machine-check', app_name),
-  strip_directory : true
-)
diff --git a/src/main.c b/src/main.c
index 587a5f3..4631a33 100644
--- a/src/main.c
+++ b/src/main.c
@@ -23,10 +23,8 @@
  */
 
 #include "wys-modem.h"
-#include "wys-audio.h"
 #include "util.h"
 #include "config.h"
-#include "mchk-machine-check.h"
 
 #include <libmm-glib.h>
 #include <glib.h>
@@ -50,8 +48,6 @@ static GMainLoop *main_loop = NULL;
 
 struct wys_data
 {
-  /** PulseAudio interface */
-  WysAudio *audio;
   /** ID for the D-Bus watch */
   guint watch_id;
   /** ModemManager object proxy */
@@ -59,51 +55,112 @@ struct wys_data
   /** Map of D-Bus object paths to WysModems */
   GHashTable *modems;
   /** How many modems have audio, in each direction */
-  guint audio_count[2];
+  guint audio_count;
 };
 
+static gboolean
+ugly_system (const gchar *cmd)
+{
+  gchar *out = NULL, *err = NULL;
+  gint status;
+  GError *error = NULL;
+  gboolean ok;
+
+  g_debug ("Executing command `%s'", cmd);
+
+  ok = g_spawn_command_line_sync
+    (cmd, &out, &err, &status, &error);
+
+  if (!ok)
+    {
+      g_warning ("Error spawning command `%s': %s'",
+                 cmd, error->message);
+      g_error_free (error);
+      return FALSE;
+    }
+
+  ok = g_spawn_check_exit_status (status, &error);
+  if (ok)
+    {
+      g_debug ("Command `%s' executed successfully"
+               "; stdout: `%s'; stderr: `%s'",
+               cmd, out, err);
+    }
+  else
+    {
+      g_warning ("Command `%s' failed: %s"
+                 "; stdout: `%s'; stderr: `%s'",
+                 cmd, error->message, out, err);
+      g_error_free (error);
+    }
+
+  g_free (out);
+  g_free (err);
+
+  return ok;
+}
+
+#define UGLY_CARD "alsa_card.platform-sound"
+
+static gboolean
+ugly_set_card_profile (const gchar *card,
+                       const gchar *profile)
+{
+  g_autofree gchar *cmd = NULL;
+
+  cmd = g_strdup_printf ("pactl set-card-profile '%s' '%s'", card, profile);
+
+  return ugly_system (cmd);
+}
+
+static gboolean
+ugly_set_voice_call (void)
+{
+  return ugly_set_card_profile (UGLY_CARD, "Voice Call");
+}
+
+static gboolean
+ugly_set_hifi (void)
+{
+  return ugly_set_card_profile (UGLY_CARD, "HiFi");
+}
 
 static void
 update_audio_count (struct wys_data *data,
-                    WysDirection     direction,
                     gint             delta)
 {
-  const guint old_count = data->audio_count[direction];
+  const guint old_count = data->audio_count;
 
-  g_assert (delta >= 0 || data->audio_count[direction] > 0);
+  g_assert (delta >= 0 || data->audio_count > 0);
 
-  data->audio_count[direction] += delta;
+  data->audio_count += delta;
 
-  if (data->audio_count[direction] > 0 && old_count == 0)
+  if (data->audio_count > 0 && old_count == 0)
     {
-      g_debug ("Audio %s now present",
-               wys_direction_get_description (direction));
-      wys_audio_ensure_loopback (data->audio, direction);
+      g_message ("Audio now present");
+      ugly_set_voice_call ();
     }
-  else if (data->audio_count[direction] == 0 && old_count > 0)
+  else if (data->audio_count == 0 && old_count > 0)
     {
-      g_debug ("Audio %s now absent",
-               wys_direction_get_description (direction));
-      wys_audio_ensure_no_loopback (data->audio, direction);
+      g_message ("Audio now absent");
+      ugly_set_hifi ();
     }
 }
 
 
 static void
 audio_present_cb (struct wys_data *data,
-                  WysDirection     direction,
                   WysModem        *modem)
 {
-  update_audio_count (data, direction, +1);
+  update_audio_count (data, +1);
 }
 
 
 static void
 audio_absent_cb (struct wys_data *data,
-                 WysDirection     direction,
                  WysModem        *modem)
 {
-  update_audio_count (data, direction, -1);
+  update_audio_count (data, -1);
 }
 
 
@@ -123,7 +180,7 @@ add_modem (struct wys_data *data,
       return;
     }
 
-  g_debug ("Adding new voice-capable modem `%s'", path);
+  g_message ("Adding new voice-capable modem `%s'", path);
 
   g_assert (MM_IS_OBJECT (object));
   voice = mm_object_get_modem_voice (MM_OBJECT (object));
@@ -153,7 +210,7 @@ interface_added_cb (struct wys_data *data,
 
   info = g_dbus_interface_get_info (interface);
 
-  g_debug ("ModemManager interface `%s' found on object `%s'",
+  g_message ("ModemManager interface `%s' found on object `%s'",
            info->name,
            g_dbus_object_get_object_path (object));
 
@@ -184,7 +241,7 @@ interface_removed_cb (struct wys_data *data,
   path = g_dbus_object_get_object_path (object);
   info = g_dbus_interface_get_info (interface);
 
-  g_debug ("ModemManager interface `%s' removed on object `%s'",
+  g_message ("ModemManager interface `%s' removed on object `%s'",
            info->name, path);
 
   if (g_strcmp0 (info->name, MM_DBUS_INTERFACE_MODEM_VOICE) == 0)
@@ -229,7 +286,7 @@ void
 object_added_cb (struct wys_data *data,
                  GDBusObject     *object)
 {
-  g_debug ("ModemManager object `%s' added",
+  g_message ("ModemManager object `%s' added",
            g_dbus_object_get_object_path (object));
 
   add_mm_object (data, object);
@@ -243,7 +300,7 @@ object_removed_cb (struct wys_data *data,
   const gchar *path;
 
   path = g_dbus_object_get_object_path (object);
-  g_debug ("ModemManager object `%s' removed", path);
+  g_message ("ModemManager object `%s' removed", path);
 
   remove_modem_object (data, path, object);
 }
@@ -286,7 +343,7 @@ mm_appeared_cb (GDBusConnection *connection,
                 const gchar *name_owner,
                 struct wys_data *data)
 {
-  g_debug ("ModemManager appeared on D-Bus");
+  g_message ("ModemManager appeared on D-Bus");
 
   mm_manager_new (connection,
                   G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
@@ -310,18 +367,14 @@ mm_vanished_cb (GDBusConnection *connection,
                 const gchar *name,
                 struct wys_data *data)
 {
-  g_debug ("ModemManager vanished from D-Bus");
+  g_message ("ModemManager vanished from D-Bus");
   clear_dbus (data);
 }
 
 
 static void
-set_up (struct wys_data *data,
-        const gchar *codec,
-        const gchar *modem)
+set_up (struct wys_data *data)
 {
-  data->audio = wys_audio_new (codec, modem);
-
   data->modems = g_hash_table_new_full (g_str_hash, g_str_equal,
                                         g_free, g_object_unref);
 
@@ -333,7 +386,7 @@ set_up (struct wys_data *data,
                       (GBusNameVanishedCallback)mm_vanished_cb,
                       data, NULL);
 
-  g_debug ("Watching for ModemManager");
+  g_message ("Watching for ModemManager");
 }
 
 
@@ -343,23 +396,19 @@ tear_down (struct wys_data *data)
   clear_dbus (data);
   g_bus_unwatch_name (data->watch_id);
   g_hash_table_unref (data->modems);
-  g_object_unref (G_OBJECT (data->audio));
 }
 
 
 static void
-run (const gchar *codec,
-     const gchar *modem)
+run (void)
 {
   struct wys_data data;
 
   memset (&data, 0, sizeof (struct wys_data));
-  set_up (&data, codec, modem);
+  set_up (&data);
 
   main_loop = g_main_loop_new (NULL, FALSE);
 
-  printf (APPLICATION_NAME " started with codec `%s', modem `%s'\n",
-          codec, modem);
   g_main_loop_run (main_loop);
 
   g_main_loop_unref (main_loop);
@@ -368,33 +417,6 @@ run (const gchar *codec,
   tear_down (&data);
 }
 
-
-static void
-check_machine (const gchar *machine)
-{
-  gboolean ok, passed;
-  GError *error = NULL;
-
-  ok = mchk_check_machine (APP_DATA_NAME,
-                           machine,
-                           &passed,
-                           &error);
-  if (!ok)
-    {
-      g_warning ("Error checking machine name against"
-                 " whitelist/blacklist, continuing anyway");
-      g_error_free (error);
-    }
-  else if (!passed)
-    {
-      g_message ("Machine name `%s' did not pass"
-                 " whitelist/blacklist check, exiting",
-                 machine);
-      exit (EXIT_SUCCESS);
-    }
-}
-
-
 static void
 terminate (int signal)
 {
@@ -429,221 +451,18 @@ setup_signals ()
 #undef try_setup
 }
 
-
-/** This function will close @fd */
-static gchar *
-read_machine_conf_file (const gchar *filename,
-                        int          fd)
-{
-  GInputStream *unix_stream;
-  GDataInputStream *data_stream;
-  gboolean try_again;
-  gchar *line;
-  GError *error = NULL;
-
-  g_debug ("Reading machine configuration file `%s'", filename);
-
-  unix_stream = g_unix_input_stream_new (fd, TRUE);
-  g_assert (unix_stream != NULL);
-
-  data_stream = g_data_input_stream_new (unix_stream);
-  g_assert (data_stream != NULL);
-  g_object_unref (unix_stream);
-
-  do
-    {
-      try_again = FALSE;
-
-      line = g_data_input_stream_read_line_utf8
-        (data_stream, NULL, NULL, &error);
-
-      if (error)
-        {
-          g_warning ("Error reading from machine"
-                     " configuration file `%s': %s",
-                     filename, error->message);
-          g_error_free (error);
-        }
-      else if (line)
-        {
-          g_strstrip (line);
-
-          // Skip comments and empty lines
-          if (line[0] == '#' || line[0] == '\0')
-            {
-              g_free (line);
-              try_again = TRUE;
-            }
-        }
-    }
-  while (try_again);
-
-  g_object_unref (data_stream);
-  return line;
-}
-
-
-static gchar *
-dir_machine_conf (const gchar *dir,
-                  const gchar *machine,
-                  const gchar *key)
-{
-  gchar *filename;
-  int fd;
-  gchar *value = NULL;
-
-  filename = g_build_filename (dir, APP_DATA_NAME,
-                               "machine-conf",
-                               machine, key, NULL);
-
-  g_debug ("Trying machine configuration file `%s'",
-           filename);
-
-  fd = g_open (filename, O_RDONLY, 0);
-  if (fd == -1)
-    {
-      if (errno != ENOENT)
-        {
-          // The error isn't that the file doesn't exist
-          g_warning ("Error opening machine"
-                     " configuration file `%s': %s",
-                     filename, g_strerror (errno));
-        }
-    }
-  else
-    {
-      value = read_machine_conf_file (filename, fd);
-    }
-
-  g_free (filename);
-  return value;
-}
-
-
-static gchar *
-machine_conf (const gchar *machine,
-              const gchar *key)
-{
-  gchar *value = NULL;
-  const gchar * const *dirs, * const *dir;
-
-
-#define try_dir(d)                                      \
-  value = dir_machine_conf (d, machine, key);           \
-  if (value)                                            \
-    {                                                   \
-      return value;                                     \
-    }
-
-
-  try_dir (g_get_user_config_dir ());
-
-  dirs = g_get_system_config_dirs ();
-  for (dir = dirs; *dir; ++dir)
-    {
-      try_dir (*dir);
-    }
-
-  try_dir (SYSCONFDIR);
-  try_dir (DATADIR);
-
-  dirs = g_get_system_data_dirs ();
-  for (dir = dirs; *dir; ++dir)
-    {
-      try_dir (*dir);
-    }
-
-#undef try_dir
-
-  return NULL;
-}
-
-
-static void
-ensure_alsa_card (const gchar  *machine,
-                  const gchar  *var,
-                  const gchar  *key,
-                        gchar **name)
-{
-  const gchar *env;
-
-  if (*name)
-    {
-      return;
-    }
-
-  env = g_getenv (var);
-  if (env)
-    {
-      *name = g_strdup (env);
-      return;
-    }
-
-  if (machine)
-    {
-      *name = machine_conf (machine, key);
-      if (*name)
-        {
-          return;
-        }
-    }
-
-  g_warning ("No %s specified, refusing to run", key);
-  exit (EXIT_SUCCESS);
-}
-
-
 int
 main (int argc, char **argv)
 {
   GError *error = NULL;
   GOptionContext *context;
   gboolean ok;
-  g_autofree gchar *codec = NULL;
-  g_autofree gchar *modem = NULL;
-  g_autofree gchar *machine = NULL;
-
-  GOptionEntry options[] =
-    {
-      { "codec", 'c', 0, G_OPTION_ARG_STRING, &codec, "Name of the codec's ALSA card", "NAME" },
-      { "modem", 'm', 0, G_OPTION_ARG_STRING, &modem, "Name of the modem's ALSA card", "NAME" },
-      { NULL }
-    };
 
   setlocale(LC_ALL, "");
 
-  machine = mchk_read_machine (NULL);
-  if (machine)
-    {
-      check_machine (machine);
-    }
-  else
-    {
-      g_warning ("Could not read machine name, continuing without machine check");
-    }
-
-
-  context = g_option_context_new ("- set up PulseAudio loopback for phone call audio");
-  g_option_context_add_main_entries (context, options, NULL);
-  ok = g_option_context_parse (context, &argc, &argv, &error);
-  if (!ok)
-    {
-      g_print ("Error parsing options: %s\n", error->message);
-    }
-
-
-  if (machine)
-    {
-      /* Convert any directory separator characters to "_" */
-      g_strdelimit (machine, G_DIR_SEPARATOR_S, '_');
-    }
-
-  ensure_alsa_card (machine, "WYS_CODEC", "codec", &codec);
-  ensure_alsa_card (machine, "WYS_MODEM", "modem", &modem);
-
   setup_signals ();
 
-  run (codec, modem);
+  run ();
 
   return 0;
 }
diff --git a/src/meson.build b/src/meson.build
index 7b99235..a8a30d7 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -22,13 +22,10 @@
 gnome = import('gnome')
 
 wys_deps = [
-  libmchk_dep,
   dependency('gobject-2.0'),
   dependency('gio-unix-2.0'),
   dependency('ModemManager'),
   dependency('mm-glib'),
-  dependency('libpulse'),
-  dependency('libpulse-mainloop-glib'),
 ]
 
 config_h = configure_file (
@@ -36,20 +33,13 @@ config_h = configure_file (
   configuration: config_data
 )
 
-wys_enum_headers = files(['wys-direction.h'])
-wys_enum_sources = gnome.mkenums_simple('enum-types',
-                                        sources : wys_enum_headers)
-
 executable (
   'wys',
   config_h,
-  wys_enum_sources,
   [
     'main.c',
     'util.h', 'util.c',
-    'wys-direction.h', 'wys-direction.c',
     'wys-modem.h', 'wys-modem.c',
-    'wys-audio.h', 'wys-audio.c',
   ],
   dependencies : wys_deps,
   include_directories : include_directories('..'),
diff --git a/src/wys-audio.c b/src/wys-audio.c
deleted file mode 100644
index 4a3c7fd..0000000
--- a/src/wys-audio.c
+++ /dev/null
@@ -1,1259 +0,0 @@
-/*
- * copyright (C) 2018, 2019 Purism SPC
- *
- * This file is part of Wys.
- *
- * Wys is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Wys is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
- * License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Wys.  If not, see <http://www.gnu.org/licenses/>.
- *
- * Author: Bob Ham <bob.ham@puri.sm>
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- *
- */
-
-#include "wys-audio.h"
-#include "util.h"
-
-#include <glib/gi18n.h>
-#include <glib-object.h>
-#include <pulse/pulseaudio.h>
-#include <pulse/glib-mainloop.h>
-
-
-struct _WysAudio
-{
-  GObject parent_instance;
-
-  gchar             *codec;
-  gchar             *modem;
-  pa_glib_mainloop  *loop;
-  pa_context        *ctx;
-};
-
-G_DEFINE_TYPE (WysAudio, wys_audio, G_TYPE_OBJECT);
-
-
-enum {
-  PROP_0,
-  PROP_CODEC,
-  PROP_MODEM,
-  PROP_LAST_PROP,
-};
-static GParamSpec *props[PROP_LAST_PROP];
-
-
-static void
-proplist_set (pa_proplist *props,
-              const char *key,
-              const char *value)
-{
-  int err = pa_proplist_sets (props, key, value);
-  if (err != 0)
-    {
-      wys_error ("Error setting PulseAudio property list"
-                 " property: %s", pa_strerror (err));
-    }
-}
-
-
-static void
-context_notify_cb (pa_context *audio, gboolean *ready)
-{
-  pa_context_state_t audio_state;
-
-  audio_state = pa_context_get_state (audio);
-  switch (audio_state)
-    {
-    case PA_CONTEXT_UNCONNECTED:
-    case PA_CONTEXT_CONNECTING:
-    case PA_CONTEXT_AUTHORIZING:
-    case PA_CONTEXT_SETTING_NAME:
-      *ready = FALSE;
-      break;
-    case PA_CONTEXT_FAILED:
-      wys_error ("Error in PulseAudio context: %s",
-                  pa_strerror (pa_context_errno (audio)));
-      break;
-    case PA_CONTEXT_TERMINATED:
-    case PA_CONTEXT_READY:
-      *ready = TRUE;
-      break;
-    }
-}
-
-
-static void
-set_up_audio_context (WysAudio *self)
-{
-  pa_proplist *props;
-  int err;
-  static gboolean ready = FALSE;
-
-  /* Meta data */
-  props = pa_proplist_new ();
-  g_assert (props != NULL);
-
-  proplist_set (props, PA_PROP_APPLICATION_NAME, APPLICATION_NAME);
-  proplist_set (props, PA_PROP_APPLICATION_ID, APPLICATION_ID);
-
-  self->loop = pa_glib_mainloop_new (NULL);
-  if (!self->loop)
-    {
-      wys_error ("Error creating PulseAudio main loop");
-    }
-
-  self->ctx = pa_context_new (pa_glib_mainloop_get_api (self->loop),
-                              APPLICATION_NAME);
-  if (!self->ctx)
-    {
-      wys_error ("Error creating PulseAudio context");
-    }
-
-  pa_context_set_state_callback (self->ctx,
-                                 (pa_context_notify_cb_t)context_notify_cb,
-                                 &ready);
-  err = pa_context_connect(self->ctx, NULL, PA_CONTEXT_NOFAIL, 0);
-  if (err < 0)
-    {
-      wys_error ("Error connecting PulseAudio context: %s",
-                  pa_strerror (err));
-    }
-
-  while (!ready)
-    {
-      g_main_context_iteration (NULL, TRUE);
-    }
-
-  pa_context_set_state_callback (self->ctx, NULL, NULL);
-}
-
-
-static void
-set_property (GObject      *object,
-              guint         property_id,
-              const GValue *value,
-              GParamSpec   *pspec)
-{
-  WysAudio *self = WYS_AUDIO (object);
-
-  switch (property_id) {
-  case PROP_CODEC:
-    self->codec = g_value_dup_string (value);
-    break;
-
-  case PROP_MODEM:
-    self->modem = g_value_dup_string (value);
-    break;
-
-  default:
-    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-    break;
-  }
-}
-
-
-static void
-constructed (GObject *object)
-{
-  GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT);
-  WysAudio *self = WYS_AUDIO (object);
-
-  set_up_audio_context (self);
-
-  parent_class->constructed (object);
-}
-
-
-static void
-dispose (GObject *object)
-{
-  GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT);
-  WysAudio *self = WYS_AUDIO (object);
-
-  if (self->ctx)
-    {
-      pa_context_disconnect (self->ctx);
-      pa_context_unref (self->ctx);
-      self->ctx = NULL;
-
-      pa_glib_mainloop_free (self->loop);
-      self->loop = NULL;
-    }
-
-
-  parent_class->dispose (object);
-}
-
-
-static void
-finalize (GObject *object)
-{
-  GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT);
-  WysAudio *self = WYS_AUDIO (object);
-
-  g_free (self->modem);
-  g_free (self->codec);
-
-  parent_class->finalize (object);
-}
-
-
-static void
-wys_audio_class_init (WysAudioClass *klass)
-{
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-  object_class->set_property = set_property;
-  object_class->constructed  = constructed;
-  object_class->dispose      = dispose;
-  object_class->finalize     = finalize;
-
-  props[PROP_CODEC] =
-    g_param_spec_string ("codec",
-                         _("Codec"),
-                         _("The ALSA card name for the codec"),
-                         "sgtl5000",
-                         G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
-
-  props[PROP_MODEM] =
-    g_param_spec_string ("modem",
-                         _("Modem"),
-                         _("The ALSA card name for the modem"),
-                         "SIMcom SIM7100",
-                         G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
-
-  g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
-}
-
-
-static void
-wys_audio_init (WysAudio *self)
-{
-}
-
-
-WysAudio *
-wys_audio_new (const gchar *codec,
-               const gchar *modem)
-{
-  return g_object_new (WYS_TYPE_AUDIO,
-                       "codec", codec,
-                       "modem", modem,
-                       NULL);
-}
-
-
-/**************** Get single info ****************/
-
-struct callback_data
-{
-  GCallback callback;
-  gpointer userdata;
-};
-
-
-#define GET_SINGLE_INFO_CALLBACK(SingleInfo, single_info)       \
-  typedef void (*Get##SingleInfo##Callback)                     \
-  (pa_context *ctx,                                             \
-   const pa_##single_info##_info *info,                         \
-   gpointer userdata);
-
-
-#define GET_SINGLE_INFO_CB(SingleInfo, single_info)             \
-  static void                                                   \
-  get_##single_info##_cb (pa_context *ctx,                      \
-                          const pa_##single_info##_info *info,  \
-                          int eol,                              \
-                          void *userdata)                       \
-  {                                                             \
-    struct callback_data *data = userdata;                      \
-    Get##SingleInfo##Callback func;                             \
-                                                                \
-    if (eol == -1)                                              \
-      {                                                         \
-        wys_error ("Error getting PulseAudio "                  \
-                   #single_info ": %s",                         \
-                   pa_strerror (pa_context_errno (ctx)));       \
-      }                                                         \
-                                                                \
-    if (eol)                                                    \
-      {                                                         \
-        if (data->callback)                                     \
-          {                                                     \
-            func = (Get##SingleInfo##Callback)data->callback;   \
-            func (ctx, NULL, data->userdata);                   \
-          }                                                     \
-                                                                \
-        g_free (data);                                          \
-        return;                                                 \
-      }                                                         \
-                                                                \
-    g_assert (info != NULL);                                    \
-                                                                \
-    /* We should only be called once with data */               \
-    g_assert (data->callback != NULL);                          \
-                                                                \
-    func = (Get##SingleInfo##Callback)data->callback;           \
-    func (ctx, info, data->userdata);                           \
-                                                                \
-    data->callback = NULL;                                      \
-  }
-
-
-#define GET_SINGLE_INFO(single_info, single_info_func)  \
-  static void                                           \
-  get_##single_info (pa_context *ctx,                   \
-                     uint32_t index,                    \
-                     GCallback callback,                \
-                     gpointer userdata)                 \
-  {                                                     \
-    pa_operation *op;                                   \
-    struct callback_data *data;                         \
-                                                        \
-    data = g_new (struct callback_data, 1);             \
-    data->callback = callback;                          \
-    data->userdata = userdata;                          \
-                                                        \
-    op = pa_context_get_##single_info_func              \
-      (ctx,                                             \
-       index,                                           \
-       get_##single_info##_cb,                          \
-       data);                                           \
-                                                        \
-    pa_operation_unref (op);                            \
-  }
-
-
-#define DECLARE_GET_SINGLE_INFO(SingleInfo, single_info, single_info_func) \
-  GET_SINGLE_INFO_CALLBACK(SingleInfo, single_info)                     \
-  GET_SINGLE_INFO_CB(SingleInfo, single_info)                           \
-  GET_SINGLE_INFO(single_info, single_info_func)
-
-
-DECLARE_GET_SINGLE_INFO(Source, source, source_info_by_index);
-DECLARE_GET_SINGLE_INFO(Module, module, module_info);
-DECLARE_GET_SINGLE_INFO(Sink,   sink,   sink_info_by_index);
-
-
-/**************** PulseAudio properties ****************/
-
-static gboolean
-prop_matches (pa_proplist *props,
-              const gchar *key,
-              const gchar *needle)
-{
-  const char *value;
-
-  value = pa_proplist_gets (props, key);
-
-  if (!value)
-    {
-      return FALSE;
-    }
-
-  return strcmp (value, needle) == 0;
-}
-
-
-static gboolean
-props_name_alsa_card (pa_proplist *props,
-                      const gchar *alsa_card)
-{
-#define check(key,val)                          \
-  if (!prop_matches (props, key, val))          \
-    {                                           \
-      return FALSE;                             \
-    }
-
-  check ("device.class",   "sound");
-  check ("device.api",     "alsa");
-  check ("alsa.card_name", alsa_card);
-
-#undef check
-
-  return TRUE;
-}
-
-
-/**************** Find loopback data ****************/
-
-typedef void (*FindLoopbackCallback) (gchar *source_alsa_card,
-                                      gchar *sink_alsa_card,
-                                      GList *modules,
-                                      gpointer userdata);
-
-
-struct find_loopback_data
-{
-  gchar *source_alsa_card;
-  gchar *sink_alsa_card;
-  GCallback callback;
-  gpointer userdata;
-  GList *modules;
-};
-
-
-static struct find_loopback_data *
-find_loopback_data_new (const gchar *source_alsa_card,
-                        const gchar *sink_alsa_card)
-{
-  struct find_loopback_data *data;
-
-  data = g_rc_box_new0 (struct find_loopback_data);
-  data->source_alsa_card = g_strdup (source_alsa_card);
-  data->sink_alsa_card = g_strdup (sink_alsa_card);
-
-  return data;
-}
-
-
-static void
-find_loopback_data_clear (struct find_loopback_data *data)
-{
-  FindLoopbackCallback func = (FindLoopbackCallback)data->callback;
-
-  func (data->source_alsa_card,
-        data->sink_alsa_card,
-        data->modules,
-        data->userdata);
-
-  g_list_free (data->modules);
-  g_free (data->sink_alsa_card);
-  g_free (data->source_alsa_card);
-}
-
-
-static inline void
-find_loopback_data_release (struct find_loopback_data *data)
-{
-  g_rc_box_release_full (data, (GDestroyNotify)find_loopback_data_clear);
-}
-
-
-/**************** Loopback module data ****************/
-
-struct loopback_module_data
-{
-  struct find_loopback_data *loopback_data;
-  uint32_t module_index;
-};
-
-
-static struct loopback_module_data *
-loopback_module_data_new (struct find_loopback_data *loopback_data,
-                          uint32_t module_index)
-{
-  struct loopback_module_data * module_data;
-
-  module_data = g_rc_box_new0 (struct loopback_module_data);
-  module_data->module_index = module_index;
-
-  g_rc_box_acquire (loopback_data);
-  module_data->loopback_data = loopback_data;
-
-  return module_data;
-}
-
-
-static inline void
-loopback_module_data_clear (struct loopback_module_data *module_data)
-{
-  find_loopback_data_release (module_data->loopback_data);
-}
-
-
-static inline void
-loopback_module_data_release (struct loopback_module_data *module_data)
-{
-  g_rc_box_release_full (module_data,
-                         (GDestroyNotify)loopback_module_data_clear);
-}
-
-
-/**************** Sink ****************/
-
-static void
-find_loopback_get_sink_cb (pa_context *ctx,
-                           const pa_sink_info *info,
-                           struct loopback_module_data *module_data)
-{
-  struct find_loopback_data *loopback_data = module_data->loopback_data;
-
-  if (!info)
-    {
-      g_warning ("Could not get sink for module %" PRIu32
-                 " owning ALSA card `%s' source output",
-                 module_data->module_index,
-                 loopback_data->source_alsa_card);
-      goto release;
-    }
-
-  if (!props_name_alsa_card (info->proplist,
-                             loopback_data->sink_alsa_card))
-    {
-      g_debug ("Sink %" PRIu32 " `%s' for module %" PRIu32
-               " is not ALSA card `%s'",
-               info->index, info->name,
-               module_data->module_index,
-               loopback_data->sink_alsa_card);
-      goto release;
-    }
-
-
-  g_debug ("Loopback module %" PRIu32 " has ALSA card `%s' source"
-           " and ALSA card `%s' sink",
-           module_data->module_index,
-           loopback_data->source_alsa_card,
-           loopback_data->sink_alsa_card);
-
-  loopback_data->modules = g_list_append
-    (loopback_data->modules,
-     GUINT_TO_POINTER (module_data->module_index));
-
- release:
-  loopback_module_data_release (module_data);
-}
-
-/**************** Sink input list ****************/
-
-static void
-find_loopback_sink_input_list_cb (pa_context *ctx,
-                                  const pa_sink_input_info *info,
-                                  int eol,
-                                  void *userdata)
-{
-  struct loopback_module_data *module_data = userdata;
-  struct find_loopback_data *loopback_data = module_data->loopback_data;
-
-  if (eol == -1)
-    {
-      wys_error ("Error listing sink inputs: %s",
-                 pa_strerror (pa_context_errno (ctx)));
-    }
-
-  if (eol)
-    {
-      g_debug ("End of sink input list reached");
-      loopback_module_data_release (module_data);
-      return;
-    }
-
-  if (info->owner_module != module_data->module_index)
-    {
-      g_debug ("Sink input %" PRIu32 " `%s' has"
-               " owner module %" PRIu32 " which does"
-               " not match sought module %" PRIu32
-               " for ALSA card `%s' source output",
-               info->index,
-               info->name,
-               info->owner_module,
-               module_data->module_index,
-               loopback_data->source_alsa_card);
-      return;
-    }
-
-  g_debug ("Checking whether sink %" PRIu32
-           " for sink input %" PRIu32
-           " `%s' owned by module %" PRIu32
-           " has ALSA card name `%s'",
-           info->sink,
-           info->index,
-           info->name,
-           info->owner_module,
-           loopback_data->sink_alsa_card);
-
-  g_rc_box_acquire (module_data);
-  get_sink (ctx,
-            info->sink,
-            G_CALLBACK (find_loopback_get_sink_cb),
-            module_data);
-}
-
-static void
-find_sink_input (pa_context *ctx,
-                 struct loopback_module_data *module_data)
-{
-  pa_operation *op;
-
-  op = pa_context_get_sink_input_info_list
-    (ctx, find_loopback_sink_input_list_cb, module_data);
-
-  pa_operation_unref (op);
-}
-
-
-/**************** Module ****************/
-
-static void
-find_loopback_get_module_cb (pa_context *ctx,
-                             const pa_module_info *info,
-                             struct loopback_module_data *module_data)
-{
-  struct find_loopback_data *loopback_data = module_data->loopback_data;
-
-  if (!info)
-    {
-      g_warning ("Could not get module %" PRIu32
-                 " for ALSA card `%s' source output",
-                 module_data->module_index,
-                 loopback_data->source_alsa_card);
-      loopback_module_data_release (module_data);
-      return;
-    }
-
-  if (strcmp (info->name, "module-loopback") != 0)
-    {
-      g_debug ("Module %" PRIu32 " for ALSA card `%s` source output"
-               " is not a loopback module",
-               info->index, loopback_data->source_alsa_card);
-      loopback_module_data_release (module_data);
-      return;
-    }
-
-
-  g_debug ("Module %" PRIu32 " for ALSA card `%s' source output is a"
-           " loopback module, finding sink input with matching module",
-           info->index, loopback_data->source_alsa_card);
-
-  find_sink_input (ctx, module_data);
-}
-
-
-/**************** Source ****************/
-
-static void
-find_loopback_get_source_cb (pa_context *ctx,
-                             const pa_source_info *info,
-                             struct loopback_module_data *module_data)
-{
-  struct find_loopback_data *loopback_data = module_data->loopback_data;
-
-  if (!info)
-    {
-      g_warning ("Couldn't find source for source output"
-                 " while finding ALSA card `%s' source",
-                 loopback_data->source_alsa_card);
-      loopback_module_data_release (module_data);
-      return;
-    }
-
-  if (!props_name_alsa_card (info->proplist,
-                             loopback_data->source_alsa_card))
-    {
-      g_debug ("Source %" PRIu32 " `%s' is not ALSA card `%s'",
-               info->index, info->name,
-               loopback_data->source_alsa_card);
-      loopback_module_data_release (module_data);
-      return;
-    }
-
-
-  g_debug ("Checking whether module %" PRIu32
-           " for ALSA card `%s' source output"
-           " is a loopback module",
-           module_data->module_index,
-           loopback_data->source_alsa_card);
-
-  get_module (ctx,
-              module_data->module_index,
-              G_CALLBACK (find_loopback_get_module_cb),
-              module_data);
-}
-
-
-/**************** Find loopback (source output list) ****************/
-
-static void
-find_loopback_source_output_list_cb (pa_context *ctx,
-                                     const pa_source_output_info *info,
-                                     int eol,
-                                     void *userdata)
-{
-  struct find_loopback_data *loopback_data = userdata;
-  struct loopback_module_data *module_data;
-
-  if (eol == -1)
-    {
-      wys_error ("Error listing PulseAudio source outputs: %s",
-                 pa_strerror (pa_context_errno (ctx)));
-    }
-
-  if (eol)
-    {
-      g_debug ("End of source output list reached");
-      find_loopback_data_release (loopback_data);
-      return;
-    }
-
-  if (info->owner_module == PA_INVALID_INDEX)
-    {
-      g_debug ("Source output %" PRIu32 " `%s'"
-               " is not owned by a module",
-               info->index, info->name);
-      return;
-    }
-
-  module_data = loopback_module_data_new (loopback_data,
-                                          info->owner_module);
-
-  g_debug ("Getting source %" PRIu32
-           " of source output %" PRIu32 " `%s'",
-           info->source, info->index, info->name);
-  get_source (ctx,
-              info->source,
-              G_CALLBACK (find_loopback_get_source_cb),
-              module_data);
-}
-
-
-/** Find any loopback module between the specified source and sink
-    alsa cards.
-
-    1. Loop through all source outputs.
-      1. Skip any source output that doesn't have the specified
-      alsa card as its source.
-      2. Skip any source output that isn't a loopback module.
-      3. Loop through all sink inputs.
-        1. Skip any sink input whose module index doesn't match.
-        2. Skip any sink input which doesn't have the specified alsa
-        card as its sink.
-        3. Found a loopback.
-*/
-static void
-find_loopback (pa_context *ctx,
-               const gchar *source_alsa_card,
-               const gchar *sink_alsa_card,
-               GCallback callback,
-               gpointer userdata)
-{
-  struct find_loopback_data *data;
-  pa_operation *op;
-
-  data = find_loopback_data_new (source_alsa_card, sink_alsa_card);
-  data->callback = callback;
-  data->userdata = userdata;
-
-  g_debug ("Finding ALSA card `%s' source output",
-           source_alsa_card);
-  op = pa_context_get_source_output_info_list
-    (ctx, find_loopback_source_output_list_cb, data);
-
-  pa_operation_unref (op);
-}
-
-
-/**************** Find ALSA card data ****************/
-
-typedef void (*FindALSACardCallback) (const gchar *alsa_card_name,
-                                      const gchar *pulse_object_name,
-                                      gpointer userdata);
-
-struct find_alsa_card_data
-{
-  gchar *alsa_card_name;
-  GCallback callback;
-  gpointer userdata;
-  gchar *pulse_object_name;
-};
-
-
-static struct find_alsa_card_data *
-find_alsa_card_data_new (const gchar *alsa_card_name)
-{
-  struct find_alsa_card_data *data;
-
-  data = g_rc_box_new0 (struct find_alsa_card_data);
-  data->alsa_card_name = g_strdup (alsa_card_name);
-
-  return data;
-}
-
-
-static void
-find_alsa_card_data_clear (struct find_alsa_card_data *data)
-{
-  FindALSACardCallback func = (FindALSACardCallback)data->callback;
-
-  func (data->alsa_card_name,
-        data->pulse_object_name,
-        data->userdata);
-
-  g_free (data->pulse_object_name);
-  g_free (data->alsa_card_name);
-}
-
-
-static inline void
-find_alsa_card_data_release (struct find_alsa_card_data *data)
-{
-  g_rc_box_release_full (data, (GDestroyNotify)find_alsa_card_data_clear);
-}
-
-
-/**************** Find ALSA card ****************/
-
-#define FIND_ALSA_CARD_LIST_CB(object_type)                             \
-  static void                                                           \
-  find_alsa_card_##object_type##list_cb                                 \
-  (pa_context *ctx,                                                     \
-   const pa_##object_type##_info *info,                                 \
-   int eol,                                                             \
-   void *userdata)                                                      \
-  {                                                                     \
-    struct find_alsa_card_data *alsa_card_data = userdata;              \
-                                                                        \
-    if (eol == -1)                                                      \
-      {                                                                 \
-        wys_error ("Error listing PulseAudio " #object_type "s: %s",    \
-                   pa_strerror (pa_context_errno (ctx)));               \
-      }                                                                 \
-                                                                        \
-    if (eol)                                                            \
-      {                                                                 \
-        g_debug ("End of " #object_type " list reached");               \
-        find_alsa_card_data_release (alsa_card_data);                   \
-        return;                                                         \
-      }                                                                 \
-                                                                        \
-    if (alsa_card_data->pulse_object_name != NULL)                      \
-      {                                                                 \
-        /* Already found our object */                                  \
-        g_debug ("Skipping " #object_type                               \
-                 " %" PRIu32 " `%s'",                                   \
-                 info->index, info->name);                              \
-        return;                                                         \
-      }                                                                 \
-                                                                        \
-    g_assert (info != NULL);                                            \
-                                                                        \
-    if (!props_name_alsa_card (info->proplist,                          \
-                               alsa_card_data->alsa_card_name))         \
-      {                                                                 \
-        g_debug ("The " #object_type " %" PRIu32                        \
-                 " `%s' is not ALSA card `%s'",                         \
-                 info->index,                                           \
-                 info->name,                                            \
-                 alsa_card_data->alsa_card_name);                       \
-        return;                                                         \
-      }                                                                 \
-                                                                        \
-    g_debug ("The " #object_type " %" PRIu32                            \
-             " `%s' is ALSA card `%s'",                                 \
-             info->index,                                               \
-             info->name,                                                \
-             alsa_card_data->alsa_card_name);                           \
-    alsa_card_data->pulse_object_name = g_strdup (info->name);          \
-  }
-
-
-#define FIND_ALSA_CARD(object_type)                             \
-  static void                                                   \
-  find_alsa_card_##object_type (pa_context *ctx,                \
-                                const gchar *alsa_card_name,    \
-                                GCallback callback,             \
-                                gpointer userdata)              \
-  {                                                             \
-    pa_operation *op;                                           \
-    struct find_alsa_card_data *data;                           \
-                                                                \
-    data = find_alsa_card_data_new (alsa_card_name);            \
-    data->callback = callback;                                  \
-    data->userdata = userdata;                                  \
-                                                                \
-    op = pa_context_get_##object_type##_info_list               \
-      (ctx, find_alsa_card_##object_type##list_cb, data);       \
-                                                                \
-    pa_operation_unref (op);                                    \
-  }
-
-#define DECLARE_FIND_ALSA_CARD(object_type)     \
-  FIND_ALSA_CARD_LIST_CB(object_type)           \
-  FIND_ALSA_CARD(object_type)
-
-
-DECLARE_FIND_ALSA_CARD(source);
-DECLARE_FIND_ALSA_CARD(sink);
-
-
-/**************** Instantiate loopback data ****************/
-
-typedef void (*InstantiateLoopbackCallback) (gchar *source_alsa_card,
-                                             gchar *sink_alsa_card,
-                                             gchar *source,
-                                             gchar *sink,
-                                             gpointer userdata);
-
-
-struct instantiate_loopback_data
-{
-  pa_context *ctx;
-  gchar *source_alsa_card;
-  gchar *sink_alsa_card;
-  gchar *media_name;
-  gchar *source;
-  gchar *sink;
-};
-
-
-static struct instantiate_loopback_data *
-instantiate_loopback_data_new (pa_context *ctx,
-                               const gchar *source_alsa_card,
-                               const gchar *sink_alsa_card,
-                               const gchar *media_name)
-{
-  struct instantiate_loopback_data *data;
-
-  data = g_rc_box_new0 (struct instantiate_loopback_data);
-
-  pa_context_ref (ctx);
-  data->ctx = ctx;
-
-  data->source_alsa_card = g_strdup (source_alsa_card);
-  data->sink_alsa_card = g_strdup (sink_alsa_card);
-  data->media_name = g_strdup (media_name);
-
-  return data;
-}
-
-
-static void
-instantiate_loopback_data_clear (struct instantiate_loopback_data *data)
-{
-  g_free (data->sink);
-  g_free (data->source);
-  g_free (data->media_name);
-  g_free (data->sink_alsa_card);
-  g_free (data->source_alsa_card);
-  pa_context_unref (data->ctx);
-}
-
-
-static inline void
-instantiate_loopback_data_release (struct instantiate_loopback_data *data)
-{
-  g_rc_box_release_full (data, (GDestroyNotify)instantiate_loopback_data_clear);
-}
-
-
-/**************** Instantiate loopback ****************/
-
-static void
-instantiate_loopback_load_module_cb (pa_context *ctx,
-                                     uint32_t index,
-                                     void *userdata)
-{
-  struct instantiate_loopback_data *data = userdata;
-
-  if (index == PA_INVALID_INDEX)
-    {
-      g_warning ("Error instantiating loopback module with source `%s'"
-                 " (ALSA card `%s') and sink `%s' (ALSA card `%s'): %s",
-                 data->source,
-                 data->source_alsa_card,
-                 data->sink,
-                 data->sink_alsa_card,
-                 pa_strerror (pa_context_errno (ctx)));
-    }
-  else
-    {
-      g_debug ("Instantiated loopback module %" PRIu32
-               " with source `%s' (ALSA card `%s')"
-               " and sink `%s' (ALSA card `%s')",
-               index,
-               data->source,
-               data->source_alsa_card,
-               data->sink,
-               data->sink_alsa_card);
-    }
-
-  instantiate_loopback_data_release (data);
-}
-
-
-static void
-instantiate_loopback_sink_cb (const gchar *alsa_card_name,
-                              const gchar *pulse_object_name,
-                              gpointer userdata)
-{
-  struct instantiate_loopback_data *data = userdata;
-  pa_proplist *stream_props;
-  gchar *stream_props_str;
-  gchar *arg;
-  pa_operation *op;
-
-  if (!pulse_object_name)
-    {
-      g_warning ("Could not find sink for ALSA card `%s'",
-                 alsa_card_name);
-      instantiate_loopback_data_release (data);
-      return;
-    }
-
-  data->sink = g_strdup (pulse_object_name);
-
-  g_debug ("Instantiating loopback module with source `%s'"
-           " (ALSA card `%s') and sink `%s' (ALSA card `%s')",
-           data->source,
-           data->source_alsa_card,
-           data->sink,
-           data->sink_alsa_card);
-
-  stream_props = pa_proplist_new ();
-  g_assert (stream_props != NULL);
-  proplist_set (stream_props, "media.role", "phone");
-  proplist_set (stream_props, "media.icon_name", "phone");
-  proplist_set (stream_props, "media.name", data->media_name);
-
-  stream_props_str = pa_proplist_to_string (stream_props);
-  pa_proplist_free (stream_props);
-  
-  arg = g_strdup_printf ("source=%s"
-                         " sink=%s"
-                         " source_dont_move=true"
-                         " sink_dont_move=true"
-                         " fast_adjust_threshold_msec=100"
-                         " max_latency_msec=25"
-                         " sink_input_properties='%s'"
-                         " source_output_properties='%s'",
-                         data->source,
-                         data->sink,
-                         stream_props_str,
-                         stream_props_str);
-  pa_xfree (stream_props_str);
-
-  op = pa_context_load_module (data->ctx,
-                               "module-loopback",
-                               arg,
-                               instantiate_loopback_load_module_cb,
-                               data);
-
-  pa_operation_unref (op);
-  g_free (arg);
-}
-
-
-static void
-instantiate_loopback_source_cb (const gchar *alsa_card_name,
-                                const gchar *pulse_object_name,
-                                gpointer userdata)
-{
-  struct instantiate_loopback_data *data = userdata;
-
-  if (!pulse_object_name)
-    {
-      g_warning ("Could not find source for ALSA card `%s'",
-                 alsa_card_name);
-      instantiate_loopback_data_release (data);
-      return;
-    }
-
-  data->source = g_strdup (pulse_object_name);
-
-  g_debug ("Finding sink for ALSA card `%s'", data->sink_alsa_card);
-  find_alsa_card_sink (data->ctx,
-                       data->sink_alsa_card,
-                       G_CALLBACK (instantiate_loopback_sink_cb),
-                       data);
-}
-
-
-static void
-instantiate_loopback (pa_context *ctx,
-                      const gchar *source_alsa_card,
-                      const gchar *sink_alsa_card,
-                      const gchar *media_name)
-{
-  struct instantiate_loopback_data *loopback_data;
-
-  loopback_data = instantiate_loopback_data_new (ctx,
-                                                 source_alsa_card,
-                                                 sink_alsa_card,
-                                                 media_name);
-
-  g_debug ("Finding source for ALSA card `%s'", source_alsa_card);
-  find_alsa_card_source (ctx,
-                         source_alsa_card,
-                         G_CALLBACK (instantiate_loopback_source_cb),
-                         loopback_data);
-}
-
-
-/**************** Ensure loopback ****************/
-
-struct ensure_loopback_data
-{
-  pa_context *ctx;
-  const gchar *media_name;
-};
-
-
-static void
-ensure_loopback_find_loopback_cb (gchar *source_alsa_card,
-                                  gchar *sink_alsa_card,
-                                  GList *modules,
-                                  struct ensure_loopback_data *data)
-{
-  if (modules != NULL)
-    {
-      g_warning ("%u loopback module(s) for ALSA card `%s' source ->"
-                 " ALSA card `%s' sink already exist",
-                 g_list_length (modules),
-                 source_alsa_card, sink_alsa_card);
-    }
-  else
-    {
-      g_debug ("Instantiating loopback module for ALSA card `%s' source ->"
-               " ALSA card `%s' sink",
-               source_alsa_card, sink_alsa_card);
-
-      instantiate_loopback (data->ctx, source_alsa_card,
-                            sink_alsa_card, data->media_name);
-    }
-
-  g_free (data);
-}
-
-
-static void
-ensure_loopback (pa_context *ctx,
-                 const gchar *source_alsa_card,
-                 const gchar *sink_alsa_card,
-                 const gchar *media_name)
-{
-  struct ensure_loopback_data *data;
-
-  data = g_new (struct ensure_loopback_data, 1);
-  data->ctx = ctx;
-  // This is a static string so we don't need a copy
-  data->media_name = media_name;
-
-  find_loopback (ctx, source_alsa_card, sink_alsa_card,
-                 G_CALLBACK (ensure_loopback_find_loopback_cb),
-                 data);
-}
-
-
-void
-wys_audio_ensure_loopback (WysAudio     *self,
-                           WysDirection  direction)
-{
-  switch (direction)
-    {
-    case WYS_DIRECTION_FROM_NETWORK:
-      ensure_loopback (self->ctx, self->modem, self->codec,
-                       "Voice call audio (to speaker)");
-      break;
-    case WYS_DIRECTION_TO_NETWORK:
-      ensure_loopback (self->ctx, self->codec, self->modem,
-                       "Voice call audio (from mic)");
-      break;
-    default:
-      break;
-    }
-}
-
-
-/**************** Ensure no loopback ****************/
-
-static void
-ensure_no_loopback_unload_module_cb (pa_context *ctx,
-                                     int success,
-                                     void *userdata)
-{
-  const guint module_index = GPOINTER_TO_UINT (userdata);
-
-  if (success)
-    {
-      g_debug ("Successfully deinstantiated loopback module %u",
-               module_index);
-    }
-  else
-    {
-      g_warning ("Error deinstantiating loopback module %u: %s",
-                 module_index,
-                 pa_strerror (pa_context_errno (ctx)));
-    }
-}
-
-
-static void
-ensure_no_loopback_modules_cb (gpointer data,
-                               pa_context *ctx)
-{
-  const uint32_t module_index = GPOINTER_TO_UINT (data);
-  pa_operation *op;
-
-  g_debug ("Deinstantiating loopback module %" PRIu32,
-           module_index);
-
-  op = pa_context_unload_module (ctx,
-                                 module_index,
-                                 ensure_no_loopback_unload_module_cb,
-                                 data);
-
-  pa_operation_unref (op);
-}
-
-
-static void
-ensure_no_loopback_find_loopback_cb (gchar *source_alsa_card,
-                                     gchar *sink_alsa_card,
-                                     GList *modules,
-                                     pa_context *ctx)
-{
-  if (modules == NULL)
-    {
-      g_warning ("No loopback module(s) for ALSA card `%s' source ->"
-                 " ALSA card `%s' sink",
-                 source_alsa_card, sink_alsa_card);
-      return;
-    }
-
-  g_debug ("Deinstantiating loopback modules for ALSA card `%s' source ->"
-           " ALSA card `%s' sink",
-           source_alsa_card, sink_alsa_card);
-
-  g_list_foreach (modules,
-                  (GFunc)ensure_no_loopback_modules_cb,
-                  ctx);
-}
-
-
-static void
-ensure_no_loopback (pa_context *ctx,
-                    const gchar *source_alsa_card,
-                    const gchar *sink_alsa_card)
-{
-  find_loopback (ctx, source_alsa_card, sink_alsa_card,
-                 G_CALLBACK (ensure_no_loopback_find_loopback_cb),
-                 ctx);
-}
-
-
-void
-wys_audio_ensure_no_loopback (WysAudio     *self,
-                              WysDirection  direction)
-{
-  switch (direction)
-    {
-    case WYS_DIRECTION_FROM_NETWORK:
-      ensure_no_loopback (self->ctx, self->modem, self->codec);
-      break;
-    case WYS_DIRECTION_TO_NETWORK:
-      ensure_no_loopback (self->ctx, self->codec, self->modem);
-      break;
-    default:
-      break;
-    }
-}
diff --git a/src/wys-audio.h b/src/wys-audio.h
deleted file mode 100644
index ce74009..0000000
--- a/src/wys-audio.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2018, 2019 Purism SPC
- *
- * This file is part of Wys.
- *
- * Wys is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Wys is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
- * License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Wys.  If not, see <http://www.gnu.org/licenses/>.
- *
- * Author: Bob Ham <bob.ham@puri.sm>
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- *
- */
-
-#ifndef WYS_AUDIO_H__
-#define WYS_AUDIO_H__
-
-#include "wys-direction.h"
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-#define WYS_TYPE_AUDIO (wys_audio_get_type ())
-
-G_DECLARE_FINAL_TYPE (WysAudio, wys_audio, WYS, AUDIO, GObject);
-
-WysAudio *wys_audio_new                (const gchar  *codec,
-                                        const gchar  *modem);
-void      wys_audio_ensure_loopback    (WysAudio     *self,
-                                        WysDirection  direction);
-void      wys_audio_ensure_no_loopback (WysAudio     *self,
-                                        WysDirection  direction);
-
-G_END_DECLS
-
-#endif /* WYS_AUDIO_H__ */
diff --git a/src/wys-direction.c b/src/wys-direction.c
deleted file mode 100644
index 1a84847..0000000
--- a/src/wys-direction.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2019 Purism SPC
- *
- * This file is part of Wys.
- *
- * Wys is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Wys is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
- * License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Wys.  If not, see <http://www.gnu.org/licenses/>.
- *
- * Author: Bob Ham <bob.ham@puri.sm>
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- *
- */
-
-
-#include "wys-direction.h"
-
-const gchar *
-wys_direction_get_description (WysDirection direction)
-{
-  switch (direction)
-    {
-    case WYS_DIRECTION_FROM_NETWORK:
-      return "from network";
-    case WYS_DIRECTION_TO_NETWORK:
-      return "to network";
-    default:
-      return NULL;
-    }
-}
diff --git a/src/wys-direction.h b/src/wys-direction.h
deleted file mode 100644
index 5c8de8b..0000000
--- a/src/wys-direction.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2019 Purism SPC
- *
- * This file is part of Wys.
- *
- * Wys is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Wys is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
- * License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Wys.  If not, see <http://www.gnu.org/licenses/>.
- *
- * Author: Bob Ham <bob.ham@puri.sm>
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- *
- */
-
-#ifndef WYS_DIRECTION_H__
-#define WYS_DIRECTION_H__
-
-#include <glib.h>
-
-G_BEGIN_DECLS
-
-typedef enum
-{
-  WYS_DIRECTION_FROM_NETWORK = 0,
-  WYS_DIRECTION_TO_NETWORK
-} WysDirection;
-
-const gchar *wys_direction_get_description (WysDirection direction);
-
-G_END_DECLS
-
-#endif /* WYS_DIRECTION_H__ */
diff --git a/src/wys-modem.c b/src/wys-modem.c
index 212f992..51a0575 100644
--- a/src/wys-modem.c
+++ b/src/wys-modem.c
@@ -24,18 +24,10 @@
 
 
 #include "wys-modem.h"
-#include "wys-direction.h"
 #include "util.h"
-#include "enum-types.h"
 
 #include <glib/gi18n.h>
 
-static const gchar * const WYS_MODEM_HAS_AUDIO[] =
-  {
-   [WYS_DIRECTION_FROM_NETWORK] = "wys-has-audio-from-network",
-   [WYS_DIRECTION_TO_NETWORK]   = "wys-has-audio-to-network"
-  };
-
 struct _WysModem
 {
   GObject parent_instance;
@@ -43,8 +35,8 @@ struct _WysModem
   MMModemVoice *voice;
   /** Map of D-Bus object paths to MMCall objects */
   GHashTable *calls;
-  /** How many calls have audio, in each direction */
-  guint audio_count[2];
+  /** How many calls have audio */
+  guint audio_count;
 };
 
 G_DEFINE_TYPE(WysModem, wys_modem, G_TYPE_OBJECT)
@@ -65,15 +57,11 @@ static guint signals [SIGNAL_LAST_SIGNAL];
 
 
 static gboolean
-call_state_has_audio (WysDirection direction,
-                      MMCallState  state)
+call_state_has_audio (MMCallState  state)
 {
   switch (state)
     {
     case MM_CALL_STATE_RINGING_OUT:
-      return
-        (direction == WYS_DIRECTION_FROM_NETWORK)
-        ? TRUE : FALSE;
     case MM_CALL_STATE_ACTIVE:
       return TRUE;
     default:
@@ -84,39 +72,35 @@ call_state_has_audio (WysDirection direction,
 
 static void
 update_audio_count (WysModem     *self,
-                    WysDirection  direction,
                     gint          delta)
 {
-  const guint old_count = self->audio_count[direction];
+  const guint old_count = self->audio_count;
 
-  g_assert (delta >= 0 || self->audio_count[direction] > 0);
+  g_assert (delta >= 0 || self->audio_count > 0);
 
-  self->audio_count[direction] += delta;
+  self->audio_count += delta;
 
-  if (self->audio_count[direction] > 0 && old_count == 0)
+  if (self->audio_count > 0 && old_count == 0)
     {
-      g_debug ("Modem `%s' audio %s now present",
-               mm_modem_voice_get_path (self->voice),
-               wys_direction_get_description (direction));
-      g_signal_emit_by_name (self, "audio-present", direction);
+      g_message ("Modem `%s' audio now present",
+                 mm_modem_voice_get_path (self->voice));
+      g_signal_emit_by_name (self, "audio-present");
     }
-  else if (self->audio_count[direction] == 0 && old_count > 0)
+  else if (self->audio_count == 0 && old_count > 0)
     {
-      g_debug ("Modem `%s' audio now absent",
-               mm_modem_voice_get_path (self->voice));
-      g_signal_emit_by_name (self, "audio-absent", direction);
+      g_message ("Modem `%s' audio now absent",
+                 mm_modem_voice_get_path (self->voice));
+      g_signal_emit_by_name (self, "audio-absent");
     }
 }
 
 
 static gboolean
-get_call_has_audio (MMCall       *mm_call,
-                    WysDirection  direction)
+get_call_has_audio (MMCall *mm_call)
 {
   gpointer data;
 
-  data = g_object_get_data (G_OBJECT (mm_call),
-                            WYS_MODEM_HAS_AUDIO[direction]);
+  data = g_object_get_data (G_OBJECT (mm_call), "audio-state");
 
   return (gboolean)(GPOINTER_TO_UINT (data));
 }
@@ -124,42 +108,38 @@ get_call_has_audio (MMCall       *mm_call,
 
 static void
 set_call_has_audio (MMCall       *mm_call,
-                    WysDirection  direction,
                     gboolean      has_audio)
 {
   g_object_set_data (G_OBJECT (mm_call),
-                     WYS_MODEM_HAS_AUDIO[direction],
+                     "audio-state",
                      GUINT_TO_POINTER ((guint)has_audio));
 }
 
 
 static void
-update_direction_state (WysModem     *self,
-                        MMCall       *mm_call,
-                        const gchar  *path,
-                        WysDirection  direction,
-                        MMCallState   old_state,
-                        MMCallState   new_state)
+update_call_state (WysModem     *self,
+                   MMCall       *mm_call,
+                   const gchar  *path,
+                   MMCallState   old_state,
+                   MMCallState   new_state)
 {
-  gboolean had_audio  = call_state_has_audio (direction, old_state);
-  gboolean have_audio = call_state_has_audio (direction, new_state);
+  gboolean had_audio  = call_state_has_audio (old_state);
+  gboolean have_audio = call_state_has_audio (new_state);
 
   if (!had_audio && have_audio)
     {
-      g_debug ("Call `%s' gained audio %s", path,
-               wys_direction_get_description (direction));
-      update_audio_count (self, direction, +1);
+      g_message ("Call `%s' gained audio", path);
+      update_audio_count (self, +1);
     }
   else if (had_audio && !have_audio)
     {
-      g_debug ("Call `%s' lost audio %s", path,
-               wys_direction_get_description (direction));
-      update_audio_count (self, direction, -1);
+      g_message ("Call `%s' lost audio", path);
+      update_audio_count (self, -1);
     }
 
   if (had_audio != have_audio)
     {
-      set_call_has_audio (mm_call, direction, have_audio);
+      set_call_has_audio (mm_call, have_audio);
     }
 }
 
@@ -174,34 +154,28 @@ call_state_changed_cb (MmGdbusCall       *mm_gdbus_call,
   MMCall *mm_call = MM_CALL (mm_gdbus_call);
   const gchar * path = mm_call_get_path (mm_call);
 
-  g_debug ("Call `%s' state changed, new: %i, old: %i",
+  g_message ("Call `%s' state changed, new: %i, old: %i",
            path, (int)new_state, (int)old_state);
 
   // FIXME: deal with calls being put on hold (one call goes
   // non-audio, another call goes audio after)
 
-  update_direction_state (self, mm_call, path,
-                          WYS_DIRECTION_FROM_NETWORK,
-                          old_state, new_state);
-  update_direction_state (self, mm_call, path,
-                          WYS_DIRECTION_TO_NETWORK,
-                          old_state, new_state);
+  update_call_state (self, mm_call, path, old_state, new_state);
 }
 
 
 static void
-init_call_direction (WysModem     *self,
-                     MMCall       *mm_call,
-                     MMCallState   state,
-                     WysDirection  direction)
+init_call_audio (WysModem     *self,
+                 MMCall       *mm_call,
+                 MMCallState   state)
 {
-  gboolean has_audio = call_state_has_audio (direction, state);
+  gboolean has_audio = call_state_has_audio (state);
 
-  set_call_has_audio (mm_call, direction, has_audio);
+  set_call_has_audio (mm_call, has_audio);
 
   if (has_audio)
     {
-      update_audio_count (self, direction, +1);
+      update_audio_count (self, +1);
     }
 }
 
@@ -223,12 +197,9 @@ add_call (WysModem *self,
                     self);
 
   state = mm_call_get_state (mm_call);
-  init_call_direction (self, mm_call, state,
-                       WYS_DIRECTION_FROM_NETWORK);
-  init_call_direction (self, mm_call, state,
-                       WYS_DIRECTION_TO_NETWORK);
+  init_call_audio (self, mm_call, state);
 
-  g_debug ("Call `%s' added, state: %i", path, (int)state);
+  g_message ("Call `%s' added, state: %i", path, (int)state);
 }
 
 
@@ -322,16 +293,14 @@ call_added_cb (MMModemVoice  *voice,
 
 
 static void
-clear_call_direction (WysModem     *self,
-                      MMCall       *mm_call,
-                      WysDirection  direction)
+clear_call_state (WysModem     *self,
+                  MMCall       *mm_call)
 {
-  gboolean has_audio =
-    get_call_has_audio (mm_call, direction);
+  gboolean has_audio = get_call_has_audio (mm_call);
 
   if (has_audio)
     {
-      update_audio_count (self, direction, -1);
+      update_audio_count (self, -1);
     }
 }
 
@@ -343,7 +312,7 @@ call_deleted_cb (MMModemVoice *voice,
 {
   MMCall *mm_call;
 
-  g_debug ("Removing call `%s'", path);
+  g_message ("Removing call `%s'", path);
 
   mm_call = g_hash_table_lookup (self->calls, path);
   if (!mm_call)
@@ -352,14 +321,11 @@ call_deleted_cb (MMModemVoice *voice,
       return;
     }
 
-  clear_call_direction (self, mm_call,
-                        WYS_DIRECTION_FROM_NETWORK);
-  clear_call_direction (self, mm_call,
-                        WYS_DIRECTION_TO_NETWORK);
+  clear_call_state (self, mm_call);
 
   g_hash_table_remove (self->calls, path);
 
-  g_debug ("Call `%s' removed", path);
+  g_message ("Call `%s' removed", path);
 }
 
 
@@ -444,11 +410,9 @@ dispose (GObject *object)
   if (g_hash_table_size (self->calls) > 0)
     {
       g_hash_table_remove_all (self->calls);
-      if (self->audio_count[WYS_DIRECTION_FROM_NETWORK] > 0 ||
-          self->audio_count[WYS_DIRECTION_TO_NETWORK] > 0)
+      if (self->audio_count > 0)
         {
-          self->audio_count[WYS_DIRECTION_FROM_NETWORK] =
-            self->audio_count[WYS_DIRECTION_TO_NETWORK] = 0;
+          self->audio_count = 0;
           g_signal_emit_by_name (self, "audio-absent");
         }
     }
@@ -504,8 +468,7 @@ wys_modem_class_init (WysModemClass *klass)
                   G_SIGNAL_RUN_LAST,
                   0, NULL, NULL, NULL,
                   G_TYPE_NONE,
-                  1,
-                  WYS_TYPE_DIRECTION);
+                  0);
 
   /**
    * WysModem::audio-absent:
@@ -520,8 +483,7 @@ wys_modem_class_init (WysModemClass *klass)
                   G_SIGNAL_RUN_LAST,
                   0, NULL, NULL, NULL,
                   G_TYPE_NONE,
-                  1,
-                  WYS_TYPE_DIRECTION);
+                  0);
 }
 
 
diff --git a/subprojects/libmachine-check/mchk-machine-check.c b/subprojects/libmachine-check/mchk-machine-check.c
deleted file mode 100644
index 2c54bda..0000000
--- a/subprojects/libmachine-check/mchk-machine-check.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (C) 2019 Purism SPC
- *
- * This file is part of libmachine-check.
- *
- * libmachine-check is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * libmachine-check is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with libmachine-check.  If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Bob Ham <bob.ham@puri.sm>
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- *
- */
-
-#include "mchk-machine-check.h"
-#include "config.h"
-
-#include <glib/gstdio.h>
-#include <gio/gunixinputstream.h>
-
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <errno.h>
-
-
-/**
- * mchk_read_machine:
- * @error: (allow-none): return location for an error, or %NULL
- *
- * Read the machine name from /proc/device-tree/machine.  If the
- * machine name could not be read, %NULL is returned and @error is
- * set.
- *
- * Returns: (nullable) (transfer full): The machine name or %NULL on
- * error.
- */
-gchar *
-mchk_read_machine (GError **error_out)
-{
-  static const gchar *MODEL_FILENAME = "/proc/device-tree/model";
-  gchar *machine;
-  GError *error = NULL;
-
-  g_return_val_if_fail (error_out == NULL || *error_out == NULL, NULL);
-
-  g_file_get_contents (MODEL_FILENAME,
-                       &machine,
-                       NULL,
-                       &error);
-  if (error)
-    {
-      g_warning ("Error reading machine name from `%s': %s",
-                 MODEL_FILENAME, error->message);
-      g_propagate_error (error_out, error);
-    }
-
-  return machine;
-}
-
-
-static gboolean
-check_list_lines (const gchar  *filename,
-                  int           fd,
-                  const gchar  *machine,
-                  gboolean     *present,
-                  GError      **error_out)
-{
-  GInputStream *unix_stream;
-  g_autoptr(GDataInputStream) data_stream = NULL;
-  GError *error = NULL;
-
-  g_debug ("Reading list file `%s'", filename);
-
-  unix_stream = g_unix_input_stream_new (fd, TRUE);
-  g_assert (unix_stream != NULL);
-
-  data_stream = g_data_input_stream_new (unix_stream);
-  g_assert (data_stream != NULL);
-  g_object_unref (unix_stream);
-
-  for (;;)
-    {
-      g_autofree gchar *line =
-        g_data_input_stream_read_line_utf8
-        (data_stream, NULL, NULL, &error);
-
-      if (error)
-        {
-          g_warning ("Error reading from check"
-                     " list file `%s': %s",
-                     filename, error->message);
-          g_propagate_error (error_out, error);
-          return FALSE;
-        }
-
-      if (line)
-        {
-          g_strstrip (line);
-
-          // Skip comments and empty lines
-          if (line[0] == '#' || line[0] == '\0')
-            {
-              continue;
-            }
-
-          // Check for the machine name
-          if (strcmp (line, machine) == 0)
-            {
-              *present = TRUE;
-              return TRUE;
-            }
-        }
-      else // EOF
-        {
-          *present = FALSE;
-          return TRUE;
-        }
-    }
-}
-
-
-static gboolean
-check_list (const gchar  *dirname,
-            const gchar  *list,
-            gboolean     *list_exists,
-            const gchar  *machine,
-            gboolean     *present,
-            GError      **error)
-{
-  g_autofree gchar *filename = NULL;
-  int fd;
-
-  filename = g_build_filename (dirname, list, NULL);
-
-  fd = g_open (filename, O_RDONLY, 0);
-  if (fd == -1)
-    {
-      if (errno == ENOENT) // The file doesn't exist
-        {
-          if (list_exists)
-            {
-              *list_exists = FALSE;
-            }
-          *present = FALSE;
-          return TRUE;
-        }
-
-      g_warning ("Error opening check"
-                 " list file `%s': %s",
-                 filename, g_strerror (errno));
-      return FALSE;
-    }
-
-  if (list_exists)
-    {
-      *list_exists = TRUE;
-    }
-
-  return check_list_lines (filename,
-                           fd,
-                           machine,
-                           present,
-                           error);
-}
-
-
-static gboolean
-check_dir_machine (const gchar  *dir,
-                   const gchar  *param,
-                   const gchar  *machine,
-                   gboolean     *done,
-                   gboolean     *passed,
-                   GError      **error)
-{
-  g_autofree gchar *check_dirname = NULL;
-  gboolean list_exists, present, ok;
-
-  check_dirname = g_build_filename (dir, "machine-check",
-                                    param, NULL);
-
-  // Check the blacklist
-  ok = check_list (check_dirname,
-                   "blacklist",
-                   NULL,
-                   machine,
-                   &present,
-                   error);
-  if (!ok)
-    {
-      return FALSE;
-    }
-
-  if (present)
-    {
-      g_debug ("Machine present in blacklist under `%s'",
-               check_dirname);
-      if (passed)
-        {
-          *passed = FALSE;
-        }
-      *done = TRUE;
-      return TRUE;
-    }
-
-  // Check the whitelist
-  ok = check_list (check_dirname,
-                   "whitelist",
-                   &list_exists,
-                   machine,
-                   &present,
-                   error);
-  if (!ok)
-    {
-      return FALSE;
-    }
-
-  if (list_exists)
-    {
-      g_debug ("Machine whitelist exists under `%s'"
-               ", machine %spresent",
-               check_dirname,
-               present ? "" : "not ");
-      *done = TRUE;
-      if (passed)
-        {
-          *passed = present;
-        }
-    }
-
-  g_debug ("No machine whitelist or blacklist under `%s'",
-           check_dirname);
-  return TRUE;
-}
-
-
-/**
- * mchk_check_machine:
- * @param: the name of the machine-check sub-directory whose blacklist
- * and whitelist should be used
- * @machine: (allow-none): the machine name to check, or %NULL
- * @passed: (out) (allow-none): return location for the check result,
- * or %NULL
- * @error: (allow-none): return location for an error, or %NULL
- *
- * Check whether the machine name is not present in a blacklist and/or
- * present in a whitelist within a machine-check sub-directory
- * named @param.  If @machine is %NULL then mchk_read_machine() will
- * be used to get the machine name.  If an error is encountered,
- * @error will be set and %FALSE will be returned.
- *
- * Returns: %TRUE on success or %FALSE on error.
- */
-gboolean
-mchk_check_machine (const gchar  *param,
-                    const gchar  *machine,
-                    gboolean     *passed,
-                    GError      **error)
-{
-  g_autofree gchar *mach = NULL;
-  const gchar * const *dirs, * const *dir;
-  gboolean done = FALSE, ok;
-
-  g_return_val_if_fail (param != NULL, FALSE);
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
-  // Get the machine name
-  if (machine)
-    {
-      mach = g_strdup (machine);
-    }
-  else
-    {
-      mach = mchk_read_machine (error);
-      if (!mach)
-        {
-          return FALSE;
-        }
-    }
-
-  // Iterate over possible whitelist/blacklist locations
-#define try_dir(d)                                      \
-  ok = check_dir_machine (d, param, mach,               \
-                          &done, passed, error);        \
-  if (!ok || done)                                      \
-    {                                                   \
-      return ok;                                        \
-    }
-
-
-  try_dir (g_get_user_config_dir ());
-
-  dirs = g_get_system_config_dirs ();
-  for (dir = dirs; *dir; ++dir)
-    {
-      try_dir (*dir);
-    }
-
-  try_dir (SYSCONFDIR);
-  try_dir (DATADIR);
-
-  dirs = g_get_system_data_dirs ();
-  for (dir = dirs; *dir; ++dir)
-    {
-      try_dir (*dir);
-    }
-
-#undef try_dir
-
-
-  g_debug ("Defaulting to pass");
-  if (passed)
-    {
-      *passed = TRUE;
-    }
-  return TRUE;
-}
diff --git a/subprojects/libmachine-check/mchk-machine-check.h b/subprojects/libmachine-check/mchk-machine-check.h
deleted file mode 100644
index 35e5e7c..0000000
--- a/subprojects/libmachine-check/mchk-machine-check.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2019 Purism SPC
- *
- * This file is part of libmachine-check.
- *
- * libmachine-check is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * libmachine-check is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with libmachine-check.  If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Bob Ham <bob.ham@puri.sm>
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- *
- */
-
-#pragma once
-
-#include <glib.h>
-
-G_BEGIN_DECLS
-
-gchar *  mchk_read_machine  (GError      **error);
-gboolean mchk_check_machine (const gchar  *param,
-                             const gchar  *machine,
-                             gboolean     *passed,
-                             GError      **error);
-
-G_END_DECLS
diff --git a/subprojects/libmachine-check/meson.build b/subprojects/libmachine-check/meson.build
deleted file mode 100644
index eb83c36..0000000
--- a/subprojects/libmachine-check/meson.build
+++ /dev/null
@@ -1,81 +0,0 @@
-#
-# Copyright (C) 2019 Purism SPC
-#
-# This file is part of libmachine-check.
-#
-# libmachine-check is free software: you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# libmachine-check is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with libmachine-check.  If not, see
-# <http://www.gnu.org/licenses/>.
-#
-# SPDX-License-Identifier: GPL-3.0-or-later
-#
-
-project (
-  'libmachine-check',
-  'c',
-  version: '0.1.0',
-  license: 'GPLv3+',
-  default_options:
-    [
-      'warning_level=1',
-      'buildtype=debugoptimized',
-      'c_std=gnu11'
-    ],
-)
-
-prefix = get_option('prefix')
-datadir = get_option('datadir')
-sysconfdir = get_option('sysconfdir')
-
-if datadir.startswith('/')
-  full_datadir = datadir
-else
-  full_datadir = join_paths(prefix, datadir)
-endif
-
-if sysconfdir.startswith('/')
-  full_sysconfdir = sysconfdir
-else
-  full_sysconfdir = join_paths(prefix, sysconfdir)
-endif
-
-config_data = configuration_data()
-config_data.set_quoted('DATADIR', full_datadir)
-config_data.set_quoted('SYSCONFDIR', full_sysconfdir)
-
-config_h = configure_file (
-  output: 'config.h',
-  configuration: config_data
-)
-
-libmachine_check_deps = [
-  dependency('gio-unix-2.0'),
-]
-
-libmachine_check_inc = include_directories('.')
-
-libmachine_check = static_library (
-  'machine-check',
-  config_h,
-  [
-    'mchk-machine-check.h', 'mchk-machine-check.c',
-  ],
-  dependencies : libmachine_check_deps,
-  include_directories : libmachine_check_inc,
-)
-
-libmachine_check_dep = declare_dependency (
-  dependencies: libmachine_check_deps,
-  link_with: libmachine_check,
-  include_directories: libmachine_check_inc,
-)
-- 
2.26.2
