diff --git a/src/main.c b/src/main.c index ea1e1342..7d32f667 100644 --- a/src/main.c +++ b/src/main.c @@ -93,9 +93,10 @@ sleeping_quick_cb (MMSleepMonitor *sleep_monitor, 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); } } diff --git a/src/mm-base-manager.c b/src/mm-base-manager.c index d4140f38..0980b9f9 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 @@ -874,6 +876,7 @@ typedef enum { DEVICE_CLEANUP_STEP_DISABLE, DEVICE_CLEANUP_STEP_LOW_POWER, DEVICE_CLEANUP_STEP_REMOVE, + DEVICE_CLEANUP_STEP_TERSE, DEVICE_CLEANUP_STEP_LAST, } DeviceCleanupStep; @@ -965,6 +968,25 @@ cleanup_disable_ready (MMBaseModem *modem, 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) @@ -1028,6 +1050,24 @@ device_cleanup_step (GTask *task) ctx->step++; /* fall through */ + 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_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); @@ -1126,7 +1166,7 @@ mm_base_manager_cleanup (MMBaseManager *self, ManagerCleanupContext *ctx; GHashTableIter iter; MMDevice *device; - gboolean disable, remove, low_power; + gboolean disable, remove, low_power, terse; g_return_if_fail (self != NULL); g_return_if_fail (MM_IS_BASE_MANAGER (self)); @@ -1137,13 +1177,16 @@ mm_base_manager_cleanup (MMBaseManager *self, 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_cleanup_context_new (self, sleep_ctx); diff --git a/src/mm-base-manager.h b/src/mm-base-manager.h index 891dd908..16943299 100644 --- a/src/mm-base-manager.h +++ b/src/mm-base-manager.h @@ -77,6 +77,7 @@ typedef enum { 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, diff --git a/src/mm-base-modem.c b/src/mm-base-modem.c index 3558775b..224320f6 100644 --- a/src/mm-base-modem.c +++ b/src/mm-base-modem.c @@ -754,6 +754,7 @@ typedef enum { STATE_OPERATION_TYPE_DISABLE, #if defined WITH_SUSPEND_RESUME STATE_OPERATION_TYPE_SYNC, + STATE_OPERATION_TYPE_TERSE, #endif } StateOperationType; @@ -879,6 +880,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 (); @@ -933,6 +940,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, + MMOperationLock operation_lock, + GAsyncReadyCallback callback, + gpointer user_data) +{ + state_operation (self, + STATE_OPERATION_TYPE_TERSE, + operation_lock, + MM_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 1f35b913..e0aecbb7 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 */ @@ -255,6 +264,13 @@ void mm_base_modem_sync (MMBaseModem *self, gboolean mm_base_modem_sync_finish (MMBaseModem *self, GAsyncResult *res, GError **error); +void mm_base_modem_terse (MMBaseModem *self, + MMOperationLock 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-broadband-modem.c b/src/mm-broadband-modem.c index 27ac8f56..12982748 100644 --- a/src/mm-broadband-modem.c +++ b/src/mm-broadband-modem.c @@ -13164,6 +13164,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 /*****************************************************************************/ @@ -14654,6 +14751,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-iface-modem-3gpp.c b/src/mm-iface-modem-3gpp.c index 490b68dc..b72ae0e4 100644 --- a/src/mm-iface-modem-3gpp.c +++ b/src/mm-iface-modem-3gpp.c @@ -3240,6 +3240,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 @@ -3257,6 +3259,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, @@ -3335,6 +3362,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. @@ -3389,6 +3443,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 */