diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2022-05-19 12:30:34 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2022-05-19 11:45:37 +0000 |
commit | c659492022c707a52853cb1f3c92d76ea8f4158b (patch) | |
tree | a98ddaa7b6515fedd58c1a91e0c2c03b64eb1612 /plugins/xmm/mm-shared-xmm.c | |
parent | 7de4d6673bb5fd0e4ab36ce195b25bb5a20b4a9b (diff) |
xmm: wait for +XLSRSTOP URC before starting next session
It is important to gracefully stop the session before starting the
next session, making sure the engine is completely stopped. This can
be ensured by waiting for the +XLSRSTOP URC just after having received
the +XLSRSTOP command response.
We'll do an explicit wait for that URC with a 10s timeout, in order to
avoid waiting forever if the URC is never received. It will be assumed
that the engine is off if the 10s timeout happens, in the same way as
we were doing until now.
During the wait time for the URC, the operation task ownership is
shared among the response processor, the URC handler and the timeout
source. Once any of them decides to complete the task, the others will
automatically avoid attempting to complete the same task.
Based on a patch originally developed by:
Som_SP <somashekhar.puttagangaiah@intel.com>
Diffstat (limited to 'plugins/xmm/mm-shared-xmm.c')
-rw-r--r-- | plugins/xmm/mm-shared-xmm.c | 235 |
1 files changed, 190 insertions, 45 deletions
diff --git a/plugins/xmm/mm-shared-xmm.c b/plugins/xmm/mm-shared-xmm.c index 73004066..90f8867a 100644 --- a/plugins/xmm/mm-shared-xmm.c +++ b/plugins/xmm/mm-shared-xmm.c @@ -61,11 +61,15 @@ typedef struct { MMPortSerialAt *gps_port; GRegex *xlsrstop_regex; GRegex *nmea_regex; + + /* Asynchronous GPS engine stop task completion */ + GTask *pending_gps_engine_stop_task; } Private; static void private_free (Private *priv) { + g_assert (!priv->pending_gps_engine_stop_task); g_clear_object (&priv->gps_port); if (priv->supported_modes) g_array_unref (priv->supported_modes); @@ -970,7 +974,7 @@ mm_shared_xmm_location_load_capabilities (MMIfaceModemLocation *self, } /*****************************************************************************/ -/* GPS engine state selection */ +/* NMEA trace processing */ static void nmea_received (MMPortSerialAt *port, @@ -984,6 +988,23 @@ nmea_received (MMPortSerialAt *port, g_free (trace); } +/*****************************************************************************/ +/* GPS engine state selection */ + +#define GPS_ENGINE_STOP_TIMEOUT_SECS 10 + +typedef struct { + GpsEngineState state; + guint engine_stop_timeout_id; +} GpsEngineSelectContext; + +static void +gps_engine_select_context_free (GpsEngineSelectContext *ctx) +{ + g_assert (!ctx->engine_stop_timeout_id); + g_slice_free (GpsEngineSelectContext, ctx); +} + static gboolean gps_engine_state_select_finish (MMSharedXmm *self, GAsyncResult *res, @@ -997,12 +1018,13 @@ xlcslsr_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { - GpsEngineState state; - const gchar *response; - GError *error = NULL; - Private *priv; + GpsEngineSelectContext *ctx; + const gchar *response; + GError *error = NULL; + Private *priv; priv = get_private (MM_SHARED_XMM (self)); + ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_full_finish (self, res, &error); if (!response) { @@ -1012,7 +1034,7 @@ xlcslsr_ready (MMBaseModem *self, return; } - state = GPOINTER_TO_UINT (g_task_get_task_data (task)); + mm_obj_dbg (self, "GPS engine started"); g_assert (priv->gps_port); mm_port_serial_at_add_unsolicited_msg_handler (priv->gps_port, @@ -1020,7 +1042,7 @@ xlcslsr_ready (MMBaseModem *self, (MMPortSerialAtUnsolicitedMsgFn)nmea_received, self, NULL); - priv->gps_engine_state = state; + priv->gps_engine_state = ctx->state; g_task_return_boolean (task, TRUE); g_object_unref (task); @@ -1029,17 +1051,17 @@ xlcslsr_ready (MMBaseModem *self, static void gps_engine_start (GTask *task) { - GpsEngineState state; - MMSharedXmm *self; - Private *priv; - GError *error = NULL; - guint transport_protocol = 0; - guint pos_mode = 0; - gchar *cmd; + GpsEngineSelectContext *ctx; + MMSharedXmm *self; + Private *priv; + GError *error = NULL; + guint transport_protocol = 0; + guint pos_mode = 0; + gchar *cmd; - self = g_task_get_source_object (task); - priv = get_private (self); - state = GPOINTER_TO_UINT (g_task_get_task_data (task)); + self = g_task_get_source_object (task); + priv = get_private (self); + ctx = g_task_get_task_data (task); g_assert (!priv->gps_port); priv->gps_port = shared_xmm_get_gps_control_port (self, &error); @@ -1049,7 +1071,7 @@ gps_engine_start (GTask *task) return; } - switch (state) { + switch (ctx->state) { case GPS_ENGINE_STATE_STANDALONE: transport_protocol = 2; pos_mode = 3; @@ -1068,6 +1090,8 @@ gps_engine_start (GTask *task) break; } + mm_obj_dbg (self, "starting GPS engine..."); + /* * AT+XLCSLSR * transport_protocol: 2 (invalid) or 1 (supl) @@ -1097,53 +1121,171 @@ gps_engine_start (GTask *task) g_free (cmd); } -static void -xlsrstop_ready (MMBaseModem *self, - GAsyncResult *res, - GTask *task) +static GTask * +recover_pending_gps_engine_stop_task (Private *priv) { - GpsEngineState state; - GError *error = NULL; - Private *priv; + GTask *task; + GpsEngineSelectContext *ctx; - mm_base_modem_at_command_full_finish (self, res, &error); + /* We're taking over full ownership of the GTask at this point. */ + if (!priv->pending_gps_engine_stop_task) + return NULL; + task = g_steal_pointer (&priv->pending_gps_engine_stop_task); + ctx = g_task_get_task_data (task); - priv = get_private (MM_SHARED_XMM (self)); - state = GPOINTER_TO_UINT (g_task_get_task_data (task)); + /* remove timeout */ + if (ctx->engine_stop_timeout_id) { + g_source_remove (ctx->engine_stop_timeout_id); + ctx->engine_stop_timeout_id = 0; + } + + /* disable urc handling */ + mm_port_serial_at_add_unsolicited_msg_handler ( + priv->gps_port, + priv->xlsrstop_regex, + NULL, NULL, NULL); + + return task; +} + +static void +gps_engine_stopped (GTask *task) +{ + MMSharedXmm *self; + GpsEngineSelectContext *ctx; + Private *priv; + + self = g_task_get_source_object (task); + priv = get_private (self); + ctx = g_task_get_task_data (task); g_assert (priv->gps_port); - mm_port_serial_at_add_unsolicited_msg_handler (priv->gps_port, priv->nmea_regex, NULL, NULL, NULL); + mm_port_serial_at_add_unsolicited_msg_handler ( + priv->gps_port, + priv->nmea_regex, + NULL, NULL, NULL); g_clear_object (&priv->gps_port); priv->gps_engine_state = GPS_ENGINE_STATE_OFF; /* If already reached requested state, we're done */ - if (state == priv->gps_engine_state) { + if (ctx->state == priv->gps_engine_state) { /* If we had an error when requesting this specific state, report it */ - if (error) - g_task_return_error (task, error); - else - g_task_return_boolean (task, TRUE); + g_task_return_boolean (task, TRUE); g_object_unref (task); return; } - /* Ignore errors if the stop operation was an intermediate one */ - g_clear_error (&error); - /* Otherwise, start with new state */ gps_engine_start (task); } +static gboolean +xlsrstop_urc_timeout (MMSharedXmm *self) +{ + GTask *task; + Private *priv; + + priv = get_private (self); + + task = recover_pending_gps_engine_stop_task (priv); + g_assert (task); + + mm_obj_dbg (self, "timed out waiting for full GPS engine stop report, assuming stopped..."); + gps_engine_stopped (task); + + return G_SOURCE_REMOVE; +} + +static void +xlsrstop_urc_received (MMPortSerialAt *port, + GMatchInfo *info, + MMSharedXmm *self) +{ + GTask *task; + Private *priv; + + priv = get_private (self); + + task = recover_pending_gps_engine_stop_task (priv); + g_assert (task); + + mm_obj_dbg (self, "GPS engine fully stopped"); + gps_engine_stopped (task); +} + +static void +xlsrstop_ready (MMBaseModem *self, + GAsyncResult *res) +{ + g_autoptr(GError) error = NULL; + + if (!mm_base_modem_at_command_full_finish (self, res, &error)) { + Private *priv; + GTask *task; + + mm_obj_dbg (self, "GPS engine stop request failed: %s", error->message); + + priv = get_private (MM_SHARED_XMM (self)); + task = recover_pending_gps_engine_stop_task (priv); + if (task) { + g_task_return_error (task, g_steal_pointer (&error)); + g_object_unref (task); + } + return; + } + + mm_obj_dbg (self, "GPS engine stop request accepted"); +} + static void gps_engine_stop (GTask *task) { - MMSharedXmm *self; - Private *priv; + MMSharedXmm *self; + Private *priv; + GpsEngineSelectContext *ctx; self = g_task_get_source_object (task); priv = get_private (self); + ctx = g_task_get_task_data (task); g_assert (priv->gps_port); + + /* After a +XLSRSTOP command the modem will reply OK to report that the stop + * request has been received, but at this point the engine may still be on. + * We must wait for the additional +XLSRSTOP URC to tell us that the engine + * is really off before going on. + * + * We do this by setting up a temporary regex match for the URC during this + * operation, and also by configuring a timeout so that we don't wait forever + * for the URC. + * + * The initial +XLSRSTOP response will be ignored unless an error is being + * reported. + * + * The operation task is kept in private info because it will be shared by all + * the possible paths that may complete it (response, URC, timeout). + */ + if (priv->pending_gps_engine_stop_task) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, + "An engine stop task is already ongoing"); + g_object_unref (task); + return; + } + priv->pending_gps_engine_stop_task = task; + + mm_obj_dbg (self, "launching GPS engine stop operation..."); + + ctx->engine_stop_timeout_id = g_timeout_add_seconds (GPS_ENGINE_STOP_TIMEOUT_SECS, + (GSourceFunc) xlsrstop_urc_timeout, + self); + + mm_port_serial_at_add_unsolicited_msg_handler ( + priv->gps_port, + priv->xlsrstop_regex, + (MMPortSerialAtUnsolicitedMsgFn)xlsrstop_urc_received, + self, + NULL); + mm_base_modem_at_command_full (MM_BASE_MODEM (self), priv->gps_port, "+XLSRSTOP", @@ -1152,7 +1294,7 @@ gps_engine_stop (GTask *task) FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)xlsrstop_ready, - task); + NULL); } static void @@ -1161,14 +1303,17 @@ gps_engine_state_select (MMSharedXmm *self, GAsyncReadyCallback callback, gpointer user_data) { - GTask *task; - Private *priv; - - task = g_task_new (self, NULL, callback, user_data); - g_task_set_task_data (task, GUINT_TO_POINTER (state), NULL); + GpsEngineSelectContext *ctx; + GTask *task; + Private *priv; priv = get_private (self); + task = g_task_new (self, NULL, callback, user_data); + ctx = g_slice_new0 (GpsEngineSelectContext); + ctx->state = state; + g_task_set_task_data (task, ctx, (GDestroyNotify)gps_engine_select_context_free); + /* If already in the requested state, we're done */ if (state == priv->gps_engine_state) { g_task_return_boolean (task, TRUE); |