diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2020-06-01 15:09:20 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2020-06-01 15:43:48 +0000 |
commit | 6eca223705b0e5c5ce52fff77858c41c82a854d7 (patch) | |
tree | 355193e1545ecce88c78fe18ddaa4d42d7eff105 | |
parent | fd28f9475011d3d62202b650b64a64b7c42b5027 (diff) |
shared-qmi: require minimum set of NMEA types before starting GNSS engine
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | src/mm-shared-qmi.c | 248 |
2 files changed, 245 insertions, 5 deletions
diff --git a/configure.ac b/configure.ac index 58f44621..d7546aa9 100644 --- a/configure.ac +++ b/configure.ac @@ -394,7 +394,7 @@ dnl----------------------------------------------------------------------------- dnl QMI support (enabled by default) dnl -LIBQMI_VERSION=1.25.5 +LIBQMI_VERSION=1.25.900 AC_ARG_WITH(qmi, AS_HELP_STRING([--without-qmi], [Build without QMI support]), [], [with_qmi=yes]) AM_CONDITIONAL(WITH_QMI, test "x$with_qmi" = "xyes") diff --git a/src/mm-shared-qmi.c b/src/mm-shared-qmi.c index 1b18719a..bbcc169b 100644 --- a/src/mm-shared-qmi.c +++ b/src/mm-shared-qmi.c @@ -3866,6 +3866,230 @@ loc_location_nmea_indication_cb (QmiClientLoc *client, } /*****************************************************************************/ +/* Location: internal helper: setup minimum required NMEA traces */ + +typedef struct { + QmiClientLoc *client; + guint timeout_id; + gulong indication_id; +} SetupRequiredNmeaTracesContext; + +static void +setup_required_nmea_traces_cleanup_action (SetupRequiredNmeaTracesContext *ctx) +{ + if (ctx->indication_id) { + g_signal_handler_disconnect (ctx->client, ctx->indication_id); + ctx->indication_id = 0; + } + if (ctx->timeout_id) { + g_source_remove (ctx->timeout_id); + ctx->timeout_id = 0; + } +} + +static void +setup_required_nmea_traces_context_free (SetupRequiredNmeaTracesContext *ctx) +{ + setup_required_nmea_traces_cleanup_action (ctx); + g_clear_object (&ctx->client); + g_slice_free (SetupRequiredNmeaTracesContext, ctx); +} + +static gboolean +setup_required_nmea_traces_finish (MMSharedQmi *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static gboolean +setup_required_nmea_traces_timeout (GTask *task) +{ + SetupRequiredNmeaTracesContext *ctx; + + ctx = g_task_get_task_data (task); + g_assert (ctx->timeout_id); + setup_required_nmea_traces_cleanup_action (ctx); + + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, + "Operation timed out"); + g_object_unref (task); + return G_SOURCE_REMOVE; +} + +static void +loc_set_nmea_types_indication_cb (QmiClientLoc *client, + QmiIndicationLocSetNmeaTypesOutput *output, + GTask *task) +{ + SetupRequiredNmeaTracesContext *ctx; + QmiLocIndicationStatus status; + GError *error = NULL; + + ctx = g_task_get_task_data (task); + g_assert (ctx->indication_id); + setup_required_nmea_traces_cleanup_action (ctx); + + if (!qmi_indication_loc_set_nmea_types_output_get_indication_status (output, &status, &error) || + !mm_error_from_qmi_loc_indication_status (status, &error)) + g_task_return_error (task, error); + else + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void +loc_set_nmea_types_ready (QmiClientLoc *client, + GAsyncResult *res, + GTask *task) +{ + SetupRequiredNmeaTracesContext *ctx; + GError *error = NULL; + g_autoptr(QmiMessageLocSetNmeaTypesOutput) output = NULL; + + output = qmi_client_loc_set_nmea_types_finish (client, res, &error); + if (!output || !qmi_message_loc_set_nmea_types_output_get_result (output, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* The task ownership is shared between signal and timeout; the one which is + * scheduled first will cancel the other. */ + ctx = g_task_get_task_data (task); + g_assert (!ctx->indication_id); + ctx->indication_id = g_signal_connect (ctx->client, + "set-nmea-types", + G_CALLBACK (loc_set_nmea_types_indication_cb), + task); + g_assert (!ctx->timeout_id); + ctx->timeout_id = g_timeout_add_seconds (10, + (GSourceFunc)setup_required_nmea_traces_timeout, + task); +} + +static void +loc_get_nmea_types_indication_cb (QmiClientLoc *client, + QmiIndicationLocGetNmeaTypesOutput *output, + GTask *task) +{ + SetupRequiredNmeaTracesContext *ctx; + QmiLocIndicationStatus status; + QmiLocNmeaType nmea_types_mask = 0; + QmiLocNmeaType desired_nmea_types_mask = (QMI_LOC_NMEA_TYPE_GGA | QMI_LOC_NMEA_TYPE_GSA | QMI_LOC_NMEA_TYPE_GSV); + GError *error = NULL; + g_autoptr(QmiMessageLocSetNmeaTypesInput) input = NULL; + + ctx = g_task_get_task_data (task); + g_assert (ctx->indication_id); + setup_required_nmea_traces_cleanup_action (ctx); + + if (!qmi_indication_loc_get_nmea_types_output_get_indication_status (output, &status, &error) || + !mm_error_from_qmi_loc_indication_status (status, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + qmi_indication_loc_get_nmea_types_output_get_nmea_types (output, &nmea_types_mask, NULL); + + /* If the configured NMEA types already include GGA, GSV and GSA, we're fine. For raw + * GPS sources GGA is the only required one, the other two are given for completeness */ + if ((nmea_types_mask & desired_nmea_types_mask) == desired_nmea_types_mask) { + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + } + + input = qmi_message_loc_set_nmea_types_input_new (); + qmi_message_loc_set_nmea_types_input_set_nmea_types (input, (nmea_types_mask | desired_nmea_types_mask), NULL); + qmi_client_loc_set_nmea_types (ctx->client, + input, + 10, + NULL, + (GAsyncReadyCallback)loc_set_nmea_types_ready, + task); +} + +static void +loc_get_nmea_types_ready (QmiClientLoc *client, + GAsyncResult *res, + GTask *task) +{ + SetupRequiredNmeaTracesContext *ctx; + GError *error = NULL; + g_autoptr(QmiMessageLocGetNmeaTypesOutput) output = NULL; + + output = qmi_client_loc_get_nmea_types_finish (client, res, &error); + if (!output || !qmi_message_loc_get_nmea_types_output_get_result (output, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* The task ownership is shared between signal and timeout; the one which is + * scheduled first will cancel the other. */ + ctx = g_task_get_task_data (task); + g_assert (!ctx->indication_id); + ctx->indication_id = g_signal_connect (ctx->client, + "get-nmea-types", + G_CALLBACK (loc_get_nmea_types_indication_cb), + task); + g_assert (!ctx->timeout_id); + ctx->timeout_id = g_timeout_add_seconds (10, + (GSourceFunc)setup_required_nmea_traces_timeout, + task); +} + +static void +setup_required_nmea_traces (MMSharedQmi *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + QmiClient *client; + GTask *task; + + task = g_task_new (self, NULL, callback, user_data); + + /* If using PDS, no further setup required */ + client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), + QMI_SERVICE_PDS, + MM_PORT_QMI_FLAG_DEFAULT, + NULL); + if (client) { + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + } + + /* Otherwise LOC */ + client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), + QMI_SERVICE_LOC, + MM_PORT_QMI_FLAG_DEFAULT, + NULL); + if (client) { + SetupRequiredNmeaTracesContext *ctx; + + ctx = g_slice_new0 (SetupRequiredNmeaTracesContext); + ctx->client = g_object_ref (client); + g_task_set_task_data (task, ctx, (GDestroyNotify)setup_required_nmea_traces_context_free); + + qmi_client_loc_get_nmea_types (ctx->client, + NULL, + 10, + NULL, + (GAsyncReadyCallback)loc_get_nmea_types_ready, + task); + return; + } + + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't find any PDS/LOC client"); + g_object_unref (task); +} + +/*****************************************************************************/ /* Location: internal helper: start gps engine */ static gboolean @@ -4751,6 +4975,22 @@ start_gps_engine_ready (MMSharedQmi *self, } static void +setup_required_nmea_traces_ready (MMSharedQmi *self, + GAsyncResult *res, + GTask *task) +{ + g_autoptr(GError) error = NULL; + + /* don't treat this error as fatal */ + if (!setup_required_nmea_traces_finish (self, res, &error)) + mm_obj_warn (self, "couldn't setup required NMEA traces: %s", error->message); + + start_gps_engine (self, + (GAsyncReadyCallback)start_gps_engine_ready, + task); +} + +static void set_gps_operation_mode_agps_ready (MMSharedQmi *self, GAsyncResult *res, GTask *task) @@ -4822,11 +5062,11 @@ parent_enable_location_gathering_ready (MMIfaceModemLocation *_self, return; } - /* Only start GPS engine if not done already */ + /* Only setup NMEA traces and start GPS engine if not done already */ if (!(priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW))) { - start_gps_engine (self, - (GAsyncReadyCallback)start_gps_engine_ready, - task); + setup_required_nmea_traces (self, + (GAsyncReadyCallback)setup_required_nmea_traces_ready, + task); return; } |