488 lines
16 KiB
Diff
488 lines
16 KiB
Diff
From d5f3a6d441f47836698b851d6049bf738d066ef6 Mon Sep 17 00:00:00 2001
|
|
From: Dylan Van Assche <me@dylanvanassche.be>
|
|
Date: Sat, 1 May 2021 15:50:47 +0200
|
|
Subject: [PATCH 01/39] base-manager: add quick suspend/resume base
|
|
|
|
Quick suspend/resume infrastructure for
|
|
synchronizing the interfaces when resuming.
|
|
---
|
|
src/main.c | 14 +++-
|
|
src/mm-base-manager.c | 48 ++++++++++++
|
|
src/mm-base-manager.h | 4 +
|
|
src/mm-base-modem.c | 155 +++++++++++++++++++++++++++++++++++++++
|
|
src/mm-base-modem.h | 23 ++++++
|
|
src/mm-broadband-modem.c | 81 ++++++++++++++++++++
|
|
src/mm-context.c | 11 +++
|
|
src/mm-context.h | 1 +
|
|
8 files changed, 336 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/src/main.c b/src/main.c
|
|
index 928078a3..4a036971 100644
|
|
--- a/src/main.c
|
|
+++ b/src/main.c
|
|
@@ -72,6 +72,13 @@ resuming_cb (MMSleepMonitor *sleep_monitor)
|
|
mm_base_manager_start (manager, FALSE);
|
|
}
|
|
|
|
+static void
|
|
+resuming_quick_cb (MMSleepMonitor *sleep_monitor)
|
|
+{
|
|
+ mm_dbg ("syncing modem state (quick resuming)");
|
|
+ mm_base_manager_sync (manager);
|
|
+}
|
|
+
|
|
#endif
|
|
|
|
static void
|
|
@@ -197,7 +204,12 @@ main (int argc, char *argv[])
|
|
|
|
if (mm_context_get_test_no_suspend_resume())
|
|
mm_dbg ("Suspend/resume support disabled at runtime");
|
|
- else {
|
|
+ else if (mm_context_get_test_quick_suspend_resume()) {
|
|
+ mm_dbg ("Quick suspend/resume hooks enabled");
|
|
+ sleep_monitor = mm_sleep_monitor_get ();
|
|
+ g_signal_connect (sleep_monitor, MM_SLEEP_MONITOR_RESUMING, G_CALLBACK (resuming_quick_cb), NULL);
|
|
+ } else {
|
|
+ mm_dbg ("Full suspend/resume hooks enabled");
|
|
sleep_monitor = mm_sleep_monitor_get ();
|
|
g_signal_connect (sleep_monitor, MM_SLEEP_MONITOR_SLEEPING, G_CALLBACK (sleeping_cb), NULL);
|
|
g_signal_connect (sleep_monitor, MM_SLEEP_MONITOR_RESUMING, G_CALLBACK (resuming_cb), NULL);
|
|
diff --git a/src/mm-base-manager.c b/src/mm-base-manager.c
|
|
index 130e5ad1..0c393f11 100644
|
|
--- a/src/mm-base-manager.c
|
|
+++ b/src/mm-base-manager.c
|
|
@@ -45,6 +45,7 @@
|
|
#include "mm-plugin.h"
|
|
#include "mm-filter.h"
|
|
#include "mm-log-object.h"
|
|
+#include "mm-base-modem.h"
|
|
|
|
static void initable_iface_init (GInitableIface *iface);
|
|
static void log_object_iface_init (MMLogObjectInterface *iface);
|
|
@@ -663,6 +664,53 @@ mm_base_manager_num_modems (MMBaseManager *self)
|
|
return n;
|
|
}
|
|
|
|
+/*****************************************************************************/
|
|
+/* Quick resume synchronization */
|
|
+
|
|
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
|
|
+
|
|
+gboolean mm_base_modem_sync_finish (MMBaseModem *self,
|
|
+ GAsyncResult *res,
|
|
+ GError **error)
|
|
+{
|
|
+ return g_task_propagate_boolean (G_TASK (res), error);
|
|
+}
|
|
+
|
|
+static void
|
|
+mm_base_modem_sync_ready (MMBaseModem *self,
|
|
+ GAsyncResult *res,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ g_autoptr(GError) error;
|
|
+
|
|
+ mm_base_modem_sync_finish (self, res, &error);
|
|
+ if (error) {
|
|
+ mm_obj_warn (self, "synchronization failed");
|
|
+ return;
|
|
+ }
|
|
+ mm_obj_info (self, "synchronization finished");
|
|
+}
|
|
+
|
|
+void
|
|
+mm_base_manager_sync (MMBaseManager *self)
|
|
+{
|
|
+ GHashTableIter iter;
|
|
+ gpointer key, value;
|
|
+
|
|
+ g_return_if_fail (self != NULL);
|
|
+ g_return_if_fail (MM_IS_BASE_MANAGER (self));
|
|
+
|
|
+ /* Refresh each device */
|
|
+ g_hash_table_iter_init (&iter, self->priv->devices);
|
|
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
|
|
+ MMBaseModem *modem = mm_device_peek_modem (MM_DEVICE (value));
|
|
+ /* We just want to start the synchronization, we don't need the result */
|
|
+ mm_base_modem_sync (modem, (GAsyncReadyCallback)mm_base_modem_sync_ready, NULL);
|
|
+ }
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
/*****************************************************************************/
|
|
/* Set logging */
|
|
|
|
diff --git a/src/mm-base-manager.h b/src/mm-base-manager.h
|
|
index d70fa08f..be51d0c0 100644
|
|
--- a/src/mm-base-manager.h
|
|
+++ b/src/mm-base-manager.h
|
|
@@ -66,6 +66,10 @@ void mm_base_manager_start (MMBaseManager *manager,
|
|
void mm_base_manager_shutdown (MMBaseManager *manager,
|
|
gboolean disable);
|
|
|
|
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
|
|
+void mm_base_manager_sync (MMBaseManager *manager);
|
|
+#endif
|
|
+
|
|
guint32 mm_base_manager_num_modems (MMBaseManager *manager);
|
|
|
|
#endif /* MM_BASE_MANAGER_H */
|
|
diff --git a/src/mm-base-modem.c b/src/mm-base-modem.c
|
|
index 926d0712..38f43f8c 100644
|
|
--- a/src/mm-base-modem.c
|
|
+++ b/src/mm-base-modem.c
|
|
@@ -369,6 +369,161 @@ mm_base_modem_grab_port (MMBaseModem *self,
|
|
return TRUE;
|
|
}
|
|
|
|
+/******************************************************************************/
|
|
+
|
|
+typedef struct {
|
|
+ gchar *name;
|
|
+ gulong link_port_grabbed_id;
|
|
+ guint timeout_id;
|
|
+} WaitLinkPortContext;
|
|
+
|
|
+static void
|
|
+wait_link_port_context_free (WaitLinkPortContext *ctx)
|
|
+{
|
|
+ g_assert (!ctx->link_port_grabbed_id);
|
|
+ g_assert (!ctx->timeout_id);
|
|
+ g_free (ctx->name);
|
|
+ g_slice_free (WaitLinkPortContext, ctx);
|
|
+}
|
|
+
|
|
+MMPort *
|
|
+mm_base_modem_wait_link_port_finish (MMBaseModem *self,
|
|
+ GAsyncResult *res,
|
|
+ GError **error)
|
|
+{
|
|
+ return g_task_propagate_pointer (G_TASK (res), error);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+wait_link_port_timeout_cb (GTask *task)
|
|
+{
|
|
+ WaitLinkPortContext *ctx;
|
|
+ MMBaseModem *self;
|
|
+
|
|
+ self = g_task_get_source_object (task);
|
|
+ ctx = g_task_get_task_data (task);
|
|
+
|
|
+ ctx->timeout_id = 0;
|
|
+ g_signal_handler_disconnect (self, ctx->link_port_grabbed_id);
|
|
+ ctx->link_port_grabbed_id = 0;
|
|
+
|
|
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
|
|
+ "Timed out waiting for link port 'net/%s'",
|
|
+ ctx->name);
|
|
+ g_object_unref (task);
|
|
+
|
|
+ return G_SOURCE_REMOVE;
|
|
+}
|
|
+
|
|
+static void
|
|
+wait_link_port_grabbed_cb (MMBaseModem *self,
|
|
+ MMPort *link_port,
|
|
+ GTask *task)
|
|
+{
|
|
+ WaitLinkPortContext *ctx;
|
|
+ MMPortSubsys link_port_subsystem;
|
|
+ const gchar *link_port_name;
|
|
+
|
|
+ ctx = g_task_get_task_data (task);
|
|
+
|
|
+ link_port_subsystem = mm_port_get_subsys (link_port);
|
|
+ link_port_name = mm_port_get_device (link_port);
|
|
+
|
|
+ if (link_port_subsystem != MM_PORT_SUBSYS_NET) {
|
|
+ mm_obj_warn (self, "unexpected link port subsystem grabbed: %s/%s",
|
|
+ mm_port_subsys_get_string (link_port_subsystem),
|
|
+ link_port_name);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (g_strcmp0 (link_port_name, ctx->name) != 0)
|
|
+ return;
|
|
+
|
|
+ /* we got it! */
|
|
+
|
|
+ g_source_remove (ctx->timeout_id);
|
|
+ ctx->timeout_id = 0;
|
|
+ g_signal_handler_disconnect (self, ctx->link_port_grabbed_id);
|
|
+ ctx->link_port_grabbed_id = 0;
|
|
+
|
|
+ g_task_return_pointer (task, g_object_ref (link_port), g_object_unref);
|
|
+ g_object_unref (task);
|
|
+}
|
|
+
|
|
+void
|
|
+mm_base_modem_wait_link_port (MMBaseModem *self,
|
|
+ const gchar *subsystem,
|
|
+ const gchar *name,
|
|
+ guint timeout_ms,
|
|
+ GAsyncReadyCallback callback,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ WaitLinkPortContext *ctx;
|
|
+ GTask *task;
|
|
+ g_autofree gchar *key = NULL;
|
|
+ MMPort *port;
|
|
+
|
|
+ task = g_task_new (self, NULL, callback, user_data);
|
|
+
|
|
+ if (!g_str_equal (subsystem, "net")) {
|
|
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
|
+ "Cannot wait for port '%s/%s', unexpected link port subsystem", subsystem, name);
|
|
+ g_object_unref (task);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ key = g_strdup_printf ("%s%s", subsystem, name);
|
|
+ port = g_hash_table_lookup (self->priv->link_ports, key);
|
|
+ if (port) {
|
|
+ mm_obj_dbg (self, "no need to wait for port '%s/%s': already grabbed", subsystem, name);
|
|
+ g_task_return_pointer (task, g_object_ref (port), g_object_unref);
|
|
+ g_object_unref (task);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ctx = g_slice_new0 (WaitLinkPortContext);
|
|
+ ctx->name = g_strdup (name);
|
|
+ g_task_set_task_data (task, ctx, (GDestroyNotify)wait_link_port_context_free);
|
|
+
|
|
+ /* task ownership shared between timeout and signal handler */
|
|
+ ctx->timeout_id = g_timeout_add (timeout_ms,
|
|
+ (GSourceFunc) wait_link_port_timeout_cb,
|
|
+ task);
|
|
+ ctx->link_port_grabbed_id = g_signal_connect (self,
|
|
+ MM_BASE_MODEM_SIGNAL_LINK_PORT_GRABBED,
|
|
+ G_CALLBACK (wait_link_port_grabbed_cb),
|
|
+ task);
|
|
+
|
|
+ mm_obj_dbg (self, "waiting for port '%s/%s'...", subsystem, name);
|
|
+}
|
|
+
|
|
+/******************************************************************************/
|
|
+
|
|
+static void
|
|
+mm_base_modem_sync_ready (MMBaseModem *self,
|
|
+ GAsyncResult *res)
|
|
+{
|
|
+ g_autoptr (GError) error = NULL;
|
|
+
|
|
+ MM_BASE_MODEM_GET_CLASS (self)->sync_finish (self, res, &error);
|
|
+ if (error) {
|
|
+ mm_obj_warn (self, "synchronization failed");
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+mm_base_modem_sync (MMBaseModem *self,
|
|
+ GAsyncReadyCallback callback,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ g_assert (MM_BASE_MODEM_GET_CLASS (self)->sync != NULL);
|
|
+ g_assert (MM_BASE_MODEM_GET_CLASS (self)->sync_finish != NULL);
|
|
+
|
|
+ MM_BASE_MODEM_GET_CLASS (self)->sync (self,
|
|
+ (GAsyncReadyCallback) mm_base_modem_sync_ready,
|
|
+ NULL);
|
|
+}
|
|
+
|
|
gboolean
|
|
mm_base_modem_disable_finish (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
diff --git a/src/mm-base-modem.h b/src/mm-base-modem.h
|
|
index 24634814..75fd99f5 100644
|
|
--- a/src/mm-base-modem.h
|
|
+++ b/src/mm-base-modem.h
|
|
@@ -101,6 +101,22 @@ struct _MMBaseModemClass {
|
|
gboolean (*disable_finish) (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
GError **error);
|
|
+
|
|
+ /* Modem synchronization.
|
|
+ * When resuming in quick suspend/resume mode,
|
|
+ * this method triggers a synchronization of all modem interfaces */
|
|
+ void (* sync) (MMBaseModem *self,
|
|
+ GAsyncReadyCallback callback,
|
|
+ gpointer user_data);
|
|
+ gboolean (* sync_finish) (MMBaseModem *self,
|
|
+ GAsyncResult *res,
|
|
+ GError **error);
|
|
+
|
|
+ /* signals */
|
|
+ void (* link_port_grabbed) (MMBaseModem *self,
|
|
+ MMPort *link_port);
|
|
+ void (* link_port_released) (MMBaseModem *self,
|
|
+ MMPort *link_port);
|
|
};
|
|
|
|
GType mm_base_modem_get_type (void);
|
|
@@ -199,6 +215,13 @@ gboolean mm_base_modem_disable_finish (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
GError **error);
|
|
|
|
+void mm_base_modem_sync (MMBaseModem *self,
|
|
+ GAsyncReadyCallback callback,
|
|
+ gpointer user_data);
|
|
+gboolean mm_base_modem_sync_finish (MMBaseModem *self,
|
|
+ GAsyncResult *res,
|
|
+ GError **error);
|
|
+
|
|
void mm_base_modem_process_sim_event (MMBaseModem *self);
|
|
|
|
#endif /* MM_BASE_MODEM_H */
|
|
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
|
|
index a25883ac..6e47757e 100644
|
|
--- a/src/mm-broadband-modem.c
|
|
+++ b/src/mm-broadband-modem.c
|
|
@@ -11337,6 +11337,82 @@ enable (MMBaseModem *self,
|
|
|
|
g_object_unref (task);
|
|
}
|
|
+/*****************************************************************************/
|
|
+
|
|
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
|
|
+
|
|
+typedef enum {
|
|
+ SYNCING_STEP_FIRST,
|
|
+ SYNCING_STEP_LAST,
|
|
+} SyncingStep;
|
|
+
|
|
+typedef struct {
|
|
+ SyncingStep step;
|
|
+} SyncingContext;
|
|
+
|
|
+static gboolean
|
|
+synchronize_finish (MMBaseModem *self,
|
|
+ GAsyncResult *res,
|
|
+ GError **error)
|
|
+{
|
|
+ return g_task_propagate_boolean (G_TASK (res), error);
|
|
+}
|
|
+
|
|
+static void
|
|
+syncing_step (GTask *task)
|
|
+{
|
|
+ MMBroadbandModem *self;
|
|
+ SyncingContext *ctx;
|
|
+
|
|
+ /* Don't run new steps if we're cancelled */
|
|
+ if (g_task_return_error_if_cancelled (task)) {
|
|
+ g_object_unref (task);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ self = g_task_get_source_object (task);
|
|
+ ctx = g_task_get_task_data (task);
|
|
+
|
|
+ switch (ctx->step) {
|
|
+ case SYNCING_STEP_FIRST:
|
|
+ ctx->step++;
|
|
+ /* fall through */
|
|
+
|
|
+ case SYNCING_STEP_LAST:
|
|
+ mm_obj_info (self, "resume synchronization state (%d/%d): all done",
|
|
+ ctx->step, SYNCING_STEP_LAST);
|
|
+ /* We are done without errors! */
|
|
+ g_task_return_boolean (task, TRUE);
|
|
+ g_object_unref (task);
|
|
+ return;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ g_assert_not_reached ();
|
|
+}
|
|
+
|
|
+/* 'sync' as function name conflicts with a declared function in unistd.h */
|
|
+static void
|
|
+synchronize (MMBaseModem *self,
|
|
+ GAsyncReadyCallback callback,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ SyncingContext *ctx;
|
|
+ GTask *task;
|
|
+
|
|
+ /* Create SyncingContext */
|
|
+ ctx = g_new0 (SyncingContext, 1);
|
|
+ ctx->step = SYNCING_STEP_FIRST;
|
|
+
|
|
+ /* Create sync steps task and execute it */
|
|
+ task = g_task_new (MM_BROADBAND_MODEM (self), NULL, callback, user_data);
|
|
+ g_task_set_task_data (task, ctx, (GDestroyNotify)g_free);
|
|
+ syncing_step (task);
|
|
+}
|
|
+
|
|
+#endif
|
|
|
|
/*****************************************************************************/
|
|
|
|
@@ -12683,6 +12759,11 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass)
|
|
base_modem_class->disable = disable;
|
|
base_modem_class->disable_finish = disable_finish;
|
|
|
|
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
|
|
+ base_modem_class->sync = synchronize;
|
|
+ base_modem_class->sync_finish = synchronize_finish;
|
|
+#endif
|
|
+
|
|
klass->setup_ports = setup_ports;
|
|
klass->initialization_started = initialization_started;
|
|
klass->initialization_started_finish = initialization_started_finish;
|
|
diff --git a/src/mm-context.c b/src/mm-context.c
|
|
index 6561127e..691ca82b 100644
|
|
--- a/src/mm-context.c
|
|
+++ b/src/mm-context.c
|
|
@@ -227,6 +227,7 @@ static gboolean test_no_udev;
|
|
#endif
|
|
#if defined WITH_SYSTEMD_SUSPEND_RESUME
|
|
static gboolean test_no_suspend_resume;
|
|
+static gboolean test_quick_suspend_resume;
|
|
#endif
|
|
|
|
static const GOptionEntry test_entries[] = {
|
|
@@ -258,6 +259,11 @@ static const GOptionEntry test_entries[] = {
|
|
"Disable suspend/resume support at runtime even if available",
|
|
NULL
|
|
},
|
|
+ {
|
|
+ "test-quick-suspend-resume", 0, 0, G_OPTION_ARG_NONE, &test_quick_suspend_resume,
|
|
+ "Enable quick suspend/resume support for modems which stay on during host suspension",
|
|
+ NULL
|
|
+ },
|
|
#endif
|
|
{ NULL }
|
|
};
|
|
@@ -308,6 +314,11 @@ mm_context_get_test_no_suspend_resume (void)
|
|
{
|
|
return test_no_suspend_resume;
|
|
}
|
|
+gboolean
|
|
+mm_context_get_test_quick_suspend_resume (void)
|
|
+{
|
|
+ return test_quick_suspend_resume;
|
|
+}
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
diff --git a/src/mm-context.h b/src/mm-context.h
|
|
index 721fee88..276567fd 100644
|
|
--- a/src/mm-context.h
|
|
+++ b/src/mm-context.h
|
|
@@ -51,6 +51,7 @@ gboolean mm_context_get_test_no_udev (void);
|
|
#endif
|
|
#if defined WITH_SYSTEMD_SUSPEND_RESUME
|
|
gboolean mm_context_get_test_no_suspend_resume (void);
|
|
+gboolean mm_context_get_test_quick_suspend_resume (void);
|
|
#endif
|
|
|
|
#endif /* MM_CONTEXT_H */
|
|
--
|
|
2.31.1
|
|
|