From b8d269cf2f5aed4ca0e0bcb9702b65897fda18bc Mon Sep 17 00:00:00 2001 From: Dylan Van Assche Date: Sat, 6 Feb 2021 07:52:32 +0100 Subject: [PATCH] suspend: add boot timer The EG25 modem needs at least 2 minutes after indicating 'RDY' to be fully operational. If the modem is suspended before that, calls or texts may not be seen by the userspace. This mostly occurs when a full reboot or poweroff/poweron sequence of the phone is performed. : --- src/at.c | 4 +- src/manager.c | 5 +- src/manager.h | 4 +- src/suspend.c | 126 +++++++++++++++++++++++++++++++++++++++----------- src/suspend.h | 2 +- 5 files changed, 108 insertions(+), 33 deletions(-) diff --git a/src/at.c b/src/at.c index 39a857a..661098a 100644 --- a/src/at.c +++ b/src/at.c @@ -202,8 +202,10 @@ static gboolean modem_response(gint fd, g_message("Response: [%s]", response); - if (strcmp(response, "RDY") == 0) + if (strcmp(response, "RDY") == 0) { + suspend_inhibit(manager, TRUE, TRUE); manager->modem_state = EG25_STATE_STARTED; + } else if (strstr(response, "ERROR")) retry_at_command(manager); else if (strstr(response, "OK")) diff --git a/src/manager.c b/src/manager.c index 6445e0f..912a095 100644 --- a/src/manager.c +++ b/src/manager.c @@ -178,7 +178,7 @@ void modem_suspend_post(struct EG25Manager *manager) { gpio_sequence_suspend(manager); g_message("suspend sequence is over, drop inhibitor"); - suspend_inhibit(manager, FALSE); + suspend_inhibit(manager, FALSE, FALSE); } void modem_resume_pre(struct EG25Manager *manager) @@ -205,7 +205,8 @@ int main(int argc, char *argv[]) memset(&manager, 0, sizeof(manager)); manager.at_fd = -1; - manager.suspend_inhibit_fd = -1; + manager.delay_inhibit_fd = -1; + manager.block_inhibit_fd = -1; opt_context = g_option_context_new ("- Power management for the Quectel EG25 modem"); g_option_context_add_main_entries (opt_context, options, NULL); diff --git a/src/manager.h b/src/manager.h index f6351be..21b3b67 100644 --- a/src/manager.h +++ b/src/manager.h @@ -43,8 +43,10 @@ struct EG25Manager { MMModem *mm_modem; GDBusProxy *suspend_proxy; - int suspend_inhibit_fd; + int delay_inhibit_fd; + int block_inhibit_fd; guint suspend_timer; + guint boot_timer; GUdevClient *udev; diff --git a/src/suspend.c b/src/suspend.c index 4b1a026..622cf00 100644 --- a/src/suspend.c +++ b/src/suspend.c @@ -13,9 +13,10 @@ #include -#define SD_NAME "org.freedesktop.login1" -#define SD_PATH "/org/freedesktop/login1" -#define SD_INTERFACE "org.freedesktop.login1.Manager" +#define SD_NAME "org.freedesktop.login1" +#define SD_PATH "/org/freedesktop/login1" +#define SD_INTERFACE "org.freedesktop.login1.Manager" +#define FULL_BOOT_DELAY 120 static gboolean check_modem_resume(struct EG25Manager *manager) { @@ -26,18 +27,28 @@ static gboolean check_modem_resume(struct EG25Manager *manager) return FALSE; } -static gboolean drop_inhibitor(struct EG25Manager *manager) +static gboolean drop_inhibitor(struct EG25Manager *manager, gboolean block) { - if (manager->suspend_inhibit_fd >= 0) { - g_message("dropping systemd sleep inhibitor"); - close(manager->suspend_inhibit_fd); - manager->suspend_inhibit_fd = -1; - return TRUE; + if (block) { + if (manager->block_inhibit_fd >= 0) { + g_message("dropping systemd sleep block inhibitor"); + close(manager->block_inhibit_fd); + manager->block_inhibit_fd = -1; + return TRUE; + } + } + else { + if (manager->delay_inhibit_fd >= 0) { + g_message("dropping systemd sleep delay inhibitor"); + close(manager->delay_inhibit_fd); + manager->delay_inhibit_fd = -1; + return TRUE; + } } return FALSE; } -static void inhibit_done(GObject *source, +static void inhibit_done_delay(GObject *source, GAsyncResult *result, gpointer user_data) { @@ -54,27 +65,81 @@ static void inhibit_done(GObject *source, if (!fd_list || g_unix_fd_list_get_length(fd_list) != 1) g_warning("didn't get a single fd back"); - manager->suspend_inhibit_fd = g_unix_fd_list_get(fd_list, 0, NULL); + manager->delay_inhibit_fd = g_unix_fd_list_get(fd_list, 0, NULL); - g_message("inhibitor fd is %d", manager->suspend_inhibit_fd); + g_message("inhibitor sleep fd is %d", manager->delay_inhibit_fd); g_object_unref(fd_list); g_variant_unref(res); } } -static void take_inhibitor(struct EG25Manager *manager) +static void inhibit_done_block(GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GDBusProxy *suspend_proxy = G_DBUS_PROXY(source); + struct EG25Manager *manager = user_data; + g_autoptr (GError) error = NULL; + GVariant *res; + GUnixFDList *fd_list; + + res = g_dbus_proxy_call_with_unix_fd_list_finish(suspend_proxy, &fd_list, result, &error); + if (!res) { + g_warning("inhibit failed: %s", error->message); + } else { + if (!fd_list || g_unix_fd_list_get_length(fd_list) != 1) + g_warning("didn't get a single fd back"); + + manager->block_inhibit_fd = g_unix_fd_list_get(fd_list, 0, NULL); + + g_message("inhibitor block fd is %d", manager->block_inhibit_fd); + g_object_unref(fd_list); + g_variant_unref(res); + } +} + +/* + * After the EG25 modem sends 'RDY', it takes up to 2 minutes before all + * capabilities are operational. If the modem is suspended before that, + * calls and texts may be not recognized properly. + */ +static gboolean modem_fully_booted(struct EG25Manager *manager) +{ + g_message("Modem is up for %d seconds and fully ready", FULL_BOOT_DELAY); + manager->boot_timer = 0; + drop_inhibitor(manager, TRUE); + + return FALSE; +} + +static void take_inhibitor(struct EG25Manager *manager, gboolean block) { GVariant *variant_arg; - if(manager->suspend_inhibit_fd != -1) - drop_inhibitor(manager); + if (block) { + if(manager->block_inhibit_fd != -1) + drop_inhibitor(manager, TRUE); - variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager", - "eg25manager needs to prepare modem for sleep", "delay"); + variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager", + "eg25manager needs to wait for modem to be fully booted", "block"); - g_message("taking systemd sleep inhibitor"); - g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy, "Inhibit", variant_arg, - 0, G_MAXINT, NULL, NULL, inhibit_done, manager); + g_message("taking systemd sleep block inhibitor"); + g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy, "Inhibit", variant_arg, + 0, G_MAXINT, NULL, NULL, inhibit_done_block, manager); + g_message("scheduling block inhibitor release"); + manager->boot_timer = g_timeout_add_seconds(FULL_BOOT_DELAY, G_SOURCE_FUNC(modem_fully_booted), manager); + } + else { + if(manager->delay_inhibit_fd != -1) + drop_inhibitor(manager, FALSE); + + variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager", + "eg25manager needs to prepare modem for sleep", "delay"); + + g_message("taking systemd sleep delay inhibitor"); + g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy, "Inhibit", variant_arg, + 0, G_MAXINT, NULL, NULL, inhibit_done_delay, manager); + } } static void signal_cb(GDBusProxy *proxy, @@ -97,7 +162,7 @@ static void signal_cb(GDBusProxy *proxy, modem_suspend_pre(manager); } else { g_message("system is resuming"); - take_inhibitor(manager); + take_inhibitor(manager, FALSE); modem_resume_pre(manager); if (manager->mm_modem) { /* @@ -126,10 +191,10 @@ static void name_owner_cb(GObject *object, owner = g_dbus_proxy_get_name_owner(proxy); if (owner) { - take_inhibitor(manager); + take_inhibitor(manager, FALSE); g_free(owner); } else { - drop_inhibitor(manager); + drop_inhibitor(manager, FALSE); } } @@ -151,7 +216,7 @@ static void on_proxy_acquired(GObject *object, owner = g_dbus_proxy_get_name_owner(manager->suspend_proxy); if (owner) { - take_inhibitor(manager); + take_inhibitor(manager, FALSE); g_free(owner); } } @@ -167,21 +232,26 @@ void suspend_init(struct EG25Manager *manager) void suspend_destroy(struct EG25Manager *manager) { - drop_inhibitor(manager); + drop_inhibitor(manager, FALSE); + drop_inhibitor(manager, TRUE); if (manager->suspend_timer) { g_source_remove(manager->suspend_timer); manager->suspend_timer = 0; } + if (manager->boot_timer) { + g_source_remove(manager->boot_timer); + manager->boot_timer = 0; + } if (manager->suspend_proxy) { g_object_unref(manager->suspend_proxy); manager->suspend_proxy = NULL; } } -void suspend_inhibit(struct EG25Manager *manager, gboolean inhibit) +void suspend_inhibit(struct EG25Manager *manager, gboolean inhibit, gboolean block) { if (inhibit) - take_inhibitor(manager); + take_inhibitor(manager, block); else - drop_inhibitor(manager); + drop_inhibitor(manager, block); } diff --git a/src/suspend.h b/src/suspend.h index 39832aa..e82eeff 100644 --- a/src/suspend.h +++ b/src/suspend.h @@ -11,4 +11,4 @@ void suspend_init (struct EG25Manager *data); void suspend_destroy (struct EG25Manager *data); -void suspend_inhibit (struct EG25Manager *data, gboolean inhibit); +void suspend_inhibit (struct EG25Manager *data, gboolean inhibit, gboolean block); -- GitLab