aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/Makefile.am11
-rw-r--r--plugins/xmm/mm-shared-xmm.c562
-rw-r--r--plugins/xmm/mm-shared-xmm.h85
3 files changed, 657 insertions, 1 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index f17dee47..1a605f47 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -247,8 +247,17 @@ test_modem_helpers_xmm_LDADD = \
$(top_builddir)/libmm-glib/libmm-glib.la \
$(NULL)
+noinst_LTLIBRARIES += libmm-utils-xmm.la
+libmm_utils_xmm_la_SOURCES = \
+ xmm/mm-shared-xmm.h \
+ xmm/mm-shared-xmm.c \
+ $(NULL)
+libmm_utils_xmm_la_LIBADD = \
+ $(builddir)/libhelpers-xmm.la \
+ $(NULL)
+
XMM_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/xmm
-XMM_COMMON_LIBADD_FLAGS = $(builddir)/libhelpers-xmm.la
+XMM_COMMON_LIBADD_FLAGS = $(builddir)/libmm-utils-xmm.la
################################################################################
# plugin: generic
diff --git a/plugins/xmm/mm-shared-xmm.c b/plugins/xmm/mm-shared-xmm.c
new file mode 100644
index 00000000..f31af6ea
--- /dev/null
+++ b/plugins/xmm/mm-shared-xmm.c
@@ -0,0 +1,562 @@
+/* -*- 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 <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log.h"
+#include "mm-iface-modem.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 struct {
+ GArray *supported_modes;
+ GArray *supported_bands;
+ MMModemMode allowed_modes;
+} Private;
+
+static void
+private_free (Private *priv)
+{
+ if (priv->supported_modes)
+ g_array_unref (priv->supported_modes);
+ if (priv->supported_bands)
+ g_array_unref (priv->supported_bands);
+ 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);
+ 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,
+ &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;
+ Private *priv;
+ GArray *result = NULL;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ 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,
+ 3,
+ 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 (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) {
+ gchar *str;
+
+ str = mm_modem_mode_build_string_from_mask (unapplied);
+ mm_warn ("Automatic band selection not applied to non-current modes %s", str);
+ g_free (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 *)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 (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,
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)xact_set_bands_ready,
+ task);
+ g_free (command);
+}
+
+/*****************************************************************************/
+
+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);
+ }
+
+ return shared_xmm_type;
+}
diff --git a/plugins/xmm/mm-shared-xmm.h b/plugins/xmm/mm-shared-xmm.h
new file mode 100644
index 00000000..144eed27
--- /dev/null
+++ b/plugins/xmm/mm-shared-xmm.h
@@ -0,0 +1,85 @@
+/* -*- 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>
+ */
+
+#ifndef MM_SHARED_XMM_H
+#define MM_SHARED_XMM_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-iface-modem.h"
+
+#define MM_TYPE_SHARED_XMM (mm_shared_xmm_get_type ())
+#define MM_SHARED_XMM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_XMM, MMSharedXmm))
+#define MM_IS_SHARED_XMM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_XMM))
+#define MM_SHARED_XMM_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_XMM, MMSharedXmm))
+
+typedef struct _MMSharedXmm MMSharedXmm;
+
+struct _MMSharedXmm {
+ GTypeInterface g_iface;
+};
+
+GType mm_shared_xmm_get_type (void);
+
+/* Shared XMM device management support */
+
+void mm_shared_xmm_load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GArray *mm_shared_xmm_load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_xmm_load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_xmm_load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error);
+void mm_shared_xmm_set_current_modes (MMIfaceModem *self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_xmm_set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_xmm_load_supported_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GArray *mm_shared_xmm_load_supported_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_xmm_load_current_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GArray *mm_shared_xmm_load_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_xmm_set_current_bands (MMIfaceModem *self,
+ GArray *bands_array,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_xmm_set_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+#endif /* MM_SHARED_XMM_H */