base-manager,sleep-monitor: wait for modem operations before sleeping
Signed-off-by: Dan Williams <dan@ioncontrol.co>
This commit is contained in:
+36
-15
@@ -59,14 +59,15 @@ quit_cb (gpointer user_data)
|
||||
#if defined WITH_SUSPEND_RESUME
|
||||
|
||||
static void
|
||||
sleeping_cb (MMSleepMonitor *sleep_monitor)
|
||||
sleeping_cb (MMSleepMonitor *sleep_monitor,
|
||||
MMSleepContext *ctx)
|
||||
{
|
||||
if (mm_context_get_test_low_power_suspend_resume ()) {
|
||||
mm_dbg ("removing devices and setting them in low power mode... (sleeping)");
|
||||
mm_base_manager_shutdown (manager, TRUE, TRUE, TRUE);
|
||||
mm_base_manager_shutdown (manager, TRUE, TRUE, TRUE, ctx);
|
||||
} else {
|
||||
mm_dbg ("removing devices... (sleeping)");
|
||||
mm_base_manager_shutdown (manager, FALSE, FALSE, TRUE);
|
||||
mm_base_manager_shutdown (manager, FALSE, FALSE, TRUE, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,11 +79,16 @@ resuming_cb (MMSleepMonitor *sleep_monitor)
|
||||
}
|
||||
|
||||
static void
|
||||
sleeping_quick_cb (MMSleepMonitor *sleep_monitor)
|
||||
sleeping_quick_cb (MMSleepMonitor *sleep_monitor,
|
||||
MMSleepContext *ctx)
|
||||
{
|
||||
if (mm_context_get_test_low_power_suspend_resume ()) {
|
||||
mm_dbg ("setting modem in low power mode... (sleeping)");
|
||||
mm_base_manager_shutdown (manager, TRUE, TRUE, FALSE);
|
||||
mm_base_manager_shutdown (manager, TRUE, TRUE, FALSE, ctx);
|
||||
} else {
|
||||
/* Don't need to wait for anything; just suspend */
|
||||
mm_dbg ("leaving modem powered... (sleeping)");
|
||||
mm_sleep_context_complete (ctx, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +174,18 @@ register_dbus_errors (void)
|
||||
g_dbus_error_register_error (G_IO_ERROR, G_IO_ERROR_CANCELLED, MM_CORE_ERROR_DBUS_PREFIX ".Cancelled");
|
||||
}
|
||||
|
||||
static void
|
||||
shutdown_done (MMSleepContext *sleep_ctx,
|
||||
GError *error,
|
||||
GMainLoop *quit_loop)
|
||||
{
|
||||
if (error)
|
||||
mm_warn ("shutdown failed: %s", error->message);
|
||||
else
|
||||
mm_msg ("shutdown complete");
|
||||
g_main_loop_quit (quit_loop);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
@@ -241,28 +259,31 @@ main (int argc, char *argv[])
|
||||
loop = NULL;
|
||||
|
||||
if (manager) {
|
||||
GTimer *timer;
|
||||
MMSleepContext *ctx;
|
||||
guint sleep_done_id;
|
||||
|
||||
mm_base_manager_shutdown (manager, TRUE, FALSE, TRUE);
|
||||
ctx = mm_sleep_context_new (MAX_SHUTDOWN_TIME_SECS);
|
||||
sleep_done_id = g_signal_connect (ctx,
|
||||
MM_SLEEP_CONTEXT_DONE,
|
||||
(GCallback)shutdown_done,
|
||||
inner);
|
||||
|
||||
mm_base_manager_shutdown (manager, TRUE, FALSE, TRUE, ctx);
|
||||
|
||||
/* Wait for all modems to be disabled and removed, but don't wait
|
||||
* forever: if disabling the modems takes longer than 20s, just
|
||||
* shutdown anyway. */
|
||||
timer = g_timer_new ();
|
||||
while (mm_base_manager_num_modems (manager) &&
|
||||
g_timer_elapsed (timer, NULL) < (gdouble)MAX_SHUTDOWN_TIME_SECS) {
|
||||
GMainContext *ctx = g_main_loop_get_context (inner);
|
||||
while (mm_base_manager_num_modems (manager))
|
||||
g_main_loop_run (inner);
|
||||
|
||||
g_main_context_iteration (ctx, FALSE);
|
||||
g_usleep (50);
|
||||
}
|
||||
g_signal_handler_disconnect (ctx, sleep_done_id);
|
||||
g_object_unref (ctx);
|
||||
|
||||
if (mm_base_manager_num_modems (manager))
|
||||
mm_warn ("disabling modems took too long, shutting down with %u modems around",
|
||||
mm_base_manager_num_modems (manager));
|
||||
|
||||
g_object_unref (manager);
|
||||
g_timer_destroy (timer);
|
||||
}
|
||||
|
||||
g_main_loop_unref (inner);
|
||||
|
||||
@@ -299,6 +299,7 @@ sources = files(
|
||||
'mm-port-probe-at.c',
|
||||
'mm-private-boxed-types.c',
|
||||
'mm-sms-list.c',
|
||||
'mm-sleep-context.c',
|
||||
)
|
||||
|
||||
sources += daemon_enums_sources
|
||||
|
||||
+136
-56
@@ -367,7 +367,7 @@ device_removed (MMBaseManager *self,
|
||||
|
||||
/* The device may have already been removed from the tracking HT, we
|
||||
* just try to remove it and if it fails, we ignore it */
|
||||
mm_device_remove_modem (device);
|
||||
mm_device_remove_modem_quick (device);
|
||||
g_hash_table_remove (self->priv->devices, mm_device_get_uid (device));
|
||||
}
|
||||
}
|
||||
@@ -455,6 +455,19 @@ existing_port (MMBaseManager *self,
|
||||
device_support_check_add_single_port (self, device, port);
|
||||
}
|
||||
|
||||
static void
|
||||
additional_port_modem_remove_ready (MMDevice *device,
|
||||
GAsyncResult *res,
|
||||
MMBaseManager *self)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
if (!mm_device_remove_modem_finish (device, res, &error))
|
||||
mm_obj_warn (device, "removing modem failed: %s", error->message);
|
||||
device_support_check_add_all_ports (self, device);
|
||||
g_object_unref (self);
|
||||
}
|
||||
|
||||
static void
|
||||
additional_port (MMBaseManager *self,
|
||||
MMDevice *device,
|
||||
@@ -501,8 +514,9 @@ additional_port (MMBaseManager *self,
|
||||
|
||||
mm_obj_info (self, "last modem object creation in device '%s' succeeded, but we have a new port addition, will retry", uid);
|
||||
g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
|
||||
mm_device_remove_modem (device);
|
||||
device_support_check_add_all_ports (self, device);
|
||||
mm_device_remove_modem (device,
|
||||
(GAsyncReadyCallback)additional_port_modem_remove_ready,
|
||||
g_object_ref (self));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -875,53 +889,110 @@ mm_base_manager_start (MMBaseManager *self,
|
||||
|
||||
typedef struct {
|
||||
MMBaseManager *self;
|
||||
|
||||
volatile gint refcount;
|
||||
gboolean low_power;
|
||||
gboolean remove;
|
||||
} DisableContext;
|
||||
MMSleepContext *sleep_ctx;
|
||||
} ShutdownContext;
|
||||
|
||||
static void
|
||||
disable_context_free (DisableContext *ctx)
|
||||
static ShutdownContext *
|
||||
shutdown_context_new (MMBaseManager *self,
|
||||
gboolean low_power,
|
||||
gboolean remove,
|
||||
MMSleepContext *sleep_ctx)
|
||||
{
|
||||
g_object_unref (ctx->self);
|
||||
g_slice_free (DisableContext, ctx);
|
||||
ShutdownContext *ctx;
|
||||
|
||||
ctx = g_slice_new0 (ShutdownContext);
|
||||
ctx->refcount = 1;
|
||||
ctx->self = g_object_ref (self);
|
||||
ctx->low_power = low_power;
|
||||
ctx->remove = remove;
|
||||
ctx->sleep_ctx = g_object_ref (sleep_ctx);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void
|
||||
remove_device_after_disable (MMBaseModem *modem,
|
||||
DisableContext *ctx)
|
||||
shutdown_context_ref (ShutdownContext *ctx)
|
||||
{
|
||||
MMDevice *device;
|
||||
g_atomic_int_inc (&ctx->refcount);
|
||||
}
|
||||
|
||||
static void
|
||||
shutdown_context_unref (ShutdownContext *ctx)
|
||||
{
|
||||
if (g_atomic_int_dec_and_test (&ctx->refcount)) {
|
||||
mm_obj_dbg (ctx->self, "shutdown complete");
|
||||
mm_sleep_context_complete (ctx->sleep_ctx, NULL);
|
||||
g_object_unref (ctx->sleep_ctx);
|
||||
g_object_unref (ctx->self);
|
||||
g_slice_free (ShutdownContext, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
modem_remove_ready (MMDevice *device,
|
||||
GAsyncResult *res,
|
||||
ShutdownContext *ctx)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
if (!mm_device_remove_modem_finish (device, res, &error))
|
||||
mm_obj_warn (ctx->self, "removing modem failed: %s", error->message);
|
||||
|
||||
device = find_device_by_modem (ctx->self, modem);
|
||||
if (device) {
|
||||
g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
|
||||
mm_device_remove_modem (device);
|
||||
g_hash_table_remove (ctx->self->priv->devices, mm_device_get_uid (device));
|
||||
/* balance foreach_remove() and remove_device_after_disable() */
|
||||
g_object_unref (device);
|
||||
|
||||
shutdown_context_unref (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
shutdown_remove_device (MMDevice *device,
|
||||
MMBaseModem *modem,
|
||||
ShutdownContext *ctx)
|
||||
{
|
||||
if (!device) {
|
||||
g_assert (modem);
|
||||
device = find_device_by_modem (ctx->self, modem);
|
||||
}
|
||||
|
||||
disable_context_free (ctx);
|
||||
if (device) {
|
||||
mm_obj_dbg (ctx->self, "removing modem device %s", mm_device_get_uid (device));
|
||||
if (modem)
|
||||
g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
|
||||
mm_device_remove_modem (g_object_ref (device), /* keep alive over removal */
|
||||
(GAsyncReadyCallback)modem_remove_ready,
|
||||
ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
shutdown_context_unref (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
shutdown_low_power_ready (MMIfaceModem *modem,
|
||||
GAsyncResult *res,
|
||||
DisableContext *ctx)
|
||||
ShutdownContext *ctx)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
if (!mm_iface_modem_set_power_state_finish (modem, res, NULL, &error))
|
||||
mm_obj_info (ctx->self, "changing to low power state failed: %s", error->message);
|
||||
|
||||
if (ctx->remove)
|
||||
remove_device_after_disable (MM_BASE_MODEM (modem), ctx);
|
||||
else
|
||||
disable_context_free (ctx);
|
||||
if (ctx->remove) {
|
||||
shutdown_remove_device (NULL, MM_BASE_MODEM (modem), ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
shutdown_context_unref (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
shutdown_disable_ready (MMBaseModem *modem,
|
||||
GAsyncResult *res,
|
||||
DisableContext *ctx)
|
||||
ShutdownContext *ctx)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
@@ -931,6 +1002,9 @@ shutdown_disable_ready (MMBaseModem *modem,
|
||||
}
|
||||
/* Bring the modem to low power mode if requested */
|
||||
else if (ctx->low_power) {
|
||||
mm_obj_dbg (ctx->self,
|
||||
"setting modem%d to low-power state",
|
||||
mm_base_modem_get_dbus_id (modem));
|
||||
mm_iface_modem_set_power_state (MM_IFACE_MODEM (modem),
|
||||
MM_MODEM_POWER_STATE_LOW,
|
||||
(GAsyncReadyCallback)shutdown_low_power_ready,
|
||||
@@ -938,47 +1012,46 @@ shutdown_disable_ready (MMBaseModem *modem,
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->remove)
|
||||
remove_device_after_disable (modem, ctx);
|
||||
else
|
||||
disable_context_free (ctx);
|
||||
if (ctx->remove) {
|
||||
shutdown_remove_device (NULL, modem, ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
shutdown_context_unref (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
foreach_disable (gpointer key,
|
||||
MMDevice *device,
|
||||
DisableContext *foreach_ctx)
|
||||
ShutdownContext *ctx)
|
||||
{
|
||||
MMBaseModem *modem;
|
||||
DisableContext *ctx;
|
||||
|
||||
modem = mm_device_peek_modem (device);
|
||||
if (!modem)
|
||||
return;
|
||||
|
||||
ctx = g_slice_new0 (DisableContext);
|
||||
ctx->self = g_object_ref (foreach_ctx->self);
|
||||
ctx->low_power = foreach_ctx->low_power;
|
||||
ctx->remove = foreach_ctx->remove;
|
||||
|
||||
if (modem) {
|
||||
mm_obj_dbg (ctx->self, "disabling modem%d", mm_base_modem_get_dbus_id (modem));
|
||||
shutdown_context_ref (ctx);
|
||||
mm_base_modem_disable (modem,
|
||||
MM_BASE_MODEM_OPERATION_LOCK_REQUIRED,
|
||||
MM_BASE_MODEM_OPERATION_PRIORITY_OVERRIDE,
|
||||
(GAsyncReadyCallback)shutdown_disable_ready,
|
||||
ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
foreach_remove (gpointer key,
|
||||
MMDevice *device,
|
||||
MMBaseManager *self)
|
||||
ShutdownContext *ctx)
|
||||
{
|
||||
MMBaseModem *modem;
|
||||
|
||||
modem = mm_device_peek_modem (device);
|
||||
if (modem)
|
||||
g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
|
||||
mm_device_remove_modem (device);
|
||||
|
||||
shutdown_context_ref (ctx);
|
||||
shutdown_remove_device (device, NULL, ctx);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -986,33 +1059,40 @@ void
|
||||
mm_base_manager_shutdown (MMBaseManager *self,
|
||||
gboolean disable,
|
||||
gboolean low_power,
|
||||
gboolean remove)
|
||||
gboolean remove,
|
||||
MMSleepContext *sleep_ctx)
|
||||
{
|
||||
ShutdownContext *ctx;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (MM_IS_BASE_MANAGER (self));
|
||||
|
||||
ctx = shutdown_context_new (self, low_power, remove, sleep_ctx);
|
||||
|
||||
/* Cancel all ongoing auth requests */
|
||||
g_cancellable_cancel (self->priv->authp_cancellable);
|
||||
|
||||
mm_obj_dbg (self,
|
||||
"manager shutting down... (%s%s%s%s%s)",
|
||||
disable ? "disable" : "",
|
||||
disable && (low_power || remove) ? "," : "",
|
||||
low_power ? "low-power" : "",
|
||||
(low_power && remove) ? "," : "",
|
||||
remove ? "remove" : "");
|
||||
|
||||
if (disable) {
|
||||
DisableContext foreach_ctx = {
|
||||
.self = self,
|
||||
.low_power = low_power,
|
||||
.remove = remove,
|
||||
};
|
||||
g_hash_table_foreach (self->priv->devices, (GHFunc)foreach_disable, &foreach_ctx);
|
||||
|
||||
/* Disabling may take a few iterations of the mainloop, so the caller
|
||||
* has to iterate the mainloop until all devices have been disabled and
|
||||
* removed.
|
||||
*/
|
||||
return;
|
||||
mm_obj_dbg (self,
|
||||
"shutdown disabling %d devices",
|
||||
g_hash_table_size (self->priv->devices));
|
||||
g_hash_table_foreach (self->priv->devices, (GHFunc)foreach_disable, ctx);
|
||||
} else if (remove) {
|
||||
mm_obj_dbg (self,
|
||||
"shutdown removing %d devices",
|
||||
g_hash_table_size (self->priv->devices));
|
||||
g_hash_table_foreach_remove (self->priv->devices, (GHRFunc)foreach_remove, ctx);
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
/* Otherwise, just remove directly */
|
||||
g_hash_table_foreach_remove (self->priv->devices, (GHRFunc)foreach_remove, self);
|
||||
}
|
||||
shutdown_context_unref (ctx);
|
||||
}
|
||||
|
||||
guint32
|
||||
@@ -1713,7 +1793,7 @@ handle_set_profile (MmGdbusTest *skeleton,
|
||||
out:
|
||||
|
||||
if (error) {
|
||||
mm_device_remove_modem (device);
|
||||
mm_device_remove_modem_quick (device);
|
||||
g_hash_table_remove (self->priv->devices, mm_device_get_uid (device));
|
||||
mm_dbus_method_invocation_return_gerror (invocation, error);
|
||||
g_error_free (error);
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "mm-filter.h"
|
||||
#include "mm-gdbus-manager.h"
|
||||
#include "mm-sleep-context.h"
|
||||
|
||||
#define MM_TYPE_BASE_MANAGER (mm_base_manager_get_type ())
|
||||
#define MM_BASE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BASE_MANAGER, MMBaseManager))
|
||||
@@ -74,7 +75,8 @@ void mm_base_manager_start (MMBaseManager *manager,
|
||||
void mm_base_manager_shutdown (MMBaseManager *manager,
|
||||
gboolean disable,
|
||||
gboolean power_low,
|
||||
gboolean remove);
|
||||
gboolean remove,
|
||||
MMSleepContext *ctx);
|
||||
|
||||
#if defined WITH_SUSPEND_RESUME
|
||||
void mm_base_manager_sync (MMBaseManager *manager);
|
||||
|
||||
+98
-13
@@ -459,14 +459,65 @@ clear_modem (MMDevice *self)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mm_device_remove_modem (MMDevice *self)
|
||||
gboolean
|
||||
mm_device_remove_modem_finish (MMDevice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
if (!self->priv->modem)
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
teardown_ports_ready (MMBaseModem *modem,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
MMDevice *self;
|
||||
GError *error = NULL;
|
||||
gboolean success;
|
||||
|
||||
self = g_task_get_source_object (task);
|
||||
|
||||
success = mm_base_modem_teardown_ports_finish (modem, res, &error);
|
||||
clear_modem (self);
|
||||
|
||||
if (!success)
|
||||
g_task_return_error (task, error);
|
||||
else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
void
|
||||
mm_device_remove_modem (MMDevice *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
if (!self->priv->modem) {
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
unexport_modem (self);
|
||||
mm_base_modem_teardown_ports (self->priv->modem,
|
||||
(GAsyncReadyCallback)teardown_ports_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
/* Should only be used from testing code or when the hardware is no longer
|
||||
* present and cannot be gracefully cleaned up.
|
||||
*/
|
||||
void
|
||||
mm_device_remove_modem_quick (MMDevice *self)
|
||||
{
|
||||
if (self->priv->modem) {
|
||||
unexport_modem (self);
|
||||
clear_modem (self);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -491,6 +542,25 @@ reprobe (MMDevice *self)
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
modem_valid_remove_ready (MMDevice *self,
|
||||
GAsyncResult *res,
|
||||
MMBaseModem *modem)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
if (!mm_device_remove_modem_finish (self, res, &error))
|
||||
mm_obj_warn (self, "removing modem failed: %s", error->message);
|
||||
|
||||
if (mm_base_modem_get_reprobe (modem)) {
|
||||
if (self->priv->reprobe_id)
|
||||
g_source_remove (self->priv->reprobe_id);
|
||||
self->priv->reprobe_id = g_timeout_add_seconds (REPROBE_SECS, (GSourceFunc)reprobe, self);
|
||||
}
|
||||
|
||||
g_object_unref (modem);
|
||||
}
|
||||
|
||||
static void
|
||||
modem_valid (MMBaseModem *modem,
|
||||
GParamSpec *pspec,
|
||||
@@ -498,9 +568,9 @@ modem_valid (MMBaseModem *modem,
|
||||
{
|
||||
if (!mm_base_modem_get_valid (modem)) {
|
||||
/* Modem no longer valid */
|
||||
mm_device_remove_modem (self);
|
||||
if (mm_base_modem_get_reprobe (modem))
|
||||
self->priv->reprobe_id = g_timeout_add_seconds (REPROBE_SECS, (GSourceFunc)reprobe, self);
|
||||
mm_device_remove_modem (self,
|
||||
(GAsyncReadyCallback)modem_valid_remove_ready,
|
||||
g_object_ref (modem));
|
||||
} else {
|
||||
/* Modem now valid, export it, but only if we really have it around.
|
||||
* It may happen that the initialization sequence fails because the
|
||||
@@ -732,6 +802,19 @@ mm_device_inhibit_finish (MMDevice *self,
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
inhibit_disable_modem_remove_ready (MMDevice *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
if (!mm_device_remove_modem_finish (self, res, &error))
|
||||
mm_obj_warn (self, "removing modem failed: %s", error->message);
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static void
|
||||
inhibit_disable_ready (MMBaseModem *modem,
|
||||
GAsyncResult *res,
|
||||
@@ -742,14 +825,16 @@ inhibit_disable_ready (MMBaseModem *modem,
|
||||
|
||||
self = g_task_get_source_object (task);
|
||||
|
||||
if (!mm_base_modem_disable_finish (modem, res, &error))
|
||||
if (!mm_base_modem_disable_finish (modem, res, &error)) {
|
||||
g_task_return_error (task, error);
|
||||
else {
|
||||
g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
|
||||
mm_device_remove_modem (self);
|
||||
g_task_return_boolean (task, TRUE);
|
||||
}
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
|
||||
mm_device_remove_modem (self,
|
||||
(GAsyncReadyCallback)inhibit_disable_modem_remove_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -899,7 +984,7 @@ set_property (GObject *object,
|
||||
self->priv->plugin = g_value_dup_object (value);
|
||||
break;
|
||||
case PROP_MODEM:
|
||||
clear_modem (self);
|
||||
g_assert (self->priv->modem == NULL);
|
||||
self->priv->modem = g_value_dup_object (value);
|
||||
break;
|
||||
case PROP_HOTPLUGGED:
|
||||
|
||||
+7
-1
@@ -82,7 +82,13 @@ void mm_device_release_port_name (MMDevice *self,
|
||||
|
||||
gboolean mm_device_create_modem (MMDevice *self,
|
||||
GError **error);
|
||||
void mm_device_remove_modem (MMDevice *self);
|
||||
void mm_device_remove_modem (MMDevice *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean mm_device_remove_modem_finish (MMDevice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
void mm_device_remove_modem_quick (MMDevice *self);
|
||||
void mm_device_initialize_modem (MMDevice *self);
|
||||
|
||||
void mm_device_inhibit (MMDevice *self,
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
* This program 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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:
|
||||
*
|
||||
* Copyright 2025 Dan Williams <dan@ioncontrol.co>
|
||||
*/
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "mm-utils.h"
|
||||
|
||||
#include "mm-sleep-context.h"
|
||||
|
||||
static void log_object_iface_init (MMLogObjectInterface *iface);
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (MMSleepContext, mm_sleep_context, G_TYPE_OBJECT, 0,
|
||||
G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
|
||||
|
||||
struct _MMSleepContextPrivate {
|
||||
GTask *task;
|
||||
guint timeout_id;
|
||||
};
|
||||
|
||||
enum {
|
||||
DONE,
|
||||
LAST_SIGNAL,
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static gchar *
|
||||
log_object_build_id (MMLogObject *_self)
|
||||
{
|
||||
return g_strdup_printf ("sleep-context");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
timeout_cleanup (MMSleepContext *self)
|
||||
{
|
||||
if (self->priv->timeout_id)
|
||||
g_source_remove (self->priv->timeout_id);
|
||||
self->priv->timeout_id = 0;
|
||||
}
|
||||
|
||||
void
|
||||
mm_sleep_context_complete (MMSleepContext *self,
|
||||
GError *error)
|
||||
{
|
||||
timeout_cleanup (self);
|
||||
|
||||
if (error) {
|
||||
mm_obj_dbg (self, "completing with error '%s'", error->message);
|
||||
g_task_return_error (self->priv->task, error);
|
||||
} else {
|
||||
mm_obj_dbg (self, "completing with success");
|
||||
g_task_return_boolean (self->priv->task, TRUE);
|
||||
}
|
||||
g_clear_object (&self->priv->task);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
operation_timeout (MMSleepContext *self)
|
||||
{
|
||||
mm_obj_dbg (self, "timeout");
|
||||
mm_sleep_context_complete (self,
|
||||
g_error_new_literal (G_IO_ERROR,
|
||||
G_IO_ERROR_TIMED_OUT,
|
||||
"timeout waiting for sleep preparation to complete"));
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
operation_ready (MMSleepContext *self,
|
||||
GAsyncResult *res)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
gboolean success;
|
||||
|
||||
mm_obj_dbg (self, "notifying listeners task is done");
|
||||
|
||||
success = g_task_propagate_boolean (G_TASK (res), &error);
|
||||
g_assert (success || error);
|
||||
g_signal_emit (self, signals[DONE], 0, error);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
MMSleepContext *
|
||||
mm_sleep_context_new (guint timeout_seconds)
|
||||
{
|
||||
MMSleepContext *self;
|
||||
|
||||
self = MM_SLEEP_CONTEXT (g_object_new (MM_TYPE_SLEEP_CONTEXT, NULL));
|
||||
self->priv->task = g_task_new (self,
|
||||
NULL,
|
||||
(GAsyncReadyCallback)operation_ready,
|
||||
NULL);
|
||||
self->priv->timeout_id = g_timeout_add_seconds (timeout_seconds,
|
||||
(GSourceFunc)operation_timeout,
|
||||
self);
|
||||
mm_obj_dbg (self, "new context with %d second timeout", timeout_seconds);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static void
|
||||
log_object_iface_init (MMLogObjectInterface *iface)
|
||||
{
|
||||
iface->build_id = log_object_build_id;
|
||||
}
|
||||
|
||||
static void
|
||||
mm_sleep_context_init (MMSleepContext *self)
|
||||
{
|
||||
/* Initialize opaque pointer to private data */
|
||||
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
||||
MM_TYPE_SLEEP_CONTEXT,
|
||||
MMSleepContextPrivate);
|
||||
}
|
||||
|
||||
static void
|
||||
dispose (GObject *object)
|
||||
{
|
||||
MMSleepContext *self = MM_SLEEP_CONTEXT (object);
|
||||
|
||||
timeout_cleanup (self);
|
||||
/* Task must always be completed and cleared before disposal */
|
||||
g_assert (self->priv->task == NULL);
|
||||
|
||||
G_OBJECT_CLASS (mm_sleep_context_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
mm_sleep_context_class_init (MMSleepContextClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (object_class, sizeof (MMSleepContextPrivate));
|
||||
|
||||
/* Virtual methods */
|
||||
object_class->dispose = dispose;
|
||||
|
||||
signals[DONE] = g_signal_new (MM_SLEEP_CONTEXT_DONE,
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
G_STRUCT_OFFSET (MMSleepContextClass, done),
|
||||
NULL, /* accumulator */
|
||||
NULL, /* accumulator data */
|
||||
g_cclosure_marshal_generic,
|
||||
G_TYPE_NONE,
|
||||
1,
|
||||
G_TYPE_ERROR);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
* This program 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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:
|
||||
*
|
||||
* Copyright 2025 Dan Williams <dan@ioncontrol.co>
|
||||
*/
|
||||
|
||||
#ifndef MM_SLEEP_CONTEXT_H
|
||||
#define MM_SLEEP_CONTEXT_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define MM_TYPE_SLEEP_CONTEXT (mm_sleep_context_get_type ())
|
||||
#define MM_SLEEP_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SLEEP_CONTEXT, MMSleepContext))
|
||||
#define MM_SLEEP_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SLEEP_CONTEXT, MMSleepContextClass))
|
||||
#define MM_IS_SLEEP_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SLEEP_CONTEXT))
|
||||
#define MM_IS_SLEEP_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SLEEP_CONTEXT))
|
||||
#define MM_SLEEP_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SLEEP_CONTEXT, MMSleepContextClass))
|
||||
|
||||
#define MM_SLEEP_CONTEXT_DONE "done"
|
||||
|
||||
typedef struct _MMSleepContext MMSleepContext;
|
||||
typedef struct _MMSleepContextClass MMSleepContextClass;
|
||||
typedef struct _MMSleepContextPrivate MMSleepContextPrivate;
|
||||
|
||||
struct _MMSleepContext {
|
||||
GObject parent;
|
||||
|
||||
MMSleepContextPrivate *priv;
|
||||
};
|
||||
|
||||
struct _MMSleepContextClass {
|
||||
GObjectClass parent;
|
||||
|
||||
void (*done) (MMSleepContext *ctx, GError *error);
|
||||
};
|
||||
|
||||
GType mm_sleep_context_get_type (void);
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSleepContext, g_object_unref)
|
||||
|
||||
MMSleepContext *mm_sleep_context_new (guint timeout_seconds);
|
||||
|
||||
/* The MMSleepContext assumes ownership of @error */
|
||||
void mm_sleep_context_complete (MMSleepContext *self,
|
||||
GError *error);
|
||||
|
||||
void mm_sleep_context_dispose (MMSleepContext *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* MM_SLEEP_CONTEXT_H */
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "mm-log-object.h"
|
||||
#include "mm-utils.h"
|
||||
#include "mm-sleep-monitor.h"
|
||||
#include "mm-sleep-context.h"
|
||||
|
||||
#define PD_NAME "org.chromium.PowerManager"
|
||||
#define PD_PATH "/org/chromium/PowerManager"
|
||||
@@ -38,12 +39,14 @@ struct _MMSleepMonitor {
|
||||
GObject parent_instance;
|
||||
|
||||
GDBusProxy *pd_proxy;
|
||||
MMSleepContext *sleep_ctx;
|
||||
guint sleep_done_id;
|
||||
};
|
||||
|
||||
struct _MMSleepMonitorClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (*sleeping) (MMSleepMonitor *monitor);
|
||||
void (*sleeping) (MMSleepMonitor *monitor, MMSleepContext *ctx);
|
||||
void (*resuming) (MMSleepMonitor *monitor);
|
||||
};
|
||||
|
||||
@@ -70,6 +73,26 @@ log_object_build_id (MMLogObject *_self)
|
||||
|
||||
/********************************************************************/
|
||||
|
||||
static void
|
||||
cleanup_sleep_context (MMSleepMonitor *self)
|
||||
{
|
||||
if (self->sleep_ctx && self->sleep_done_id)
|
||||
g_signal_handler_disconnect (self->sleep_ctx, self->sleep_done_id);
|
||||
self->sleep_done_id = 0;
|
||||
g_clear_object (&self->sleep_ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
sleep_context_done (MMSleepContext *sleep_ctx,
|
||||
GError *error,
|
||||
MMSleepMonitor *self)
|
||||
{
|
||||
if (error)
|
||||
mm_obj_warn (self, "sleep context failed: %s", error->message);
|
||||
mm_obj_msg (self, "ready to sleep");
|
||||
cleanup_sleep_context (self);
|
||||
}
|
||||
|
||||
static void
|
||||
signal_cb (GDBusProxy *proxy,
|
||||
const gchar *sendername,
|
||||
@@ -79,15 +102,25 @@ signal_cb (GDBusProxy *proxy,
|
||||
{
|
||||
MMSleepMonitor *self = data;
|
||||
|
||||
if (proxy == self->pd_proxy) {
|
||||
if (proxy != self->pd_proxy)
|
||||
return;
|
||||
|
||||
if (strcmp (signalname, "SuspendImminent") == 0) {
|
||||
mm_obj_msg (self, "system suspend signal from powerd");
|
||||
g_signal_emit (self, signals[SLEEPING], 0);
|
||||
if (self->sleep_ctx || self->sleep_done_id) {
|
||||
mm_obj_warn (self, "clearing unfinished sleep context...");
|
||||
cleanup_sleep_context (self);
|
||||
}
|
||||
self->sleep_ctx = mm_sleep_context_new (5);
|
||||
self->sleep_done_id = g_signal_connect (self->sleep_ctx,
|
||||
MM_SLEEP_CONTEXT_DONE,
|
||||
(GCallback)sleep_context_done,
|
||||
self);
|
||||
|
||||
g_signal_emit (self, signals[SLEEPING], 0, self->sleep_ctx);
|
||||
} else if (strcmp (signalname, "SuspendDone") == 0) {
|
||||
mm_obj_msg (self, "system resume signal from powerd");
|
||||
g_signal_emit (self, signals[RESUMING], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -120,15 +153,14 @@ mm_sleep_monitor_init (MMSleepMonitor *self)
|
||||
}
|
||||
|
||||
static void
|
||||
finalize (GObject *object)
|
||||
dispose (GObject *object)
|
||||
{
|
||||
MMSleepMonitor *self = MM_SLEEP_MONITOR (object);
|
||||
|
||||
if (self->pd_proxy)
|
||||
g_object_unref (self->pd_proxy);
|
||||
cleanup_sleep_context (self);
|
||||
g_clear_object (&self->pd_proxy);
|
||||
|
||||
if (G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize != NULL)
|
||||
G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize (object);
|
||||
G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -144,7 +176,7 @@ mm_sleep_monitor_class_init (MMSleepMonitorClass *klass)
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = finalize;
|
||||
gobject_class->dispose = dispose;
|
||||
|
||||
signals[SLEEPING] = g_signal_new (MM_SLEEP_MONITOR_SLEEPING,
|
||||
MM_TYPE_SLEEP_MONITOR,
|
||||
@@ -152,8 +184,8 @@ mm_sleep_monitor_class_init (MMSleepMonitorClass *klass)
|
||||
G_STRUCT_OFFSET (MMSleepMonitorClass, sleeping),
|
||||
NULL, /* accumulator */
|
||||
NULL, /* accumulator data */
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
g_cclosure_marshal_VOID__OBJECT,
|
||||
G_TYPE_NONE, 1, G_TYPE_CANCELLABLE);
|
||||
signals[RESUMING] = g_signal_new (MM_SLEEP_MONITOR_RESUMING,
|
||||
MM_TYPE_SLEEP_MONITOR,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "mm-log-object.h"
|
||||
#include "mm-utils.h"
|
||||
#include "mm-sleep-monitor.h"
|
||||
#include "mm-sleep-context.h"
|
||||
|
||||
#define SD_NAME "org.freedesktop.login1"
|
||||
#define SD_PATH "/org/freedesktop/login1"
|
||||
@@ -39,12 +40,14 @@ struct _MMSleepMonitor {
|
||||
|
||||
GDBusProxy *sd_proxy;
|
||||
gint inhibit_fd;
|
||||
MMSleepContext *sleep_ctx;
|
||||
guint sleep_done_id;
|
||||
};
|
||||
|
||||
struct _MMSleepMonitorClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (*sleeping) (MMSleepMonitor *monitor);
|
||||
void (*sleeping) (MMSleepMonitor *monitor, MMSleepContext *ctx);
|
||||
void (*resuming) (MMSleepMonitor *monitor);
|
||||
};
|
||||
|
||||
@@ -137,6 +140,27 @@ take_inhibitor (MMSleepMonitor *self)
|
||||
self);
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup_sleep_context (MMSleepMonitor *self)
|
||||
{
|
||||
if (self->sleep_ctx && self->sleep_done_id)
|
||||
g_signal_handler_disconnect (self->sleep_ctx, self->sleep_done_id);
|
||||
self->sleep_done_id = 0;
|
||||
g_clear_object (&self->sleep_ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
sleep_context_done (MMSleepContext *sleep_ctx,
|
||||
GError *error,
|
||||
MMSleepMonitor *self)
|
||||
{
|
||||
if (error)
|
||||
mm_obj_warn (self, "sleep context failed: %s", error->message);
|
||||
mm_obj_msg (self, "ready to sleep; dropping inhibitor");
|
||||
drop_inhibitor (self);
|
||||
cleanup_sleep_context (self);
|
||||
}
|
||||
|
||||
static void
|
||||
signal_cb (GDBusProxy *proxy,
|
||||
const gchar *sendername,
|
||||
@@ -154,8 +178,17 @@ signal_cb (GDBusProxy *proxy,
|
||||
|
||||
if (is_about_to_suspend) {
|
||||
mm_obj_msg (self, "system is about to suspend");
|
||||
g_signal_emit (self, signals[SLEEPING], 0);
|
||||
drop_inhibitor (self);
|
||||
|
||||
if (self->sleep_ctx || self->sleep_done_id) {
|
||||
mm_obj_warn (self, "clearing unfinished sleep context...");
|
||||
cleanup_sleep_context (self);
|
||||
}
|
||||
self->sleep_ctx = mm_sleep_context_new (5);
|
||||
self->sleep_done_id = g_signal_connect (self->sleep_ctx,
|
||||
MM_SLEEP_CONTEXT_DONE,
|
||||
(GCallback)sleep_context_done,
|
||||
self);
|
||||
g_signal_emit (self, signals[SLEEPING], 0, self->sleep_ctx);
|
||||
} else {
|
||||
mm_obj_msg (self, "system is resuming");
|
||||
take_inhibitor (self);
|
||||
@@ -219,16 +252,24 @@ mm_sleep_monitor_init (MMSleepMonitor *self)
|
||||
(GAsyncReadyCallback) on_proxy_acquired, self);
|
||||
}
|
||||
|
||||
static void
|
||||
dispose (GObject *object)
|
||||
{
|
||||
MMSleepMonitor *self = MM_SLEEP_MONITOR (object);
|
||||
|
||||
cleanup_sleep_context (self);
|
||||
g_clear_object (&self->sd_proxy);
|
||||
|
||||
G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
finalize (GObject *object)
|
||||
{
|
||||
MMSleepMonitor *self = MM_SLEEP_MONITOR (object);
|
||||
|
||||
drop_inhibitor (self);
|
||||
if (self->sd_proxy)
|
||||
g_object_unref (self->sd_proxy);
|
||||
|
||||
if (G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize != NULL)
|
||||
G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
@@ -245,6 +286,7 @@ mm_sleep_monitor_class_init (MMSleepMonitorClass *klass)
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = dispose;
|
||||
gobject_class->finalize = finalize;
|
||||
|
||||
signals[SLEEPING] = g_signal_new (MM_SLEEP_MONITOR_SLEEPING,
|
||||
@@ -253,8 +295,8 @@ mm_sleep_monitor_class_init (MMSleepMonitorClass *klass)
|
||||
G_STRUCT_OFFSET (MMSleepMonitorClass, sleeping),
|
||||
NULL, /* accumulator */
|
||||
NULL, /* accumulator data */
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
g_cclosure_marshal_VOID__OBJECT,
|
||||
G_TYPE_NONE, 1, MM_TYPE_SLEEP_CONTEXT);
|
||||
signals[RESUMING] = g_signal_new (MM_SLEEP_MONITOR_RESUMING,
|
||||
MM_TYPE_SLEEP_MONITOR,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
|
||||
Reference in New Issue
Block a user