aboutsummaryrefslogtreecommitdiff
path: root/src/mm-iface-modem.c
diff options
context:
space:
mode:
authorAleksander Morgado <aleksandermj@chromium.org>2023-12-18 12:12:20 +0000
committerAleksander Morgado <aleksander@aleksander.es>2024-01-11 11:13:12 +0000
commit19b3e59151db7a3823bc479c5cddf7269bbca595 (patch)
tree55759cb54586e6e9c1e87cd40326904e9685b24a /src/mm-iface-modem.c
parent7f8b7adfde45d5f47ad478a1a7ff67e04928599a (diff)
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.
Diffstat (limited to 'src/mm-iface-modem.c')
-rw-r--r--src/mm-iface-modem.c56
1 files changed, 56 insertions, 0 deletions
diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c
index 35ee4b5a..fef64fc5 100644
--- a/src/mm-iface-modem.c
+++ b/src/mm-iface-modem.c
@@ -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,14 +4197,32 @@ 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
@@ -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));