2847 lines
82 KiB
Diff
2847 lines
82 KiB
Diff
|
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
|