From f66610d26395de15e3928cf4284573ca9cfabf26 Mon Sep 17 00:00:00 2001 From: Arnaud Ferraris 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 #include @@ -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 . - * - * Author: Bob Ham - * - * SPDX-License-Identifier: GPL-3.0-or-later - * - */ - -#include "wys-audio.h" -#include "util.h" - -#include -#include -#include -#include - - -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 . - * - * Author: Bob Ham - * - * SPDX-License-Identifier: GPL-3.0-or-later - * - */ - -#ifndef WYS_AUDIO_H__ -#define WYS_AUDIO_H__ - -#include "wys-direction.h" - -#include - -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 . - * - * Author: Bob Ham - * - * 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 . - * - * Author: Bob Ham - * - * SPDX-License-Identifier: GPL-3.0-or-later - * - */ - -#ifndef WYS_DIRECTION_H__ -#define WYS_DIRECTION_H__ - -#include - -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 -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 - * . - * - * Author: Bob Ham - * - * SPDX-License-Identifier: GPL-3.0-or-later - * - */ - -#include "mchk-machine-check.h" -#include "config.h" - -#include -#include - -#include -#include -#include -#include - - -/** - * 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 - * . - * - * Author: Bob Ham - * - * SPDX-License-Identifier: GPL-3.0-or-later - * - */ - -#pragma once - -#include - -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 -# . -# -# 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