diff --git a/src/main.c b/src/main.c index d6447c39..7d32f667 100644 --- a/src/main.c +++ b/src/main.c @@ -64,10 +64,14 @@ sleeping_cb (MMSleepMonitor *sleep_monitor, { 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, ctx); + mm_base_manager_cleanup (manager, + MM_BASE_MANAGER_CLEANUP_DISABLE | + MM_BASE_MANAGER_CLEANUP_LOW_POWER | + MM_BASE_MANAGER_CLEANUP_REMOVE, + ctx); } else { mm_dbg ("removing devices... (sleeping)"); - mm_base_manager_shutdown (manager, FALSE, FALSE, TRUE, ctx); + mm_base_manager_cleanup (manager, MM_BASE_MANAGER_CLEANUP_REMOVE, ctx); } } @@ -84,11 +88,15 @@ sleeping_quick_cb (MMSleepMonitor *sleep_monitor, { 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, ctx); + mm_base_manager_cleanup (manager, + MM_BASE_MANAGER_CLEANUP_DISABLE | + MM_BASE_MANAGER_CLEANUP_LOW_POWER, + ctx); } else { - /* Don't need to wait for anything; just suspend */ - mm_dbg ("leaving modem powered... (sleeping)"); - mm_sleep_context_complete (ctx, NULL); + mm_dbg ("setting modem in terse mode... (only send important signals (call/text))"); + mm_base_manager_cleanup (manager, + MM_BASE_MANAGER_CLEANUP_TERSE, + ctx); } } @@ -268,7 +276,9 @@ main (int argc, char *argv[]) (GCallback)shutdown_done, inner); - mm_base_manager_shutdown (manager, TRUE, FALSE, TRUE, ctx); + mm_base_manager_cleanup (manager, + MM_BASE_MANAGER_CLEANUP_DISABLE | MM_BASE_MANAGER_CLEANUP_REMOVE, + ctx); /* Wait for all modems to be disabled and removed, but don't wait * forever: if disabling the modems takes longer than 20s, just diff --git a/src/mm-base-manager.c b/src/mm-base-manager.c index f473ef2a..84c450e4 100644 --- a/src/mm-base-manager.c +++ b/src/mm-base-manager.c @@ -17,6 +17,8 @@ * Copyright (C) 2011 - 2016 Aleksander Morgado */ +#include "gio/gio.h" +#include "glib.h" #include #include @@ -870,48 +872,42 @@ mm_base_manager_start (MMBaseManager *self, /*****************************************************************************/ typedef enum { - DEVICE_SHUTDOWN_STEP_FIRST = 0, - DEVICE_SHUTDOWN_STEP_DISABLE, - DEVICE_SHUTDOWN_STEP_LOW_POWER, - DEVICE_SHUTDOWN_STEP_REMOVE, - DEVICE_SHUTDOWN_STEP_LAST, -} DeviceShutdownStep; + DEVICE_CLEANUP_STEP_FIRST = 0, + DEVICE_CLEANUP_STEP_DISABLE, + DEVICE_CLEANUP_STEP_LOW_POWER, + DEVICE_CLEANUP_STEP_REMOVE, + DEVICE_CLEANUP_STEP_TERSE, + DEVICE_CLEANUP_STEP_LAST, +} DeviceCleanupStep; typedef struct { - MMBaseModem *modem; - gboolean disable; - gboolean low_power; - gboolean remove; + MMBaseModem *modem; + MMBaseManagerCleanupFlags flags; + DeviceCleanupStep step; +} DeviceCleanupContext; - DeviceShutdownStep step; -} DeviceShutdownContext; +static void device_cleanup_step (GTask *task); -static void device_shutdown_step (GTask *task); - -static DeviceShutdownContext * -device_shutdown_context_new (MMBaseModem *modem, - gboolean disable, - gboolean low_power, - gboolean remove) +static DeviceCleanupContext * +device_cleanup_context_new (MMBaseModem *modem, + MMBaseManagerCleanupFlags flags) { - DeviceShutdownContext *ctx; + DeviceCleanupContext *ctx; - ctx = g_slice_new0 (DeviceShutdownContext); + ctx = g_slice_new0 (DeviceCleanupContext); if (modem) ctx->modem = g_object_ref (modem); - ctx->step = DEVICE_SHUTDOWN_STEP_FIRST; - ctx->disable = disable; - ctx->low_power = low_power; - ctx->remove = remove; + ctx->step = DEVICE_CLEANUP_STEP_FIRST; + ctx->flags = flags; return ctx; } static void -device_shutdown_context_free (DeviceShutdownContext *ctx) +device_cleanup_context_free (DeviceCleanupContext *ctx) { g_clear_object (&ctx->modem); - g_slice_free (DeviceShutdownContext, ctx); + g_slice_free (DeviceCleanupContext, ctx); } static void @@ -919,7 +915,7 @@ modem_remove_ready (MMDevice *device, GAsyncResult *res, GTask *task) { - DeviceShutdownContext *ctx; + DeviceCleanupContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); @@ -928,15 +924,15 @@ modem_remove_ready (MMDevice *device, mm_obj_warn (device, "removing modem failed: %s", error->message); ctx->step++; - device_shutdown_step (task); + device_cleanup_step (task); } static void -shutdown_low_power_ready (MMIfaceModem *modem, - GAsyncResult *res, - GTask *task) +cleanup_low_power_ready (MMIfaceModem *modem, + GAsyncResult *res, + GTask *task) { - DeviceShutdownContext *ctx; + DeviceCleanupContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); @@ -945,15 +941,15 @@ shutdown_low_power_ready (MMIfaceModem *modem, mm_obj_info (modem, "changing to low power state failed: %s", error->message); ctx->step++; - device_shutdown_step (task); + device_cleanup_step (task); } static void -shutdown_disable_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) +cleanup_disable_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) { - DeviceShutdownContext *ctx; + DeviceCleanupContext *ctx; g_autoptr(GError) error = NULL; ctx = g_task_get_task_data (task); @@ -966,57 +962,83 @@ shutdown_disable_ready (MMBaseModem *modem, /* If disabling failed there's no point in trying to low-power the * modem; just remove it. */ - ctx->step = DEVICE_SHUTDOWN_STEP_REMOVE; + ctx->step = DEVICE_CLEANUP_STEP_REMOVE; } - device_shutdown_step (task); + device_cleanup_step (task); +} + +#if defined WITH_SUSPEND_RESUME +static void +cleanup_terse_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + DeviceCleanupContext *ctx; + g_autoptr(GError) error = NULL; + + ctx = g_task_get_task_data (task); + if (!mm_base_modem_terse_finish (modem, res, &error)) + mm_obj_info (modem, "setting terse failed: %s", error->message); + + ctx->step++; + + device_cleanup_step (task); +} +#endif + +static gboolean +has_cleanup_flag (MMBaseManagerCleanupFlags flags, + MMBaseManagerCleanupFlags expected) +{ + return flags & expected; } static void -device_shutdown_step (GTask *task) +device_cleanup_step (GTask *task) { - DeviceShutdownContext *ctx; + DeviceCleanupContext *ctx; MMDevice *device; ctx = g_task_get_task_data (task); device = g_task_get_source_object (task); switch (ctx->step) { - case DEVICE_SHUTDOWN_STEP_FIRST: + case DEVICE_CLEANUP_STEP_FIRST: ctx->step++; /* fall through */ - case DEVICE_SHUTDOWN_STEP_DISABLE: - if (ctx->disable && ctx->modem) { + case DEVICE_CLEANUP_STEP_DISABLE: + if (ctx->modem && has_cleanup_flag (ctx->flags, MM_BASE_MANAGER_CLEANUP_DISABLE)) { mm_obj_dbg (device, "disabling modem%d", mm_base_modem_get_dbus_id (ctx->modem)); mm_base_modem_disable (ctx->modem, MM_BASE_MODEM_OPERATION_LOCK_REQUIRED, MM_BASE_MODEM_OPERATION_PRIORITY_OVERRIDE, - (GAsyncReadyCallback)shutdown_disable_ready, + (GAsyncReadyCallback)cleanup_disable_ready, task); return; } ctx->step++; /* fall through */ - case DEVICE_SHUTDOWN_STEP_LOW_POWER: - if (ctx->low_power && ctx->modem) { + case DEVICE_CLEANUP_STEP_LOW_POWER: + if (ctx->modem && has_cleanup_flag (ctx->flags, MM_BASE_MANAGER_CLEANUP_LOW_POWER)) { mm_obj_dbg (device, "setting modem%d to low-power state", mm_base_modem_get_dbus_id (ctx->modem)); mm_iface_modem_set_power_state (MM_IFACE_MODEM (ctx->modem), MM_MODEM_POWER_STATE_LOW, - (GAsyncReadyCallback)shutdown_low_power_ready, + (GAsyncReadyCallback)cleanup_low_power_ready, task); return; } ctx->step++; /* fall through */ - case DEVICE_SHUTDOWN_STEP_REMOVE: - if (ctx->remove && ctx->modem) { + case DEVICE_CLEANUP_STEP_REMOVE: + if (ctx->modem && has_cleanup_flag (ctx->flags, MM_BASE_MANAGER_CLEANUP_REMOVE)) { mm_obj_dbg (device, "removing modem%d", mm_base_modem_get_dbus_id (ctx->modem)); @@ -1028,8 +1050,26 @@ device_shutdown_step (GTask *task) ctx->step++; /* fall through */ - case DEVICE_SHUTDOWN_STEP_LAST: - mm_obj_dbg (device, "device shutdown complete"); + case DEVICE_CLEANUP_STEP_TERSE: + +#if defined WITH_SUSPEND_RESUME + if (ctx->modem && has_cleanup_flag (ctx->flags, MM_BASE_MANAGER_CLEANUP_TERSE)) { + mm_obj_dbg(device, + "settting terse on modem%d", + mm_base_modem_get_dbus_id (ctx->modem)); + mm_base_modem_terse (ctx->modem, + MM_BASE_MODEM_OPERATION_LOCK_REQUIRED, + (GAsyncReadyCallback)cleanup_terse_ready, + task); + return; + } +#endif + + ctx->step++; + /* fall through */ + + case DEVICE_CLEANUP_STEP_LAST: + mm_obj_dbg (device, "device cleanup complete"); g_task_return_boolean (task, TRUE); g_object_unref (task); return; @@ -1046,25 +1086,25 @@ typedef struct { GSList *tasks; GSList *devices_to_remove; MMSleepContext *sleep_ctx; -} ManagerShutdownContext; +} ManagerCleanupContext; -static ManagerShutdownContext * -manager_shutdown_context_new (MMBaseManager *self, - MMSleepContext *sleep_ctx) +static ManagerCleanupContext * +manager_cleanup_context_new (MMBaseManager *self, + MMSleepContext *sleep_ctx) { - ManagerShutdownContext *ctx; + ManagerCleanupContext *ctx; - ctx = g_slice_new0 (ManagerShutdownContext); + ctx = g_slice_new0 (ManagerCleanupContext); ctx->self = g_object_ref (self); ctx->sleep_ctx = g_object_ref (sleep_ctx); return ctx; } static void -manager_shutdown_context_maybe_complete (ManagerShutdownContext *ctx, - GTask *task, - MMDevice *device, - gboolean remove) +manager_cleanup_context_maybe_complete (ManagerCleanupContext *ctx, + GTask *task, + MMDevice *device, + gboolean remove) { if (task) ctx->tasks = g_slist_remove (ctx->tasks, task); @@ -1085,50 +1125,49 @@ manager_shutdown_context_maybe_complete (ManagerShutdownContext *ctx, g_hash_table_remove (ctx->self->priv->devices, mm_device_get_uid (device)); g_slist_free (ctx->devices_to_remove); - mm_obj_dbg (ctx->self, "shutdown complete"); + mm_obj_dbg (ctx->self, "cleanup complete"); mm_sleep_context_complete (ctx->sleep_ctx, NULL); g_object_unref (ctx->sleep_ctx); g_object_unref (ctx->self); - g_slice_free (ManagerShutdownContext, ctx); + g_slice_free (ManagerCleanupContext, ctx); } } static void -device_shutdown_ready (MMDevice *device, - GAsyncResult *res, - ManagerShutdownContext *ctx) +device_cleanup_ready (MMDevice *device, + GAsyncResult *res, + ManagerCleanupContext *ctx) { GTask *task; - DeviceShutdownContext *dctx; + DeviceCleanupContext *dctx; g_autoptr(GError) error = NULL; + gboolean remove; task = G_TASK (res); if (!g_task_propagate_boolean (task, &error)) { mm_obj_info (ctx->self, - "failed to shutdown device %s: %s", + "failed to cleanup device %s: %s", mm_device_get_uid (device), error->message); } dctx = g_task_get_task_data (task); - manager_shutdown_context_maybe_complete (ctx, - task, - device, - dctx->remove); + remove = has_cleanup_flag (dctx->flags, MM_BASE_MANAGER_CLEANUP_REMOVE); + manager_cleanup_context_maybe_complete (ctx, task, device, remove); } void -mm_base_manager_shutdown (MMBaseManager *self, - gboolean disable, - gboolean low_power, - gboolean remove, - MMSleepContext *sleep_ctx) +mm_base_manager_cleanup (MMBaseManager *self, + MMBaseManagerCleanupFlags flags, + MMSleepContext *sleep_ctx) { - ManagerShutdownContext *ctx; + ManagerCleanupContext *ctx; GHashTableIter iter; MMDevice *device; + gboolean disable, remove, low_power, terse; + guint active_bearers = 0; g_return_if_fail (self != NULL); g_return_if_fail (MM_IS_BASE_MANAGER (self)); @@ -1136,38 +1175,60 @@ mm_base_manager_shutdown (MMBaseManager *self, /* Cancel all ongoing auth requests */ g_cancellable_cancel (self->priv->authp_cancellable); + disable = has_cleanup_flag (flags, MM_BASE_MANAGER_CLEANUP_DISABLE); + low_power = has_cleanup_flag (flags, MM_BASE_MANAGER_CLEANUP_LOW_POWER); + remove = has_cleanup_flag (flags, MM_BASE_MANAGER_CLEANUP_REMOVE); + terse = has_cleanup_flag (flags, MM_BASE_MANAGER_CLEANUP_TERSE); mm_obj_dbg (self, - "manager shutting down... (%s%s%s%s%s)", + "manager shutting down... (%s%s%s%s%s%s%s)", disable ? "disable" : "", - disable && (low_power || remove) ? "," : "", + disable && (low_power || remove || terse) ? "," : "", low_power ? "low-power" : "", - (low_power && remove) ? "," : "", - remove ? "remove" : ""); + low_power && (remove || terse) ? "," : "", + remove ? "remove" : "", + (remove && terse) ? "," : "", + terse ? "terse" : ""); - ctx = manager_shutdown_context_new (self, sleep_ctx); + ctx = manager_cleanup_context_new (self, sleep_ctx); g_hash_table_iter_init (&iter, self->priv->devices); while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) { - DeviceShutdownContext *dctx; - GTask *task; + DeviceCleanupContext *dctx; + GTask *task; + MMBaseModem *modem; + + modem = mm_device_peek_modem (device); + if (modem) { + guint count = 0; + + mm_iface_modem_count_bearers (MM_IFACE_MODEM (modem), + MM_IFACE_MODEM_COUNT_BEARERS_FLAG_ACTIVE, + &count, + NULL, + NULL); + active_bearers += count; + } task = g_task_new (device, NULL, - (GAsyncReadyCallback)device_shutdown_ready, + (GAsyncReadyCallback)device_cleanup_ready, ctx); ctx->tasks = g_slist_append (ctx->tasks, task); - dctx = device_shutdown_context_new (mm_device_peek_modem (device), - disable, - low_power, - remove); - g_task_set_task_data (task, dctx, (GDestroyNotify)device_shutdown_context_free); + dctx = device_cleanup_context_new (modem, flags); + g_task_set_task_data (task, dctx, (GDestroyNotify)device_cleanup_context_free); - device_shutdown_step (task); + device_cleanup_step (task); } - /* Complete shutdown if there were no devices */ - manager_shutdown_context_maybe_complete (ctx, NULL, NULL, FALSE); + /* Increase cleanup timeout for each connected bearer; disconnecting a + * bearer can take many seconds. + */ + if (active_bearers) + mm_sleep_context_timeout_backoff (sleep_ctx, active_bearers * 20); + + /* Complete cleanup if there were no devices */ + manager_cleanup_context_maybe_complete (ctx, NULL, NULL, FALSE); } guint32 diff --git a/src/mm-base-manager.h b/src/mm-base-manager.h index c164b2fa..16943299 100644 --- a/src/mm-base-manager.h +++ b/src/mm-base-manager.h @@ -72,11 +72,17 @@ MMBaseManager *mm_base_manager_new (GDBusConnection *bus, void mm_base_manager_start (MMBaseManager *manager, gboolean manual_scan); -void mm_base_manager_shutdown (MMBaseManager *manager, - gboolean disable, - gboolean power_low, - gboolean remove, - MMSleepContext *ctx); +typedef enum { + MM_BASE_MANAGER_CLEANUP_NONE = 0x0, + MM_BASE_MANAGER_CLEANUP_DISABLE = 0x1, + MM_BASE_MANAGER_CLEANUP_LOW_POWER = 0x2, + MM_BASE_MANAGER_CLEANUP_REMOVE = 0x4, + MM_BASE_MANAGER_CLEANUP_TERSE = 0x8, +} MMBaseManagerCleanupFlags; + +void mm_base_manager_cleanup (MMBaseManager *manager, + MMBaseManagerCleanupFlags flags, + MMSleepContext *ctx); #if defined WITH_SUSPEND_RESUME void mm_base_manager_sync (MMBaseManager *manager); diff --git a/src/mm-base-modem.c b/src/mm-base-modem.c index 005bf396..668c8649 100644 --- a/src/mm-base-modem.c +++ b/src/mm-base-modem.c @@ -730,6 +730,7 @@ typedef enum { STATE_OPERATION_TYPE_DISABLE, #if defined WITH_SUSPEND_RESUME STATE_OPERATION_TYPE_SYNC, + STATE_OPERATION_TYPE_TERSE, #endif } StateOperationType; @@ -855,6 +856,12 @@ state_operation (MMBaseModem *self, ctx->operation = MM_BASE_MODEM_GET_CLASS (self)->sync; ctx->operation_finish = MM_BASE_MODEM_GET_CLASS (self)->sync_finish; break; + case STATE_OPERATION_TYPE_TERSE: + operation_description = "terse"; + optional = TRUE; + ctx->operation = MM_BASE_MODEM_GET_CLASS (self)->terse; + ctx->operation_finish = MM_BASE_MODEM_GET_CLASS (self)->terse_finish; + break; #endif default: g_assert_not_reached (); @@ -909,6 +916,28 @@ mm_base_modem_sync (MMBaseModem *self, user_data); } +gboolean +mm_base_modem_terse_finish (MMBaseModem *self, + GAsyncResult *res, + GError **error) +{ + return state_operation_finish (self, res, error); +} + +void +mm_base_modem_terse (MMBaseModem *self, + MMBaseModemOperationLock operation_lock, + GAsyncReadyCallback callback, + gpointer user_data) +{ + state_operation (self, + STATE_OPERATION_TYPE_TERSE, + operation_lock, + MM_BASE_MODEM_OPERATION_PRIORITY_DEFAULT, + callback, + user_data); +} + #endif /* WITH_SUSPEND_RESUME */ /******************************************************************************/ diff --git a/src/mm-base-modem.h b/src/mm-base-modem.h index f053f3f5..957263bc 100644 --- a/src/mm-base-modem.h +++ b/src/mm-base-modem.h @@ -109,6 +109,15 @@ struct _MMBaseModemClass { * this method triggers a synchronization of all modem interfaces */ StateOperation sync; StateOperationFinish sync_finish; + + /* Modem terse. + * When suspending in quick suspend/resume mode, + * this method disables unsolicited events on the 3GPP interface only. + * This is enough for phones to suspend properly, but it + * might be useful to extend it to all other interfaces + * that support it as well */ + StateOperation terse; + StateOperationFinish terse_finish; #endif /* Allow plugins to subclass port object creation as needed */ @@ -298,13 +307,20 @@ gboolean mm_base_modem_disable_finish (MMBaseModem *self, GError **error); #if defined WITH_SUSPEND_RESUME -void mm_base_modem_sync (MMBaseModem *self, - MMBaseModemOperationLock operation_lock, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean mm_base_modem_sync_finish (MMBaseModem *self, - GAsyncResult *res, - GError **error); +void mm_base_modem_sync (MMBaseModem *self, + MMBaseModemOperationLock operation_lock, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_base_modem_sync_finish (MMBaseModem *self, + GAsyncResult *res, + GError **error); +void mm_base_modem_terse (MMBaseModem *self, + MMBaseModemOperationLock operation_lock, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_base_modem_terse_finish (MMBaseModem *self, + GAsyncResult *res, + GError **error); #endif void mm_base_modem_teardown_ports (MMBaseModem *self, diff --git a/src/mm-bearer-mbim.c b/src/mm-bearer-mbim.c index 33a1b8f1..d6bff9c8 100644 --- a/src/mm-bearer-mbim.c +++ b/src/mm-bearer-mbim.c @@ -1458,10 +1458,11 @@ load_settings_from_bearer (MMBearerMbim *self, guint max_multiplexed_bearers; const gchar *data_port_driver; - if (!mm_broadband_modem_get_active_multiplexed_bearers (MM_BROADBAND_MODEM (ctx->modem), - ¤t_multiplexed_bearers, - &max_multiplexed_bearers, - error)) + if (!mm_iface_modem_count_bearers (MM_IFACE_MODEM (ctx->modem), + MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED, + ¤t_multiplexed_bearers, + &max_multiplexed_bearers, + error)) return FALSE; /* Check multiplex support in the kernel and the device */ diff --git a/src/mm-bearer-qmi.c b/src/mm-bearer-qmi.c index cccbf239..cf6c1fcf 100644 --- a/src/mm-bearer-qmi.c +++ b/src/mm-bearer-qmi.c @@ -2424,10 +2424,11 @@ load_settings_from_bearer (MMBearerQmi *self, guint max_multiplexed_bearers; gboolean multiplex_supported = TRUE; - if (!mm_broadband_modem_get_active_multiplexed_bearers (MM_BROADBAND_MODEM (ctx->modem), - ¤t_multiplexed_bearers, - &max_multiplexed_bearers, - error)) + if (!mm_iface_modem_count_bearers (MM_IFACE_MODEM (ctx->modem), + MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED, + ¤t_multiplexed_bearers, + &max_multiplexed_bearers, + error)) return FALSE; /* Check multiplex support in the kernel and the device */ diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c index 6eb04fe2..c14bce3f 100644 --- a/src/mm-broadband-modem.c +++ b/src/mm-broadband-modem.c @@ -13158,6 +13158,103 @@ synchronize (MMBaseModem *self, syncing_step (task); } +typedef enum { + TERSE_STEP_FIRST, + TERSE_STEP_DISABLE_UNSOLICITED_EVENTS_3GPP, + TERSE_STEP_LAST, +} TerseStep; + +typedef struct { + TerseStep step; +} TerseContext; + +static void terse_step (GTask *task); + +static gboolean +terse_finish (MMBaseModem *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +iface_modem_3gpp_terse_ready (MMIfaceModem3gpp *self, + GAsyncResult *res, + GTask *task) +{ + TerseContext *ctx; + g_autoptr(GError) error = NULL; + + ctx = g_task_get_task_data (task); + + if (!mm_iface_modem_3gpp_terse_finish (self, res, &error)) + mm_obj_warn (self, "3GPP interface disable unsolicited events failed: %s", error->message); + + /* Go on to next step */ + ctx->step++; + terse_step (task); +} + +static void +terse_step (GTask *task) +{ + MMBroadbandModem *self; + TerseContext *ctx; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + switch (ctx->step) { + case TERSE_STEP_FIRST: + ctx->step++; + /* fall through */ + + case TERSE_STEP_DISABLE_UNSOLICITED_EVENTS_3GPP: + if (self->priv->modem_3gpp_dbus_skeleton && + (self->priv->modem_state >= MM_MODEM_STATE_ENABLED)) { + mm_obj_msg (self, "terse state (%d/%d): 3GPP interface disable unsolicited events", + ctx->step, TERSE_STEP_LAST); + mm_iface_modem_3gpp_terse (MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback)iface_modem_3gpp_terse_ready, task); + return; + } + ctx->step++; + /* fall through */ + + case TERSE_STEP_LAST: + mm_obj_msg (self, "setting terse state (%d/%d): all done", + ctx->step, TERSE_STEP_LAST); + /* We are done without errors! */ + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + + default: + break; + } + + g_assert_not_reached (); +} + +static void +terse (MMBaseModem *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TerseContext *ctx; + GTask *task; + + task = g_task_new (self, cancellable, callback, user_data); + + /* Create TerseContext */ + ctx = g_new0 (TerseContext, 1); + ctx->step = TERSE_STEP_FIRST; + g_task_set_task_data (task, ctx, (GDestroyNotify)g_free); + + terse_step (task); +} + #endif /*****************************************************************************/ @@ -13702,52 +13799,6 @@ mm_broadband_modem_get_current_charset (MMBroadbandModem *self) /*****************************************************************************/ -static void -bearer_count_multiplexed_connected (MMBaseBearer *bearer, - guint *count) -{ - /* The Multiplexed property is only set if connected, so it's enough to check - * that one to see if we're connected and multiplexed */ - if (mm_gdbus_bearer_get_multiplexed (MM_GDBUS_BEARER (bearer))) - *count += 1; -} - -gboolean -mm_broadband_modem_get_active_multiplexed_bearers (MMBroadbandModem *self, - guint *out_current, - guint *out_max, - GError **error) -{ - g_autoptr(MMBearerList) list = NULL; - guint max; - guint count = 0; - - g_object_get (self, - MM_IFACE_MODEM_BEARER_LIST, &list, - NULL); - - if (!list) { - g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Bearer list unavailable"); - return FALSE; - } - - max = mm_bearer_list_get_max_active_multiplexed (list); - - mm_bearer_list_foreach (list, - (MMBearerListForeachFunc)bearer_count_multiplexed_connected, - &count); - g_assert (!(!max && count)); - - if (out_max) - *out_max = max; - if (out_current) - *out_current = count; - - return TRUE; -} - -/*****************************************************************************/ - gchar * mm_broadband_modem_create_device_identifier (MMBroadbandModem *self, const gchar *ati, @@ -14645,6 +14696,8 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass) #if defined WITH_SUSPEND_RESUME base_modem_class->sync = synchronize; base_modem_class->sync_finish = synchronize_finish; + base_modem_class->terse = terse; + base_modem_class->terse_finish = terse_finish; #endif klass->setup_ports = setup_ports; diff --git a/src/mm-broadband-modem.h b/src/mm-broadband-modem.h index f820e045..4930d4de 100644 --- a/src/mm-broadband-modem.h +++ b/src/mm-broadband-modem.h @@ -143,12 +143,6 @@ gboolean mm_broadband_modem_sim_hot_swap_ports_context_init (MMBroadbandModem GError **error); void mm_broadband_modem_sim_hot_swap_ports_context_reset (MMBroadbandModem *self); -/* Helper to manage multiplexed bearers */ -gboolean mm_broadband_modem_get_active_multiplexed_bearers (MMBroadbandModem *self, - guint *out_current, - guint *out_max, - GError **error); - /* Helper to manage initial EPS bearer */ gint mm_broadband_modem_get_initial_eps_bearer_cid (MMBroadbandModem *self); diff --git a/src/mm-iface-modem-3gpp.c b/src/mm-iface-modem-3gpp.c index c103fd13..faaa4dfd 100644 --- a/src/mm-iface-modem-3gpp.c +++ b/src/mm-iface-modem-3gpp.c @@ -3232,6 +3232,8 @@ static void interface_syncing_step (GTask *task); typedef enum { SYNCING_STEP_FIRST, + SYNCING_STEP_ENABLE_UNSOLICITED_EVENTS, + SYNCING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS, SYNCING_STEP_REFRESH_3GPP_REGISTRATION, SYNCING_STEP_REFRESH_EPS_BEARER, SYNCING_STEP_LAST @@ -3249,6 +3251,31 @@ mm_iface_modem_3gpp_sync_finish (MMIfaceModem3gpp *self, return g_task_propagate_boolean (G_TASK (res), error); } +#undef VOID_SYNC_REPLY_READY_FN +#define VOID_SYNC_REPLY_READY_FN(NAME,DISPLAY) \ + static void \ + sync_##NAME##_ready (MMIfaceModem3gpp *self, \ + GAsyncResult *res, \ + GTask *task) \ + { \ + DisablingContext *ctx; \ + g_autoptr(GError) error = NULL; \ + \ + MM_IFACE_MODEM_3GPP_GET_IFACE (self)->NAME##_finish (self, res, &error); \ + if (error) \ + mm_obj_dbg (self, "couldn't %s: %s", DISPLAY, error->message); \ + \ + /* Go on to next step */ \ + ctx = g_task_get_task_data (task); \ + ctx->step++; \ + interface_syncing_step (task); \ + } + +VOID_SYNC_REPLY_READY_FN (enable_unsolicited_events, + "enable unsolicited events") +VOID_SYNC_REPLY_READY_FN (enable_unsolicited_registration_events, + "enable unsolicited registration events") + static void sync_eps_bearer_ready (MMIfaceModem3gpp *self, GAsyncResult *res, @@ -3327,6 +3354,33 @@ interface_syncing_step (GTask *task) ctx->step++; /* fall through */ + case SYNCING_STEP_ENABLE_UNSOLICITED_EVENTS: + if (MM_IFACE_MODEM_3GPP_GET_IFACE (self)->enable_unsolicited_events && + MM_IFACE_MODEM_3GPP_GET_IFACE (self)->enable_unsolicited_events_finish) { + MM_IFACE_MODEM_3GPP_GET_IFACE (self)->enable_unsolicited_events ( + self, + (GAsyncReadyCallback)sync_enable_unsolicited_events_ready, + task); + return; + } + ctx->step++; + /* fall through */ + + case SYNCING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS: + if (MM_IFACE_MODEM_3GPP_GET_IFACE (self)->enable_unsolicited_registration_events && + MM_IFACE_MODEM_3GPP_GET_IFACE (self)->enable_unsolicited_registration_events_finish) { + MM_IFACE_MODEM_3GPP_GET_IFACE (self)->enable_unsolicited_registration_events ( + self, + get_cs_network_supported (self), + get_ps_network_supported (self), + get_eps_network_supported (self), + (GAsyncReadyCallback)sync_enable_unsolicited_registration_events_ready, + task); + return; + } + ctx->step++; + /* fall through */ + case SYNCING_STEP_REFRESH_3GPP_REGISTRATION: /* * Refresh registration info to verify that the modem is still registered. @@ -3381,6 +3435,129 @@ mm_iface_modem_3gpp_sync (MMIfaceModem3gpp *self, interface_syncing_step (task); } +typedef struct _TerseContext TerseContext; +static void interface_terse_step (GTask *task); + +typedef enum { + TERSE_STEP_FIRST, + TERSE_STEP_DISABLE_UNSOLICITED_REGISTRATION_EVENTS, + TERSE_STEP_DISABLE_UNSOLICITED_EVENTS, + TERSE_STEP_LAST +} TerseStep; + +struct _TerseContext { + TerseStep step; +}; + +gboolean +mm_iface_modem_3gpp_terse_finish (MMIfaceModem3gpp *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +#undef VOID_TERSE_REPLY_READY_FN +#define VOID_TERSE_REPLY_READY_FN(NAME,DISPLAY) \ + static void \ + terse_##NAME##_ready (MMIfaceModem3gpp *self, \ + GAsyncResult *res, \ + GTask *task) \ + { \ + TerseContext *ctx; \ + g_autoptr(GError) error = NULL; \ + \ + MM_IFACE_MODEM_3GPP_GET_IFACE (self)->NAME##_finish (self, res, &error); \ + if (error) \ + mm_obj_dbg (self, "couldn't %s: %s", DISPLAY, error->message); \ + \ + /* Go on to next step */ \ + ctx = g_task_get_task_data (task); \ + ctx->step++; \ + interface_terse_step (task); \ + } + +VOID_TERSE_REPLY_READY_FN (disable_unsolicited_events, + "disable unsolicited events") +VOID_TERSE_REPLY_READY_FN (disable_unsolicited_registration_events, + "disable unsolicited registration events") + +static void +interface_terse_step (GTask *task) +{ + MMIfaceModem3gpp *self; + TerseContext *ctx; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + switch (ctx->step) { + case TERSE_STEP_FIRST: + ctx->step++; + /* fall through */ + + case TERSE_STEP_DISABLE_UNSOLICITED_REGISTRATION_EVENTS: + if (MM_IFACE_MODEM_3GPP_GET_IFACE (self)->disable_unsolicited_registration_events && + MM_IFACE_MODEM_3GPP_GET_IFACE (self)->disable_unsolicited_registration_events_finish) { + MM_IFACE_MODEM_3GPP_GET_IFACE (self)->disable_unsolicited_registration_events ( + self, + get_cs_network_supported (self), + get_ps_network_supported (self), + get_eps_network_supported (self), + (GAsyncReadyCallback)terse_disable_unsolicited_registration_events_ready, + task); + mm_obj_msg (self, "terse state 3GPP (%d/%d): disable unsolicited registration events done", + ctx->step, TERSE_STEP_LAST); + return; + } + ctx->step++; + /* fall through */ + + case TERSE_STEP_DISABLE_UNSOLICITED_EVENTS: + if (MM_IFACE_MODEM_3GPP_GET_IFACE (self)->disable_unsolicited_events && + MM_IFACE_MODEM_3GPP_GET_IFACE (self)->disable_unsolicited_events_finish) { + MM_IFACE_MODEM_3GPP_GET_IFACE (self)->disable_unsolicited_events ( + self, + (GAsyncReadyCallback)terse_disable_unsolicited_events_ready, + task); + mm_obj_msg (self, "terse state 3GPP (%d/%d): disable unsolicited events done", + ctx->step, TERSE_STEP_LAST); + return; + } + ctx->step++; + /* fall through */ + + case TERSE_STEP_LAST: + /* We are done without errors! */ + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + + default: + g_assert_not_reached (); + } + + g_assert_not_reached (); +} + +void +mm_iface_modem_3gpp_terse (MMIfaceModem3gpp *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TerseContext *ctx; + GTask *task; + + /* Create SyncingContext */ + ctx = g_new0 (TerseContext, 1); + ctx->step = TERSE_STEP_FIRST; + + /* Create sync steps task and execute it */ + task = g_task_new (self, NULL, callback, user_data); + g_task_set_task_data (task, ctx, (GDestroyNotify)g_free); + interface_terse_step (task); +} + #endif /*****************************************************************************/ diff --git a/src/mm-iface-modem-3gpp.h b/src/mm-iface-modem-3gpp.h index 4860e5c0..1bfc43f9 100644 --- a/src/mm-iface-modem-3gpp.h +++ b/src/mm-iface-modem-3gpp.h @@ -318,6 +318,13 @@ gboolean mm_iface_modem_3gpp_sync_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error); +void mm_iface_modem_3gpp_terse (MMIfaceModem3gpp *self, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_iface_modem_3gpp_terse_finish (MMIfaceModem3gpp *self, + GAsyncResult *res, + GError **error); + #endif /* Shutdown Modem 3GPP interface */ diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c index de25296b..d9b2e007 100644 --- a/src/mm-iface-modem.c +++ b/src/mm-iface-modem.c @@ -6720,6 +6720,74 @@ mm_iface_modem_get_current_modes (MMIfaceModem *self, /*****************************************************************************/ +typedef struct { + const MMIfaceModemCountBearersFlags flags; + guint count; +} CountBearersContext; + +static void +bearer_count (MMBaseBearer *bearer, + CountBearersContext *ctx) +{ + MMBearerStatus status; + + status = mm_base_bearer_get_status (MM_BASE_BEARER (bearer)); + + if (ctx->flags & MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED) { + /* The Multiplexed property is only set if connected, so it's enough to check + * that one to see if we're connected and multiplexed */ + if (mm_gdbus_bearer_get_multiplexed (MM_GDBUS_BEARER (bearer))) + ctx->count++; + } else if (ctx->flags & MM_IFACE_MODEM_COUNT_BEARERS_FLAG_CONNECTED) { + if (status == MM_BEARER_STATUS_CONNECTED) + ctx->count++; + } else if (ctx->flags & MM_IFACE_MODEM_COUNT_BEARERS_FLAG_ACTIVE) { + if (status >= MM_BEARER_STATUS_CONNECTING) + ctx->count++; + } else + ctx->count++; +} + +gboolean +mm_iface_modem_count_bearers (MMIfaceModem *self, + MMIfaceModemCountBearersFlags flags, + guint *out_current, + guint *out_max, + GError **error) +{ + g_autoptr(MMBearerList) list = NULL; + guint max = 0; + CountBearersContext ctx = { + .flags = flags, + .count = 0, + }; + + g_object_get (self, + MM_IFACE_MODEM_BEARER_LIST, &list, + NULL); + if (!list) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Bearer list unavailable"); + return FALSE; + } + + if (flags & MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED) + max = mm_bearer_list_get_max_active_multiplexed (list); + else + max = mm_bearer_list_get_max_active (list); + + mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_count, &ctx); + g_assert (max >= ctx.count); + + if (out_max) + *out_max = max; + if (out_current) + *out_current = ctx.count; + + return TRUE; +} + +/*****************************************************************************/ + static void mm_iface_modem_default_init (MMIfaceModemInterface *iface) { diff --git a/src/mm-iface-modem.h b/src/mm-iface-modem.h index 18c7e9ea..977dd16c 100644 --- a/src/mm-iface-modem.h +++ b/src/mm-iface-modem.h @@ -620,4 +620,19 @@ void mm_iface_modem_modify_sim (MMIfaceModem *self, void mm_iface_modem_process_sim_event (MMIfaceModem *self); +typedef enum { + /* Connecting or connected bearers */ + MM_IFACE_MODEM_COUNT_BEARERS_FLAG_ACTIVE = 0x1, + /* Fully connected bearers */ + MM_IFACE_MODEM_COUNT_BEARERS_FLAG_CONNECTED = 0x2, + /* Multiplexed implies connected */ + MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED = 0x4, +} MMIfaceModemCountBearersFlags; + +gboolean mm_iface_modem_count_bearers (MMIfaceModem *self, + MMIfaceModemCountBearersFlags flags, + guint *out_current, + guint *out_max, + GError **error); + #endif /* MM_IFACE_MODEM_H */ diff --git a/src/mm-sleep-context.c b/src/mm-sleep-context.c index 69a9da22..d6c01198 100644 --- a/src/mm-sleep-context.c +++ b/src/mm-sleep-context.c @@ -27,6 +27,7 @@ G_DEFINE_TYPE_EXTENDED (MMSleepContext, mm_sleep_context, G_TYPE_OBJECT, 0, struct _MMSleepContextPrivate { GTask *task; guint timeout_id; + gint64 timeout_start; }; enum { @@ -52,6 +53,7 @@ timeout_cleanup (MMSleepContext *self) if (self->priv->timeout_id) g_source_remove (self->priv->timeout_id); self->priv->timeout_id = 0; + self->priv->timeout_start = 0; } void @@ -95,6 +97,33 @@ operation_ready (MMSleepContext *self, g_signal_emit (self, signals[DONE], 0, error); } +static void +timeout_add (MMSleepContext *self, + guint interval) +{ + timeout_cleanup (self); + self->priv->timeout_start = g_get_monotonic_time (); + self->priv->timeout_id = g_timeout_add_seconds (interval, + (GSourceFunc)operation_timeout, + self); +} + +void +mm_sleep_context_timeout_backoff (MMSleepContext *self, + guint more_seconds) +{ + gint64 cur_time; + gint64 new_interval; + + new_interval = more_seconds * G_USEC_PER_SEC; + if (self->priv->timeout_start) { + cur_time = g_get_monotonic_time (); + new_interval += (cur_time - self->priv->timeout_start); + } + + timeout_add (self, new_interval / G_USEC_PER_SEC); +} + /*****************************************************************************/ MMSleepContext * @@ -107,9 +136,7 @@ mm_sleep_context_new (guint timeout_seconds) NULL, (GAsyncReadyCallback)operation_ready, NULL); - self->priv->timeout_id = g_timeout_add_seconds (timeout_seconds, - (GSourceFunc)operation_timeout, - self); + timeout_add (self, timeout_seconds); mm_obj_dbg (self, "new context with %d second timeout", timeout_seconds); return self; diff --git a/src/mm-sleep-context.h b/src/mm-sleep-context.h index cd45c1f2..44b3c28a 100644 --- a/src/mm-sleep-context.h +++ b/src/mm-sleep-context.h @@ -51,6 +51,9 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSleepContext, g_object_unref) MMSleepContext *mm_sleep_context_new (guint timeout_seconds); +void mm_sleep_context_timeout_backoff (MMSleepContext *self, + guint more_seconds); + /* The MMSleepContext assumes ownership of @error */ void mm_sleep_context_complete (MMSleepContext *self, GError *error);