iface-modem: ensure power state updates are spaced in time

This change is an attempt to behave a bit better with modems when
multiple consecutive enable/full-power/disable/low-power operations
are scheduled in a modem.

MM will automatically throttle these requests so that the power state
updates are spaced in time, with at least 2 full seconds between
them.
This commit is contained in:
Aleksander Morgado
2024-01-11 11:13:12 +00:00
committed by Aleksander Morgado
parent 7f8b7adfde
commit 19b3e59151
+56
View File
@@ -47,6 +47,14 @@
#define SIGNAL_CHECK_INITIAL_TIMEOUT_SEC 3
#define SIGNAL_CHECK_TIMEOUT_SEC 30
/* Make sure this amount of seconds is left between two power state transitions,
* so that the modem can have time to process them properly. This is just a safe
* measure taken because we know modems may report us that the power state
* transition has already finished even if it hasn't. The timeout will really
* only apply if doing many power state transitions quickly one after the other,
* so this is just to cover that corner case. */
#define POWER_STATE_MIN_TIME_BETWEEN_UPDATES_SEC 2
/*****************************************************************************/
/* Private data context */
@@ -79,6 +87,10 @@ typedef struct {
/* SIM hot swap setup done flag */
gboolean sim_hot_swap_configured;
/* Timer that tracks when the last power operation request was
* performed, so that we can throttle the requests to the modem. */
GTimer *power_state_timer;
} Private;
static void
@@ -92,6 +104,7 @@ private_free (Private *priv)
g_source_remove (priv->signal_check_timeout_source);
if (priv->restart_initialize_idle_id)
g_source_remove (priv->restart_initialize_idle_id);
g_clear_pointer (&priv->power_state_timer, (GDestroyNotify) g_timer_destroy);
g_slice_free (Private, priv);
}
@@ -4050,6 +4063,7 @@ typedef enum {
SET_POWER_STATE_STEP_FIRST,
SET_POWER_STATE_STEP_LOAD,
SET_POWER_STATE_STEP_CHECK,
SET_POWER_STATE_STEP_WAIT_BEFORE_UPDATE,
SET_POWER_STATE_STEP_UPDATE,
SET_POWER_STATE_STEP_FCC_UNLOCK,
SET_POWER_STATE_STEP_AFTER_UPDATE,
@@ -4183,16 +4197,34 @@ requested_power_setup_ready (MMIfaceModem *self,
GTask *task)
{
SetPowerStateContext *ctx;
Private *priv;
ctx = g_task_get_task_data (task);
priv = get_private (self);
g_assert (!ctx->saved_error);
if (!ctx->requested_power_setup_finish (self, res, &ctx->saved_error))
mm_obj_info (self, "couldn't update power state: %s", ctx->saved_error->message);
/* Reset time of last power update */
g_timer_reset (priv->power_state_timer);
ctx->step++;
set_power_state_step (task);
}
static gboolean
wait_before_update_ready (GTask *task)
{
SetPowerStateContext *ctx;
ctx = g_task_get_task_data (task);
ctx->step++;
set_power_state_step (task);
return G_SOURCE_REMOVE;
}
static void
set_power_state_load_ready (MMIfaceModem *self,
GAsyncResult *res,
@@ -4219,9 +4251,11 @@ set_power_state_step (GTask *task)
{
MMIfaceModem *self;
SetPowerStateContext *ctx;
Private *priv;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
priv = get_private (self);
switch (ctx->step) {
case SET_POWER_STATE_STEP_FIRST:
@@ -4256,6 +4290,28 @@ set_power_state_step (GTask *task)
ctx->step++;
/* fall-through */
case SET_POWER_STATE_STEP_WAIT_BEFORE_UPDATE:
/* No wait if this is the first time */
if (!priv->power_state_timer)
priv->power_state_timer = g_timer_new ();
else {
gdouble time_since_last_update_sec;
time_since_last_update_sec = g_timer_elapsed (priv->power_state_timer, NULL);
if (time_since_last_update_sec < (gdouble)POWER_STATE_MIN_TIME_BETWEEN_UPDATES_SEC) {
guint wait_time_ms;
/* Compute wait time in ms */
wait_time_ms = (guint)(((gdouble)POWER_STATE_MIN_TIME_BETWEEN_UPDATES_SEC - time_since_last_update_sec) * 1000.0);
mm_obj_dbg (self, "waiting before updating power state: %ums", wait_time_ms);
g_timeout_add (wait_time_ms, (GSourceFunc) wait_before_update_ready, task);
return;
}
}
mm_obj_dbg (self, "no need to wait before updating power state");
ctx->step++;
/* fall-through */
case SET_POWER_STATE_STEP_UPDATE:
mm_obj_dbg (self, "updating power state: '%s'...", mm_modem_power_state_get_string (ctx->requested_power_state));