summaryrefslogtreecommitdiff
path: root/plugins/xmm/mm-shared-xmm.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/xmm/mm-shared-xmm.c')
-rw-r--r--plugins/xmm/mm-shared-xmm.c1553
1 files changed, 1553 insertions, 0 deletions
diff --git a/plugins/xmm/mm-shared-xmm.c b/plugins/xmm/mm-shared-xmm.c
new file mode 100644
index 00000000..2bf5e8e2
--- /dev/null
+++ b/plugins/xmm/mm-shared-xmm.c
@@ -0,0 +1,1553 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+#include <arpa/inet.h>
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-object.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-signal.h"
+#include "mm-iface-modem-location.h"
+#include "mm-base-modem.h"
+#include "mm-base-modem-at.h"
+#include "mm-shared-xmm.h"
+#include "mm-modem-helpers-xmm.h"
+
+/*****************************************************************************/
+/* Private data context */
+
+#define PRIVATE_TAG "shared-xmm-private-tag"
+static GQuark private_quark;
+
+typedef enum {
+ GPS_ENGINE_STATE_OFF,
+ GPS_ENGINE_STATE_STANDALONE,
+ GPS_ENGINE_STATE_AGPS_MSA,
+ GPS_ENGINE_STATE_AGPS_MSB,
+} GpsEngineState;
+
+typedef struct {
+ /* Broadband modem class support */
+ MMBroadbandModemClass *broadband_modem_class_parent;
+
+ /* Modem interface support */
+ GArray *supported_modes;
+ GArray *supported_bands;
+ MMModemMode allowed_modes;
+
+ /* Location interface support */
+ MMIfaceModemLocation *iface_modem_location_parent;
+ MMModemLocationSource supported_sources;
+ MMModemLocationSource enabled_sources;
+ GpsEngineState gps_engine_state;
+ MMPortSerialAt *gps_port;
+ GRegex *xlsrstop_regex;
+ GRegex *nmea_regex;
+} Private;
+
+static void
+private_free (Private *priv)
+{
+ g_clear_object (&priv->gps_port);
+ if (priv->supported_modes)
+ g_array_unref (priv->supported_modes);
+ if (priv->supported_bands)
+ g_array_unref (priv->supported_bands);
+ g_regex_unref (priv->xlsrstop_regex);
+ g_regex_unref (priv->nmea_regex);
+ g_slice_free (Private, priv);
+}
+
+static Private *
+get_private (MMSharedXmm *self)
+{
+ Private *priv;
+
+ if (G_UNLIKELY (!private_quark))
+ private_quark = g_quark_from_static_string (PRIVATE_TAG);
+
+ priv = g_object_get_qdata (G_OBJECT (self), private_quark);
+ if (!priv) {
+ priv = g_slice_new0 (Private);
+ priv->gps_engine_state = GPS_ENGINE_STATE_OFF;
+
+ /* Setup regex for URCs */
+ priv->xlsrstop_regex = g_regex_new ("\\r\\n\\+XLSRSTOP:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ priv->nmea_regex = g_regex_new ("(?:\\r\\n)?(?:\\r\\n)?(\\$G.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+
+ /* Setup parent class' MMBroadbandModemClass */
+ g_assert (MM_SHARED_XMM_GET_INTERFACE (self)->peek_parent_broadband_modem_class);
+ priv->broadband_modem_class_parent = MM_SHARED_XMM_GET_INTERFACE (self)->peek_parent_broadband_modem_class (self);
+
+ /* Setup parent class' MMIfaceModemLocation */
+ g_assert (MM_SHARED_XMM_GET_INTERFACE (self)->peek_parent_location_interface);
+ priv->iface_modem_location_parent = MM_SHARED_XMM_GET_INTERFACE (self)->peek_parent_location_interface (self);
+
+ g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
+ }
+
+ return priv;
+}
+
+/*****************************************************************************/
+/* Supported modes/bands (Modem interface) */
+
+GArray *
+mm_shared_xmm_load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ Private *priv;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return NULL;
+
+ priv = get_private (MM_SHARED_XMM (self));
+ g_assert (priv->supported_modes);
+ return g_array_ref (priv->supported_modes);
+}
+
+GArray *
+mm_shared_xmm_load_supported_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ Private *priv;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return NULL;
+
+ priv = get_private (MM_SHARED_XMM (self));
+ g_assert (priv->supported_bands);
+ return g_array_ref (priv->supported_bands);
+}
+
+static void
+xact_test_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response ||
+ !mm_xmm_parse_xact_test_response (response,
+ self,
+ &priv->supported_modes,
+ &priv->supported_bands,
+ &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+common_load_supported_modes_bands (GTask *task)
+{
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (g_task_get_source_object (task)),
+ "+XACT=?",
+ 3,
+ TRUE, /* allow caching */
+ (GAsyncReadyCallback)xact_test_ready,
+ task);
+}
+
+void
+mm_shared_xmm_load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ priv = get_private (MM_SHARED_XMM (self));
+
+ if (!priv->supported_modes) {
+ common_load_supported_modes_bands (task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_xmm_load_supported_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ priv = get_private (MM_SHARED_XMM (self));
+
+ if (!priv->supported_bands) {
+ common_load_supported_modes_bands (task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Current modes (Modem interface) */
+
+gboolean
+mm_shared_xmm_load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error)
+{
+ MMModemModeCombination *result;
+
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
+ return FALSE;
+
+ *allowed = result->allowed;
+ *preferred = result->preferred;
+ g_free (result);
+ return TRUE;
+}
+
+static void
+xact_query_modes_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ Private *priv;
+ MMModemModeCombination *result;
+
+ priv = get_private (MM_SHARED_XMM (self));
+ result = g_new0 (MMModemModeCombination, 1);
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response || !mm_xmm_parse_xact_query_response (response, result, NULL, &error)) {
+ priv->allowed_modes = MM_MODEM_MODE_NONE;
+ g_free (result);
+ g_task_return_error (task, error);
+ } else {
+ priv->allowed_modes = result->allowed;
+ g_task_return_pointer (task, result, g_free);
+ }
+ g_object_unref (task);
+}
+
+void
+mm_shared_xmm_load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+XACT?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)xact_query_modes_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Current bands (Modem interface) */
+
+GArray *
+mm_shared_xmm_load_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return (GArray *) g_task_propagate_pointer (G_TASK (res), error);
+}
+
+
+static void
+xact_query_bands_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ GArray *result = NULL;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response ||
+ !mm_xmm_parse_xact_query_response (response, NULL, &result, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, result, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
+}
+
+void
+mm_shared_xmm_load_current_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+XACT?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)xact_query_bands_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Set current modes (Modem interface) */
+
+gboolean
+mm_shared_xmm_set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+xact_set_modes_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_xmm_set_current_modes (MMIfaceModem *self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ MMModemModeCombination mode;
+ gchar *command;
+ GError *error = NULL;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (allowed != MM_MODEM_MODE_ANY) {
+ mode.allowed = allowed;
+ mode.preferred = preferred;
+ } else {
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+ mode.allowed = mm_xmm_get_modem_mode_any (priv->supported_modes);
+ mode.preferred = MM_MODEM_MODE_NONE;
+ }
+
+ command = mm_xmm_build_xact_set_command (&mode, NULL, &error);
+ if (!command) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ command,
+ 10,
+ FALSE,
+ (GAsyncReadyCallback)xact_set_modes_ready,
+ task);
+ g_free (command);
+}
+
+/*****************************************************************************/
+/* Set current bands (Modem interface) */
+
+gboolean
+mm_shared_xmm_set_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+xact_set_bands_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static gchar *
+validate_and_build_command_set_current_bands (MMSharedXmm *self,
+ const GArray *bands_array,
+ const GArray *supported_modes,
+ MMModemMode allowed_modes,
+ GError **error)
+{
+ gboolean band_2g_found = FALSE;
+ gboolean band_3g_found = FALSE;
+ gboolean band_4g_found = FALSE;
+ GArray *unapplied_bands;
+ GError *inner_error = NULL;
+ guint i;
+
+ /* ANY applies only to the currently selected modes */
+ if (bands_array->len == 1 && g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
+ MMModemModeCombination mode;
+ MMModemMode unapplied;
+
+ /* If we are enabling automatic band selection to a mode combination that does not include
+ * all supported modes, warn about it because automatic band selection wouldn't be executed
+ * for the non-selected modes.
+ *
+ * This is a known limitation of the modem firmware.
+ */
+ unapplied = mm_xmm_get_modem_mode_any (supported_modes) & ~(allowed_modes);
+ if (unapplied != MM_MODEM_MODE_NONE) {
+ g_autofree gchar *str = NULL;
+
+ str = mm_modem_mode_build_string_from_mask (unapplied);
+ mm_obj_warn (self, "automatic band selection not applied to non-current modes %s", str);
+ }
+
+ /* Nothing else to validate, go build the command right away */
+
+ /* We must create the set command with an explicit set of allowed modes.
+ * We pass NONE as preferred, but that WON'T change the currently selected preferred mode,
+ * it will be ignored when the command is processed as an empty field will be given */
+ mode.allowed = allowed_modes;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ return mm_xmm_build_xact_set_command (&mode, bands_array, error);
+ }
+
+ unapplied_bands = g_array_new (FALSE, FALSE, sizeof (MMModemBand));
+ for (i = 0; i < bands_array->len; i++) {
+ MMModemBand band;
+
+ band = g_array_index (bands_array, MMModemBand, i);
+ if (mm_common_band_is_eutran (band)) {
+ band_4g_found = TRUE;
+ if (!(allowed_modes & MM_MODEM_MODE_4G))
+ g_array_append_val (unapplied_bands, band);
+ }
+ if (mm_common_band_is_utran (band)) {
+ band_3g_found = TRUE;
+ if (!(allowed_modes & MM_MODEM_MODE_3G))
+ g_array_append_val (unapplied_bands, band);
+ }
+ if (mm_common_band_is_gsm (band)) {
+ band_2g_found = TRUE;
+ if (!(allowed_modes & MM_MODEM_MODE_2G))
+ g_array_append_val (unapplied_bands, band);
+ }
+ }
+
+ /* If 2G selected, there must be at least one 2G band */
+ if ((allowed_modes & MM_MODEM_MODE_2G) && !band_2g_found) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "At least one GSM band is required when 2G mode is allowed");
+ goto out;
+ }
+
+ /* If 3G selected, there must be at least one 3G band */
+ if ((allowed_modes & MM_MODEM_MODE_3G) && !band_3g_found) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "At least one UTRAN band is required when 3G mode is allowed");
+ goto out;
+ }
+
+ /* If 4G selected, there must be at least one 4G band */
+ if ((allowed_modes & MM_MODEM_MODE_4G) && !band_4g_found) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "At least one E-UTRAN band is required when 4G mode is allowed");
+ goto out;
+ }
+
+ /* Don't try to modify bands for modes that are not enabled */
+ if (unapplied_bands->len > 0) {
+ gchar *str;
+
+ str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)unapplied_bands->data, unapplied_bands->len);
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot update bands for modes not currently allowed: %s", str);
+ g_free (str);
+ goto out;
+ }
+
+out:
+ if (unapplied_bands)
+ g_array_unref (unapplied_bands);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return NULL;
+ }
+
+ return mm_xmm_build_xact_set_command (NULL, bands_array, error);
+}
+
+void
+mm_shared_xmm_set_current_bands (MMIfaceModem *self,
+ GArray *bands_array,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ gchar *command = NULL;
+ GError *error = NULL;
+ Private *priv;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Setting bands requires additional validation rules based on the
+ * currently selected list of allowed modes */
+ priv = get_private (MM_SHARED_XMM (self));
+ if (priv->allowed_modes == MM_MODEM_MODE_NONE) {
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Cannot set bands if allowed modes are unknown");
+ goto out;
+ }
+
+ command = validate_and_build_command_set_current_bands (MM_SHARED_XMM (self),
+ bands_array,
+ priv->supported_modes,
+ priv->allowed_modes,
+ &error);
+
+out:
+ if (!command) {
+ g_assert (error);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ command,
+ 10,
+ FALSE,
+ (GAsyncReadyCallback)xact_set_bands_ready,
+ task);
+ g_free (command);
+}
+
+/*****************************************************************************/
+/* Power state loading (Modem interface) */
+
+MMModemPowerState
+mm_shared_xmm_load_power_state_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ guint state;
+ const gchar *response;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!response)
+ return MM_MODEM_POWER_STATE_UNKNOWN;
+
+ if (!mm_3gpp_parse_cfun_query_response (response, &state, error))
+ return MM_MODEM_POWER_STATE_UNKNOWN;
+
+ switch (state) {
+ case 1:
+ return MM_MODEM_POWER_STATE_ON;
+ case 4:
+ return MM_MODEM_POWER_STATE_LOW;
+ default:
+ break;
+ }
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unknown +CFUN state: %u", state);
+ return MM_MODEM_POWER_STATE_UNKNOWN;
+}
+
+void
+mm_shared_xmm_load_power_state (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CFUN?",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Modem power up/down/off (Modem interface) */
+
+static gboolean
+common_modem_power_operation_finish (MMSharedXmm *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+power_operation_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+common_modem_power_operation (MMSharedXmm *self,
+ const gchar *command,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ command,
+ 30,
+ FALSE,
+ (GAsyncReadyCallback) power_operation_ready,
+ task);
+}
+
+gboolean
+mm_shared_xmm_reset_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_modem_power_operation_finish (MM_SHARED_XMM (self), res, error);
+}
+
+void
+mm_shared_xmm_reset (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_modem_power_operation (MM_SHARED_XMM (self), "+CFUN=16", callback, user_data);
+}
+
+gboolean
+mm_shared_xmm_power_off_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_modem_power_operation_finish (MM_SHARED_XMM (self), res, error);
+}
+
+void
+mm_shared_xmm_power_off (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_modem_power_operation (MM_SHARED_XMM (self), "+CPWROFF", callback, user_data);
+}
+
+gboolean
+mm_shared_xmm_power_down_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_modem_power_operation_finish (MM_SHARED_XMM (self), res, error);
+}
+
+void
+mm_shared_xmm_power_down (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_modem_power_operation (MM_SHARED_XMM (self), "+CFUN=4", callback, user_data);
+}
+
+gboolean
+mm_shared_xmm_power_up_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_modem_power_operation_finish (MM_SHARED_XMM (self), res, error);
+}
+
+void
+mm_shared_xmm_power_up (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_modem_power_operation (MM_SHARED_XMM (self), "+CFUN=1", callback, user_data);
+}
+
+/*****************************************************************************/
+/* Check support (Signal interface) */
+
+gboolean
+mm_shared_xmm_signal_check_support_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+}
+
+void
+mm_shared_xmm_signal_check_support (MMIfaceModemSignal *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+XCESQ=?",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Load extended signal information (Signal interface) */
+
+gboolean
+mm_shared_xmm_signal_load_values_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ MMSignal **cdma,
+ MMSignal **evdo,
+ MMSignal **gsm,
+ MMSignal **umts,
+ MMSignal **lte,
+ MMSignal **nr5g,
+ GError **error)
+{
+ const gchar *response;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!response || !mm_xmm_xcesq_response_to_signal_info (response, self, gsm, umts, lte, error))
+ return FALSE;
+
+ if (cdma)
+ *cdma = NULL;
+ if (evdo)
+ *evdo = NULL;
+ if (nr5g)
+ *nr5g = NULL;
+
+ return TRUE;
+}
+
+void
+mm_shared_xmm_signal_load_values (MMIfaceModemSignal *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+XCESQ?",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Load capabilities (Location interface) */
+
+MMModemLocationSource
+mm_shared_xmm_location_load_capabilities_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCATION_SOURCE_NONE;
+ }
+ return (MMModemLocationSource)value;
+}
+
+static void
+xlcslsr_test_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource sources;
+ const gchar *response;
+ GError *error = NULL;
+ Private *priv;
+ gboolean transport_protocol_invalid_supported;
+ gboolean transport_protocol_supl_supported;
+ gboolean standalone_position_mode_supported;
+ gboolean ms_assisted_based_position_mode_supported;
+ gboolean loc_response_type_nmea_supported;
+ gboolean gnss_type_gps_glonass_supported;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ /* Recover parent sources */
+ sources = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response ||
+ !mm_xmm_parse_xlcslsr_test_response (response,
+ &transport_protocol_invalid_supported,
+ &transport_protocol_supl_supported,
+ &standalone_position_mode_supported,
+ &ms_assisted_based_position_mode_supported,
+ &loc_response_type_nmea_supported,
+ &gnss_type_gps_glonass_supported,
+ &error)) {
+ mm_obj_dbg (self, "XLCSLSR based GPS control unsupported: %s", error->message);
+ g_clear_error (&error);
+ } else if (!transport_protocol_invalid_supported ||
+ !standalone_position_mode_supported ||
+ !loc_response_type_nmea_supported ||
+ !gnss_type_gps_glonass_supported) {
+ mm_obj_dbg (self, "XLCSLSR based GPS control unsupported: protocol invalid %s, standalone %s, nmea %s, gps/glonass %s",
+ transport_protocol_invalid_supported ? "supported" : "unsupported",
+ standalone_position_mode_supported ? "supported" : "unsupported",
+ loc_response_type_nmea_supported ? "supported" : "unsupported",
+ gnss_type_gps_glonass_supported ? "supported" : "unsupported");
+ } else {
+ mm_obj_dbg (self, "XLCSLSR based GPS control supported");
+ priv->supported_sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW);
+
+ if (transport_protocol_supl_supported && ms_assisted_based_position_mode_supported) {
+ mm_obj_dbg (self, "XLCSLSR based A-GPS control supported");
+ priv->supported_sources |= (MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB);
+ } else {
+ mm_obj_dbg (self, "XLCSLSR based A-GPS control unsupported: protocol supl %s, ms assisted/based %s",
+ transport_protocol_supl_supported ? "supported" : "unsupported",
+ ms_assisted_based_position_mode_supported ? "supported" : "unsupported");
+ }
+
+ sources |= priv->supported_sources;
+ }
+
+ g_task_return_int (task, sources);
+ g_object_unref (task);
+}
+
+static void
+run_xlcslsr_test (GTask *task)
+{
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (g_task_get_source_object (task)),
+ "+XLCSLSR=?",
+ 3,
+ TRUE, /* allow caching */
+ (GAsyncReadyCallback)xlcslsr_test_ready,
+ task);
+}
+
+static void
+parent_load_capabilities_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource sources;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ sources = priv->iface_modem_location_parent->load_capabilities_finish (self, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* If parent already supports GPS sources, we won't do anything else */
+ if (sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
+ mm_obj_dbg (self, "no need to run XLCSLSR based location gathering");
+ g_task_return_int (task, sources);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Cache sources supported by the parent */
+ g_task_set_task_data (task, GUINT_TO_POINTER (sources), NULL);
+ run_xlcslsr_test (task);
+}
+
+void
+mm_shared_xmm_location_load_capabilities (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+ task = g_task_new (self, NULL, callback, user_data);
+
+ g_assert (priv->iface_modem_location_parent);
+
+ if (!priv->iface_modem_location_parent->load_capabilities ||
+ !priv->iface_modem_location_parent->load_capabilities_finish) {
+ /* no parent capabilities */
+ g_task_set_task_data (task, GUINT_TO_POINTER (MM_MODEM_LOCATION_SOURCE_NONE), NULL);
+ run_xlcslsr_test (task);
+ return;
+ }
+
+ priv->iface_modem_location_parent->load_capabilities (self,
+ (GAsyncReadyCallback)parent_load_capabilities_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* GPS engine state selection */
+
+static void
+nmea_received (MMPortSerialAt *port,
+ GMatchInfo *info,
+ MMSharedXmm *self)
+{
+ gchar *trace;
+
+ trace = g_match_info_fetch (info, 1);
+ mm_iface_modem_location_gps_update (MM_IFACE_MODEM_LOCATION (self), trace);
+ g_free (trace);
+}
+
+static gboolean
+gps_engine_state_select_finish (MMSharedXmm *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+xlcslsr_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GpsEngineState state;
+ const gchar *response;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ response = mm_base_modem_at_command_full_finish (self, res, &error);
+ if (!response) {
+ g_clear_object (&priv->gps_port);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ state = GPOINTER_TO_UINT (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,
+ (MMPortSerialAtUnsolicitedMsgFn)nmea_received,
+ self,
+ NULL);
+ priv->gps_engine_state = state;
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+gps_engine_start (GTask *task)
+{
+ GpsEngineState state;
+ MMSharedXmm *self;
+ Private *priv;
+ 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));
+
+ /* Look for an AT port to use for GPS. Prefer secondary port if there is one,
+ * otherwise use primary */
+ g_assert (!priv->gps_port);
+ priv->gps_port = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
+ if (!priv->gps_port) {
+ priv->gps_port = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
+ if (!priv->gps_port) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No valid port found to control GPS");
+ g_object_unref (task);
+ return;
+ }
+ }
+
+ switch (state) {
+ case GPS_ENGINE_STATE_STANDALONE:
+ transport_protocol = 2;
+ pos_mode = 3;
+ break;
+ case GPS_ENGINE_STATE_AGPS_MSB:
+ transport_protocol = 1;
+ pos_mode = 1;
+ break;
+ case GPS_ENGINE_STATE_AGPS_MSA:
+ transport_protocol = 1;
+ pos_mode = 2;
+ break;
+ case GPS_ENGINE_STATE_OFF:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ /*
+ * AT+XLCSLSR
+ * transport_protocol: 2 (invalid) or 1 (supl)
+ * pos_mode: 3 (standalone), 1 (msb) or 2 (msa)
+ * client_id: <empty>
+ * client_id_type: <empty>
+ * mlc_number: <empty>
+ * mlc_number_type: <empty>
+ * interval: 1 (seconds)
+ * service_type_id: <empty>
+ * pseudonym_indicator: <empty>
+ * loc_response_type: 1 (NMEA strings)
+ * nmea_mask: 118 (01110110: GGA,GSA,GSV,RMC,VTG)
+ * gnss_type: 0 (GPS or GLONASS)
+ */
+ g_assert (priv->gps_port);
+ cmd = g_strdup_printf ("AT+XLCSLSR=%u,%u,,,,,1,,,1,118,0", transport_protocol, pos_mode);
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ priv->gps_port,
+ cmd,
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)xlcslsr_ready,
+ task);
+ g_free (cmd);
+}
+
+static void
+xlsrstop_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GpsEngineState state;
+ GError *error = NULL;
+ Private *priv;
+
+ mm_base_modem_at_command_full_finish (self, res, &error);
+
+ priv = get_private (MM_SHARED_XMM (self));
+ state = GPOINTER_TO_UINT (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);
+ 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 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_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 void
+gps_engine_stop (GTask *task)
+{
+ MMSharedXmm *self;
+ Private *priv;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (self);
+
+ g_assert (priv->gps_port);
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ priv->gps_port,
+ "+XLSRSTOP",
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)xlsrstop_ready,
+ task);
+}
+
+static void
+gps_engine_state_select (MMSharedXmm *self,
+ GpsEngineState state,
+ 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);
+
+ priv = get_private (self);
+
+ /* If already in the requested state, we're done */
+ if (state == priv->gps_engine_state) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* If states are different we always STOP first */
+ if (priv->gps_engine_state != GPS_ENGINE_STATE_OFF) {
+ gps_engine_stop (task);
+ return;
+ }
+
+ /* If GPS already stopped, go on to START right away */
+ g_assert (state != GPS_ENGINE_STATE_OFF);
+ gps_engine_start (task);
+}
+
+static GpsEngineState
+gps_engine_state_get_expected (MMModemLocationSource sources)
+{
+ /* If at lease one of GPS nmea/raw sources enabled, engine started */
+ if (sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
+ /* If MSA A-GPS is enabled, MSA mode */
+ if (sources & MM_MODEM_LOCATION_SOURCE_AGPS_MSA)
+ return GPS_ENGINE_STATE_AGPS_MSA;
+ /* If MSB A-GPS is enabled, MSB mode */
+ if (sources & MM_MODEM_LOCATION_SOURCE_AGPS_MSB)
+ return GPS_ENGINE_STATE_AGPS_MSB;
+ /* Otherwise, STANDALONE */
+ return GPS_ENGINE_STATE_STANDALONE;
+ }
+ /* If no GPS nmea/raw sources enabled, engine stopped */
+ return GPS_ENGINE_STATE_OFF;
+}
+
+/*****************************************************************************/
+/* Disable location gathering (Location interface) */
+
+gboolean
+mm_shared_xmm_disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+disable_gps_engine_state_select_ready (MMSharedXmm *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource source;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ if (!gps_engine_state_select_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ source = GPOINTER_TO_UINT (g_task_get_task_data (task));
+ priv->enabled_sources &= ~source;
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_disable_location_gathering_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ g_assert (priv->iface_modem_location_parent);
+ if (!priv->iface_modem_location_parent->disable_location_gathering_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_xmm_disable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
+
+ priv = get_private (MM_SHARED_XMM (self));
+ g_assert (priv->iface_modem_location_parent);
+
+ /* Only consider request if it applies to one of the sources we are
+ * supporting, otherwise run parent disable */
+ if (!(priv->supported_sources & source)) {
+ /* If disabling implemented by the parent, run it. */
+ if (priv->iface_modem_location_parent->disable_location_gathering &&
+ priv->iface_modem_location_parent->disable_location_gathering_finish) {
+ priv->iface_modem_location_parent->disable_location_gathering (self,
+ source,
+ (GAsyncReadyCallback)parent_disable_location_gathering_ready,
+ task);
+ return;
+ }
+ /* Otherwise, we're done */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* We only expect GPS sources here */
+ g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSA |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSB));
+
+ /* Update engine based on the expected sources */
+ gps_engine_state_select (MM_SHARED_XMM (self),
+ gps_engine_state_get_expected (priv->enabled_sources & ~source),
+ (GAsyncReadyCallback) disable_gps_engine_state_select_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Enable location gathering (Location interface) */
+
+gboolean
+mm_shared_xmm_enable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+enable_gps_engine_state_select_ready (MMSharedXmm *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource source;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ if (!gps_engine_state_select_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ source = GPOINTER_TO_UINT (g_task_get_task_data (task));
+ priv->enabled_sources |= source;
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_enable_location_gathering_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ g_assert (priv->iface_modem_location_parent);
+ if (!priv->iface_modem_location_parent->enable_location_gathering_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_xmm_enable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
+
+ priv = get_private (MM_SHARED_XMM (self));
+ g_assert (priv->iface_modem_location_parent);
+
+ /* Only consider request if it applies to one of the sources we are
+ * supporting, otherwise run parent enable */
+ if (priv->iface_modem_location_parent->enable_location_gathering &&
+ priv->iface_modem_location_parent->enable_location_gathering_finish &&
+ !(priv->supported_sources & source)) {
+ priv->iface_modem_location_parent->enable_location_gathering (self,
+ source,
+ (GAsyncReadyCallback)parent_enable_location_gathering_ready,
+ task);
+ return;
+ }
+
+ /* We only expect GPS sources here */
+ g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSA |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSB));
+
+ /* Update engine based on the expected sources */
+ gps_engine_state_select (MM_SHARED_XMM (self),
+ gps_engine_state_get_expected (priv->enabled_sources | source),
+ (GAsyncReadyCallback) enable_gps_engine_state_select_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Location: Load SUPL server */
+
+gchar *
+mm_shared_xmm_location_load_supl_server_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+xlcsslp_query_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ gchar *supl_address;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response || !mm_xmm_parse_xlcsslp_query_response (response, &supl_address, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, supl_address, g_free);
+ g_object_unref (task);
+}
+
+void
+mm_shared_xmm_location_load_supl_server (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+XLCSSLP?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)xlcsslp_query_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_shared_xmm_location_set_supl_server_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+xlcsslp_set_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_xmm_location_set_supl_server (MMIfaceModemLocation *self,
+ const gchar *supl,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ gchar *cmd = NULL;
+ gchar *fqdn = NULL;
+ guint32 ip;
+ guint16 port;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_parse_supl_address (supl, &fqdn, &ip, &port, NULL);
+ g_assert (port);
+ if (fqdn)
+ cmd = g_strdup_printf ("+XLCSSLP=1,%s,%u", fqdn, port);
+ else if (ip) {
+ struct in_addr a = { .s_addr = ip };
+ gchar buf[INET_ADDRSTRLEN + 1] = { 0 };
+
+ /* we got 'ip' from inet_pton(), so this next step should always succeed */
+ g_assert (inet_ntop (AF_INET, &a, buf, sizeof (buf) - 1));
+ cmd = g_strdup_printf ("+XLCSSLP=0,%s,%u", buf, port);
+ } else
+ g_assert_not_reached ();
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ cmd,
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)xlcsslp_set_ready,
+ task);
+ g_free (cmd);
+ g_free (fqdn);
+}
+
+/*****************************************************************************/
+
+void
+mm_shared_xmm_setup_ports (MMBroadbandModem *self)
+{
+ Private *priv;
+ MMPortSerialAt *ports[2];
+ guint i;
+
+ priv = get_private (MM_SHARED_XMM (self));
+ g_assert (priv->broadband_modem_class_parent);
+ g_assert (priv->broadband_modem_class_parent->setup_ports);
+
+ /* Parent setup first always */
+ priv->broadband_modem_class_parent->setup_ports (self);
+
+ ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ /* Setup primary and secondary ports */
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
+ if (!ports[i])
+ continue;
+
+ /* After running AT+XLSRSTOP we may get an unsolicited response
+ * reporting its status, we just ignore it. */
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ priv->xlsrstop_regex,
+ NULL, NULL, NULL);
+
+
+
+ /* make sure GPS is stopped in case it was left enabled */
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ ports[i],
+ "+XLSRSTOP",
+ 3, FALSE, FALSE, NULL, NULL, NULL);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+shared_xmm_init (gpointer g_iface)
+{
+}
+
+GType
+mm_shared_xmm_get_type (void)
+{
+ static GType shared_xmm_type = 0;
+
+ if (!G_UNLIKELY (shared_xmm_type)) {
+ static const GTypeInfo info = {
+ sizeof (MMSharedXmm), /* class_size */
+ shared_xmm_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ shared_xmm_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedXmm", &info, 0);
+ g_type_interface_add_prerequisite (shared_xmm_type, MM_TYPE_IFACE_MODEM);
+ g_type_interface_add_prerequisite (shared_xmm_type, MM_TYPE_IFACE_MODEM_LOCATION);
+ }
+
+ return shared_xmm_type;
+}