aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/telit
diff options
context:
space:
mode:
authorAleksander Morgado <aleksandermj@chromium.org>2022-12-08 13:37:55 +0000
committerAleksander Morgado <aleksander@aleksander.es>2023-01-03 13:56:25 +0000
commite14b904cbd6816cb0227d519d308ae71ddaf6e07 (patch)
tree4997ab68cc606fdf4d72a571e821cec0c8df42ef /src/plugins/telit
parent072d7ac9065f444e83b390a1e2af5471ac0d48f6 (diff)
build: move plugins directory to src/plugins
We are going to allow including the plugin sources built within the ModemManager daemon binary; moving the sources within the daemon sources directory makes it easier.
Diffstat (limited to 'src/plugins/telit')
-rw-r--r--src/plugins/telit/77-mm-telit-port-types.rules146
-rw-r--r--src/plugins/telit/mm-broadband-modem-mbim-telit.c242
-rw-r--r--src/plugins/telit/mm-broadband-modem-mbim-telit.h48
-rw-r--r--src/plugins/telit/mm-broadband-modem-telit.c1562
-rw-r--r--src/plugins/telit/mm-broadband-modem-telit.h51
-rw-r--r--src/plugins/telit/mm-common-telit.c373
-rw-r--r--src/plugins/telit/mm-common-telit.h40
-rw-r--r--src/plugins/telit/mm-modem-helpers-telit.c967
-rw-r--r--src/plugins/telit/mm-modem-helpers-telit.h90
-rw-r--r--src/plugins/telit/mm-plugin-telit.c132
-rw-r--r--src/plugins/telit/mm-plugin-telit.h42
-rw-r--r--src/plugins/telit/mm-shared-telit.c795
-rw-r--r--src/plugins/telit/mm-shared-telit.h107
-rw-r--r--src/plugins/telit/mm-shared.c20
-rw-r--r--src/plugins/telit/tests/test-mm-modem-helpers-telit.c695
15 files changed, 5310 insertions, 0 deletions
diff --git a/src/plugins/telit/77-mm-telit-port-types.rules b/src/plugins/telit/77-mm-telit-port-types.rules
new file mode 100644
index 00000000..b9439ffc
--- /dev/null
+++ b/src/plugins/telit/77-mm-telit-port-types.rules
@@ -0,0 +1,146 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change|move|bind", GOTO="mm_telit_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="1bc7", GOTO="mm_telit_generic"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="8087", GOTO="mm_telit_intel"
+GOTO="mm_telit_end"
+
+LABEL="mm_telit_generic"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
+# UC864-E, UC864-E-AUTO, UC864-K, UC864-WD, UC864-WDU
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1003", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1003", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# UC864-G
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1004", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1004", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1004", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# CC864-DUAL
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1005", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1005", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1005", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# CC864-SINGLE, CC864-KPS
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1006", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1006", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# DE910-DUAL
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+
+# CE910-DUAL
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1011", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+
+# LE910C1-EUX
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1031", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1031", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1031", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# LE910C1-EUX (ECM composition)
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1033", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1033", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1033", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# LE922, LM9x0
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+
+# LE922, LM9x0 (MBIM composition)
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{.MM_USBIFNUM}=="07", ENV{ID_MM_PORT_IGNORE}="1"
+
+# FN980
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+
+# FN980 (MBIM composition)
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{.MM_USBIFNUM}=="07", ENV{ID_MM_PORT_IGNORE}="1"
+
+# LN920
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+
+# LN920 (MBIM composition)
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{.MM_USBIFNUM}=="07", ENV{ID_MM_PORT_IGNORE}="1"
+
+# LE910C1 with default usb cfg
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+
+# LE910C1 (MBIM)
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1204", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1204", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1204", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1204", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1204", ENV{.MM_USBIFNUM}=="07", ENV{ID_MM_PORT_IGNORE}="1"
+
+# ME910C1
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1101", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1101", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1101", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# MEx10G1
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="110a", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="110a", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="110a", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# LE910S1 (RNDIS)
+# The following port is ignored since it's a diagnostic port for collecting proprietary modem traces (not QCDM)
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7010", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7010", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7010", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+
+# LE910S1 (ECM)
+# The following port is ignored since it's a diagnostic port for collecting proprietary modem traces (not QCDM)
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7011", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7011", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7011", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+
+# LM940/960 initial port delay
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{ID_MM_TELIT_PORT_DELAY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{ID_MM_TELIT_PORT_DELAY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1042", ENV{ID_MM_TELIT_PORT_DELAY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1043", ENV{ID_MM_TELIT_PORT_DELAY}="1"
+
+# FN980 initial port delay
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{ID_MM_TELIT_PORT_DELAY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{ID_MM_TELIT_PORT_DELAY}="1"
+
+# LN920 initial port delay
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{ID_MM_TELIT_PORT_DELAY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{ID_MM_TELIT_PORT_DELAY}="1"
+
+GOTO="mm_telit_end"
+
+LABEL="mm_telit_intel"
+
+# Telit LN930, generic Intel vid:pid in MBIM mode
+ATTRS{idVendor}=="8087", ATTRS{idProduct}=="0911", ENV{ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED}="1"
+
+LABEL="mm_telit_end"
diff --git a/src/plugins/telit/mm-broadband-modem-mbim-telit.c b/src/plugins/telit/mm-broadband-modem-mbim-telit.c
new file mode 100644
index 00000000..8437c841
--- /dev/null
+++ b/src/plugins/telit/mm-broadband-modem-mbim-telit.c
@@ -0,0 +1,242 @@
+/* -*- 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) 2019 Daniele Palmas <dnlplm@gmail.com>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "ModemManager.h"
+#include "mm-log-object.h"
+#include "mm-modem-helpers.h"
+#include "mm-iface-modem.h"
+#include "mm-base-modem-at.h"
+#include "mm-broadband-modem-mbim-telit.h"
+#include "mm-modem-helpers-telit.h"
+#include "mm-shared-telit.h"
+
+static void iface_modem_init (MMIfaceModem *iface);
+static void shared_telit_init (MMSharedTelit *iface);
+
+static MMIfaceModem *iface_modem_parent;
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimTelit, mm_broadband_modem_mbim_telit, MM_TYPE_BROADBAND_MODEM_MBIM, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_TELIT, shared_telit_init))
+
+/*****************************************************************************/
+/* Load supported modes (Modem interface) */
+
+static GArray *
+load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return (GArray *) g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_supported_modes_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemModeCombination modes_combination;
+ MMModemMode modes_mask = MM_MODEM_MODE_NONE;
+ const gchar *response;
+ GArray *modes;
+ GArray *all;
+ GArray *combinations;
+ GArray *filtered;
+ GError *error = NULL;
+ MMSharedTelit *shared = MM_SHARED_TELIT (self);
+ guint i;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (error) {
+ g_prefix_error (&error, "generic query of supported 3GPP networks with WS46=? failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ modes = mm_3gpp_parse_ws46_test_response (response, self, &error);
+ if (!modes) {
+ g_prefix_error (&error, "parsing WS46=? response failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ for (i = 0; i < modes->len; i++) {
+ MMModemMode mode;
+ g_autofree gchar *str = NULL;
+
+ mode = g_array_index (modes, MMModemMode, i);
+
+ modes_mask |= mode;
+
+ str = mm_modem_mode_build_string_from_mask (mode);
+ mm_obj_dbg (self, "device allows (3GPP) mode combination: %s", str);
+ }
+
+ g_array_unref (modes);
+
+ /* Build a mask with all supported modes */
+ all = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
+ modes_combination.allowed = modes_mask;
+ modes_combination.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (all, modes_combination);
+
+ /* Filter out those unsupported modes */
+ combinations = mm_telit_build_modes_list();
+ filtered = mm_filter_supported_modes (all, combinations, self);
+ g_array_unref (all);
+ g_array_unref (combinations);
+
+ mm_shared_telit_store_supported_modes (shared, filtered);
+ g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
+}
+
+static void
+load_supported_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),
+ "+WS46=?",
+ 3,
+ TRUE, /* allow caching, it's a test command */
+ (GAsyncReadyCallback) load_supported_modes_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Load revision (Modem interface) */
+
+static gchar *
+load_revision_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+parent_load_revision_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ gchar *revision = NULL;
+
+ revision = iface_modem_parent->load_revision_finish (self, res, &error);
+ if (!revision) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ mm_shared_telit_store_revision (MM_SHARED_TELIT (self), revision);
+ g_task_return_pointer (task, revision, g_free);
+ g_object_unref (task);
+}
+
+static void
+load_revision (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ /* Run parent's loading */
+ /* Telit's custom revision loading (in telit/mm-shared) is AT-only and the
+ * MBIM modem might not have an AT port available, so we call the parent's
+ * load_revision and store the revision taken from the firmware info capabilities. */
+ iface_modem_parent->load_revision (
+ MM_IFACE_MODEM (self),
+ (GAsyncReadyCallback)parent_load_revision_ready,
+ g_task_new (self, NULL, callback, user_data));
+}
+
+/*****************************************************************************/
+
+MMBroadbandModemMbimTelit *
+mm_broadband_modem_mbim_telit_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id,
+ guint16 subsystem_vendor_id)
+{
+ return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_TELIT,
+ MM_BASE_MODEM_DEVICE, device,
+ MM_BASE_MODEM_DRIVERS, drivers,
+ MM_BASE_MODEM_PLUGIN, plugin,
+ MM_BASE_MODEM_VENDOR_ID, vendor_id,
+ MM_BASE_MODEM_PRODUCT_ID, product_id,
+ MM_BASE_MODEM_SUBSYSTEM_VENDOR_ID, subsystem_vendor_id,
+ /* MBIM bearer supports NET only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
+ NULL);
+}
+
+static void
+mm_broadband_modem_mbim_telit_init (MMBroadbandModemMbimTelit *self)
+{
+}
+
+static void
+iface_modem_init (MMIfaceModem *iface)
+{
+ iface_modem_parent = g_type_interface_peek_parent (iface);
+
+ iface->set_current_bands = mm_shared_telit_modem_set_current_bands;
+ iface->set_current_bands_finish = mm_shared_telit_modem_set_current_bands_finish;
+ iface->load_current_bands = mm_shared_telit_modem_load_current_bands;
+ iface->load_current_bands_finish = mm_shared_telit_modem_load_current_bands_finish;
+ iface->load_supported_bands = mm_shared_telit_modem_load_supported_bands;
+ iface->load_supported_bands_finish = mm_shared_telit_modem_load_supported_bands_finish;
+ iface->load_supported_modes = load_supported_modes;
+ iface->load_supported_modes_finish = load_supported_modes_finish;
+ iface->load_current_modes = mm_shared_telit_load_current_modes;
+ iface->load_current_modes_finish = mm_shared_telit_load_current_modes_finish;
+ iface->set_current_modes = mm_shared_telit_set_current_modes;
+ iface->set_current_modes_finish = mm_shared_telit_set_current_modes_finish;
+ iface->load_revision_finish = load_revision_finish;
+ iface->load_revision = load_revision;
+}
+
+static MMIfaceModem *
+peek_parent_modem_interface (MMSharedTelit *self)
+{
+ return iface_modem_parent;
+}
+
+static void
+shared_telit_init (MMSharedTelit *iface)
+{
+ iface->peek_parent_modem_interface = peek_parent_modem_interface;
+}
+
+static void
+mm_broadband_modem_mbim_telit_class_init (MMBroadbandModemMbimTelitClass *klass)
+{
+}
diff --git a/src/plugins/telit/mm-broadband-modem-mbim-telit.h b/src/plugins/telit/mm-broadband-modem-mbim-telit.h
new file mode 100644
index 00000000..50c21e20
--- /dev/null
+++ b/src/plugins/telit/mm-broadband-modem-mbim-telit.h
@@ -0,0 +1,48 @@
+/* -*- 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) 2019 Daniele Palmas <dnlplm@gmail.com>
+ */
+
+#ifndef MM_BROADBAND_MODEM_MBIM_TELIT_H
+#define MM_BROADBAND_MODEM_MBIM_TELIT_H
+
+#include "mm-broadband-modem-mbim.h"
+
+#define MM_TYPE_BROADBAND_MODEM_MBIM_TELIT (mm_broadband_modem_mbim_telit_get_type ())
+#define MM_BROADBAND_MODEM_MBIM_TELIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_TELIT, MMBroadbandModemMbimTelit))
+#define MM_BROADBAND_MODEM_MBIM_TELIT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_TELIT, MMBroadbandModemMbimTelitClass))
+#define MM_IS_BROADBAND_MODEM_MBIM_TELIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_TELIT))
+#define MM_IS_BROADBAND_MODEM_MBIM_TELIT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_TELIT))
+#define MM_BROADBAND_MODEM_MBIM_TELIT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_TELIT, MMBroadbandModemMbimTelitClass))
+
+typedef struct _MMBroadbandModemMbimTelit MMBroadbandModemMbimTelit;
+typedef struct _MMBroadbandModemMbimTelitClass MMBroadbandModemMbimTelitClass;
+
+struct _MMBroadbandModemMbimTelit {
+ MMBroadbandModemMbim parent;
+};
+
+struct _MMBroadbandModemMbimTelitClass{
+ MMBroadbandModemMbimClass parent;
+};
+
+GType mm_broadband_modem_mbim_telit_get_type (void);
+
+MMBroadbandModemMbimTelit *mm_broadband_modem_mbim_telit_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id,
+ guint16 subsystem_vendor_id);
+
+#endif /* MM_BROADBAND_MODEM_TELIT_H */
diff --git a/src/plugins/telit/mm-broadband-modem-telit.c b/src/plugins/telit/mm-broadband-modem-telit.c
new file mode 100644
index 00000000..1683d38a
--- /dev/null
+++ b/src/plugins/telit/mm-broadband-modem-telit.c
@@ -0,0 +1,1562 @@
+/* -*- 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) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2012 Red Hat, Inc.
+ * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "ModemManager.h"
+#include "mm-log-object.h"
+#include "mm-errors-types.h"
+#include "mm-modem-helpers.h"
+#include "mm-base-modem-at.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-location.h"
+#include "mm-broadband-modem-telit.h"
+#include "mm-modem-helpers-telit.h"
+#include "mm-telit-enums-types.h"
+#include "mm-shared-telit.h"
+
+static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
+static void shared_telit_init (MMSharedTelit *iface);
+static void iface_modem_location_init (MMIfaceModemLocation *iface);
+
+static MMIfaceModem *iface_modem_parent;
+static MMIfaceModem3gpp *iface_modem_3gpp_parent;
+static MMIfaceModemLocation *iface_modem_location_parent;
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemTelit, mm_broadband_modem_telit, MM_TYPE_BROADBAND_MODEM, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_TELIT, shared_telit_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init));
+
+#define CSIM_UNLOCK_MAX_TIMEOUT 3
+
+typedef enum {
+ FEATURE_SUPPORT_UNKNOWN,
+ FEATURE_NOT_SUPPORTED,
+ FEATURE_SUPPORTED
+} FeatureSupport;
+
+struct _MMBroadbandModemTelitPrivate {
+ FeatureSupport csim_lock_support;
+ MMTelitQssStatus qss_status;
+ MMTelitCsimLockState csim_lock_state;
+ GTask *csim_lock_task;
+ guint csim_lock_timeout_id;
+ gboolean parse_qss;
+ MMModemLocationSource enabled_sources;
+};
+
+typedef struct {
+ MMModemLocationSource source;
+ guint gps_enable_step;
+} LocationGatheringContext;
+
+/*
+ * AT$GPSNMUN
+ * enable: 0 NMEA stream disabled (default)
+ * 1 NMEA stream enabled in the form $GPSNMUN: <nmea sentence><CR>
+ * 2 NMEA stream enabled in the form <nmea sentence><CR>
+ * 3 dedicated NMEA stream
+ * GGA: 0 disable (default), 1 enable
+ * GLL: 0 disable (default), 1 enable
+ * GSA: 0 disable (default), 1 enable
+ * GSV: 0 disable (default), 1 enable
+ * RMC: 0 disable (default), 1 enable
+ * VTG: 0 disable (default), 1 enable
+ */
+static const gchar *gps_enable[] = {
+ "$GPSP=1",
+ "$GPSNMUN=2,1,1,1,1,1,1"
+};
+
+static gboolean
+disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+gps_disabled_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ LocationGatheringContext *ctx;
+ MMPortSerialGps *gps_port;
+ GError *error = NULL;
+
+ mm_base_modem_at_command_finish (self, res, &error);
+ ctx = g_task_get_task_data (task);
+ /* Only use the GPS port in NMEA/RAW setups */
+ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
+ /* Even if we get an error here, we try to close the GPS port */
+ gps_port = mm_base_modem_peek_port_gps (self);
+ if (gps_port)
+ mm_port_serial_close (MM_PORT_SERIAL (gps_port));
+ }
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
+}
+
+static void
+disable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemTelit *telit = MM_BROADBAND_MODEM_TELIT (self);
+ gboolean stop_gps = FALSE;
+ LocationGatheringContext *ctx;
+ GTask *task;
+
+ ctx = g_new (LocationGatheringContext, 1);
+ ctx->source = source;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ /* Only stop GPS engine if no GPS-related sources enabled */
+ if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
+ telit->priv->enabled_sources &= ~source;
+
+ if (!(telit->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)))
+ stop_gps = TRUE;
+ }
+
+ if (stop_gps) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "$GPSP=0",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)gps_disabled_ready,
+ task);
+ return;
+ }
+ /* For any other location (e.g. 3GPP), or if still some GPS needed, just return */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+gps_enabled_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ LocationGatheringContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ g_prefix_error (&error, "couldn't power up GNSS controller: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ /* After Receiver was powered up we still have to enable unsolicited NMEA events */
+ if (ctx->gps_enable_step < G_N_ELEMENTS (gps_enable)) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ gps_enable[ctx->gps_enable_step++],
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)gps_enabled_ready,
+ task);
+ return;
+ }
+
+ mm_obj_dbg (self, "GNSS controller is powered up");
+
+ /* Only use the GPS port in NMEA/RAW setups */
+ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
+ MMPortSerialGps *gps_port;
+
+ gps_port = mm_base_modem_peek_port_gps (self);
+ if (!gps_port ||
+ !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) {
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't open raw GPS serial port");
+ } else
+ g_task_return_boolean (task, TRUE);
+ } else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
+}
+
+static void
+parent_enable_location_gathering_ready (MMIfaceModemLocation *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemTelit *self = MM_BROADBAND_MODEM_TELIT (_self);
+ LocationGatheringContext *ctx;
+ gboolean start_gps = FALSE;
+ GError *error = NULL;
+
+ if (!iface_modem_location_parent->enable_location_gathering_finish (_self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ /* Now our own enabling */
+ ctx = g_task_get_task_data (task);
+
+ /* NMEA, RAW and UNMANAGED are all enabled in the same way */
+ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
+ /* Only start GPS engine if not done already */
+ if (!(self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)))
+ start_gps = TRUE;
+ self->priv->enabled_sources |= ctx->source;
+ }
+
+ if (start_gps && ctx->gps_enable_step < G_N_ELEMENTS (gps_enable)) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ gps_enable[ctx->gps_enable_step++],
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)gps_enabled_ready,
+ task);
+ return;
+ }
+ /* For any other location (e.g. 3GPP), or if GPS already running just return */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+enable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ LocationGatheringContext *ctx;
+ GTask *task;
+
+ ctx = g_new (LocationGatheringContext, 1);
+ ctx->source = source;
+ ctx->gps_enable_step = 0;
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ /* Chain up parent's gathering enable */
+ iface_modem_location_parent->enable_location_gathering (
+ self,
+ source,
+ (GAsyncReadyCallback)parent_enable_location_gathering_ready,
+ task);
+}
+
+static void
+trace_received (MMPortSerialGps *port,
+ const gchar *trace,
+ MMIfaceModemLocation *self)
+{
+ mm_iface_modem_location_gps_update (self, trace);
+}
+
+static gboolean
+enable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+setup_ports (MMBroadbandModem *self)
+{
+ MMPortSerialGps *gps_data_port;
+
+ /* Call parent's setup ports first always */
+ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_telit_parent_class)->setup_ports (self);
+
+ gps_data_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self));
+ if (gps_data_port) {
+ /* It may happen that the modem was started with GPS already enabled,
+ * in this case GPSP AT command returns always error. Disable it for consistency
+ */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "$GPSP=0", 3, FALSE, FALSE, NULL);
+
+ /* Add handler for the NMEA traces */
+ mm_port_serial_gps_add_trace_handler (gps_data_port,
+ (MMPortSerialGpsTraceFn)trace_received,
+ self,
+ NULL);
+ }
+}
+
+static MMModemLocationSource
+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
+gpsp_test_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ MMModemLocationSource sources;
+
+ sources = GPOINTER_TO_UINT (g_task_get_task_data (task));
+ mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (error) {
+ mm_obj_dbg (self, "GPS controller not supported: %s", error->message);
+ g_clear_error (&error);
+ } else if (mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)))
+ sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED);
+
+ g_task_return_int (task, sources);
+ g_object_unref (task);
+}
+
+static void
+parent_load_capabilities_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource sources;
+ GError *error = NULL;
+
+ sources = iface_modem_location_parent->load_capabilities_finish (self, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ g_task_set_task_data (task, GUINT_TO_POINTER (sources), NULL);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "$GPSP=?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback)gpsp_test_ready,
+ task);
+}
+
+static void
+location_load_capabilities (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ /* Chain up parent's setup */
+ iface_modem_location_parent->load_capabilities (
+ self,
+ (GAsyncReadyCallback)parent_load_capabilities_ready,
+ g_task_new (self, NULL, callback, user_data));
+}
+
+/*****************************************************************************/
+/* Setup SIM hot swap (Modem interface) */
+
+typedef enum {
+ QSS_SETUP_STEP_FIRST,
+ QSS_SETUP_STEP_QUERY,
+ QSS_SETUP_STEP_ENABLE_PRIMARY_PORT,
+ QSS_SETUP_STEP_ENABLE_SECONDARY_PORT,
+ QSS_SETUP_STEP_LAST
+} QssSetupStep;
+
+typedef struct {
+ QssSetupStep step;
+ MMPortSerialAt *primary;
+ MMPortSerialAt *secondary;
+ GError *primary_error;
+ GError *secondary_error;
+} QssSetupContext;
+
+static void qss_setup_step (GTask *task);
+static void pending_csim_unlock_complete (MMBroadbandModemTelit *self);
+
+static void
+telit_qss_unsolicited_handler (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModemTelit *self)
+{
+ MMTelitQssStatus cur_qss_status;
+ MMTelitQssStatus prev_qss_status;
+
+ if (!mm_get_int_from_match_info (match_info, 1, (gint*)&cur_qss_status))
+ return;
+
+ prev_qss_status = self->priv->qss_status;
+ self->priv->qss_status = cur_qss_status;
+
+ if (self->priv->csim_lock_state >= CSIM_LOCK_STATE_LOCK_REQUESTED) {
+
+ if (prev_qss_status > QSS_STATUS_SIM_REMOVED && cur_qss_status == QSS_STATUS_SIM_REMOVED) {
+ mm_obj_dbg (self, "QSS handler: #QSS=0 after +CSIM=1: CSIM locked!");
+ self->priv->csim_lock_state = CSIM_LOCK_STATE_LOCKED;
+ }
+
+ if (prev_qss_status == QSS_STATUS_SIM_REMOVED && cur_qss_status != QSS_STATUS_SIM_REMOVED) {
+ mm_obj_dbg (self, "QSS handler: #QSS>=1 after +CSIM=0: CSIM unlocked!");
+ self->priv->csim_lock_state = CSIM_LOCK_STATE_UNLOCKED;
+
+ if (self->priv->csim_lock_timeout_id) {
+ g_source_remove (self->priv->csim_lock_timeout_id);
+ self->priv->csim_lock_timeout_id = 0;
+ }
+
+ pending_csim_unlock_complete (self);
+ }
+
+ return;
+ }
+
+ if (cur_qss_status != prev_qss_status)
+ mm_obj_dbg (self, "QSS handler: status changed %s -> %s",
+ mm_telit_qss_status_get_string (prev_qss_status),
+ mm_telit_qss_status_get_string (cur_qss_status));
+
+ if (self->priv->parse_qss == FALSE) {
+ mm_obj_dbg (self, "QSS handler: message ignored");
+ return;
+ }
+
+ if ((prev_qss_status == QSS_STATUS_SIM_REMOVED && cur_qss_status != QSS_STATUS_SIM_REMOVED) ||
+ (prev_qss_status > QSS_STATUS_SIM_REMOVED && cur_qss_status == QSS_STATUS_SIM_REMOVED)) {
+ mm_obj_msg (self, "QSS handler: SIM swap detected");
+ mm_iface_modem_process_sim_event (MM_IFACE_MODEM (self));
+ }
+}
+
+static void
+qss_setup_context_free (QssSetupContext *ctx)
+{
+ g_clear_object (&(ctx->primary));
+ g_clear_object (&(ctx->secondary));
+ g_clear_error (&(ctx->primary_error));
+ g_clear_error (&(ctx->secondary_error));
+ g_slice_free (QssSetupContext, ctx);
+}
+
+static gboolean
+modem_setup_sim_hot_swap_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+telit_qss_enable_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QssSetupContext *ctx;
+ MMPortSerialAt *port;
+ GError **error;
+ g_autoptr(GRegex) pattern = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->step == QSS_SETUP_STEP_ENABLE_PRIMARY_PORT) {
+ port = ctx->primary;
+ error = &ctx->primary_error;
+ } else if (ctx->step == QSS_SETUP_STEP_ENABLE_SECONDARY_PORT) {
+ port = ctx->secondary;
+ error = &ctx->secondary_error;
+ } else
+ g_assert_not_reached ();
+
+ if (!mm_base_modem_at_command_full_finish (self, res, error)) {
+ mm_obj_warn (self, "QSS: error enabling unsolicited on port %s: %s", mm_port_get_device (MM_PORT (port)), (*error)->message);
+ goto next_step;
+ }
+
+ pattern = g_regex_new ("#QSS:\\s*([0-3])\\r\\n", G_REGEX_RAW, 0, NULL);
+ g_assert (pattern);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ port,
+ pattern,
+ (MMPortSerialAtUnsolicitedMsgFn)telit_qss_unsolicited_handler,
+ self,
+ NULL);
+
+next_step:
+ ctx->step++;
+ qss_setup_step (task);
+}
+
+static void
+telit_qss_query_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemTelit *self;
+ GError *error = NULL;
+ const gchar *response;
+ MMTelitQssStatus qss_status;
+ QssSetupContext *ctx;
+
+ self = MM_BROADBAND_MODEM_TELIT (_self);
+ ctx = g_task_get_task_data (task);
+
+ response = mm_base_modem_at_command_finish (_self, res, &error);
+ if (error) {
+ mm_obj_warn (self, "could not get \"#QSS?\" reply: %s", error->message);
+ g_error_free (error);
+ goto next_step;
+ }
+
+ qss_status = mm_telit_parse_qss_query (response, &error);
+ if (error) {
+ mm_obj_warn (self, "QSS query parse error: %s", error->message);
+ g_error_free (error);
+ goto next_step;
+ }
+
+ mm_obj_dbg (self, "QSS: current status is '%s'", mm_telit_qss_status_get_string (qss_status));
+ self->priv->qss_status = qss_status;
+
+next_step:
+ ctx->step++;
+ qss_setup_step (task);
+}
+
+static void
+telit_qss_support_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ QssSetupContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ mm_obj_dbg (self, "#QSS command unsupported: '%s'", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->step++;
+ qss_setup_step (task);
+}
+
+static void
+qss_setup_step (GTask *task)
+{
+ QssSetupContext *ctx;
+ MMBroadbandModemTelit *self;
+
+ self = MM_BROADBAND_MODEM_TELIT (g_task_get_source_object (task));
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case QSS_SETUP_STEP_FIRST:
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "#QSS=?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback) telit_qss_support_ready,
+ task);
+ return;
+ case QSS_SETUP_STEP_QUERY:
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "#QSS?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) telit_qss_query_ready,
+ task);
+ return;
+ case QSS_SETUP_STEP_ENABLE_PRIMARY_PORT:
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ ctx->primary,
+ "#QSS=1",
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback) telit_qss_enable_ready,
+ task);
+ return;
+ case QSS_SETUP_STEP_ENABLE_SECONDARY_PORT:
+ if (ctx->secondary) {
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ ctx->secondary,
+ "#QSS=1",
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback) telit_qss_enable_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+ case QSS_SETUP_STEP_LAST:
+ /* If all enabling actions failed (either both, or only primary if
+ * there is no secondary), then we return an error */
+ if (ctx->primary_error && (ctx->secondary_error || !ctx->secondary)) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "QSS: couldn't enable unsolicited");
+ } else {
+ g_autoptr(GError) error = NULL;
+
+ if (!mm_broadband_modem_sim_hot_swap_ports_context_init (MM_BROADBAND_MODEM (self), &error))
+ mm_obj_warn (self, "failed to initialize SIM hot swap ports context: %s", error->message);
+
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+modem_setup_sim_hot_swap (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QssSetupContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (QssSetupContext);
+ ctx->step = QSS_SETUP_STEP_FIRST;
+ ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
+ ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
+
+ g_task_set_task_data (task, ctx, (GDestroyNotify) qss_setup_context_free);
+ qss_setup_step (task);
+}
+
+/*****************************************************************************/
+/* SIM hot swap cleanup (Modem interface) */
+
+static void
+modem_cleanup_sim_hot_swap (MMIfaceModem *self)
+{
+ mm_broadband_modem_sim_hot_swap_ports_context_reset (MM_BROADBAND_MODEM (self));
+}
+
+/*****************************************************************************/
+/* Load unlock retries (Modem interface)
+ *
+ * NOTE: the logic must make sure that LOAD_UNLOCK_RETRIES_STEP_UNLOCK is always
+ * run if LOAD_UNLOCK_RETRIES_STEP_LOCK has been run. Currently, the logic just
+ * runs all intermediate steps ignoring errors (i.e. not completing the
+ * operation if something fails), so the LOAD_UNLOCK_RETRIES_STEP_UNLOCK is
+ * always run.
+ */
+
+#define CSIM_LOCK_STR "+CSIM=1"
+#define CSIM_UNLOCK_STR "+CSIM=0"
+#define CSIM_QUERY_TIMEOUT 3
+
+typedef enum {
+ LOAD_UNLOCK_RETRIES_STEP_FIRST,
+ LOAD_UNLOCK_RETRIES_STEP_LOCK,
+ LOAD_UNLOCK_RETRIES_STEP_PARENT,
+ LOAD_UNLOCK_RETRIES_STEP_UNLOCK,
+ LOAD_UNLOCK_RETRIES_STEP_LAST
+} LoadUnlockRetriesStep;
+
+typedef struct {
+ MMUnlockRetries *retries;
+ LoadUnlockRetriesStep step;
+} LoadUnlockRetriesContext;
+
+static void load_unlock_retries_step (GTask *task);
+
+static void
+load_unlock_retries_context_free (LoadUnlockRetriesContext *ctx)
+{
+ if (ctx->retries)
+ g_object_unref (ctx->retries);
+ g_slice_free (LoadUnlockRetriesContext, ctx);
+}
+
+static MMUnlockRetries *
+modem_load_unlock_retries_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return (MMUnlockRetries *) g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+csim_unlock_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ MMBroadbandModemTelit *self;
+ LoadUnlockRetriesContext *ctx;
+
+ self = MM_BROADBAND_MODEM_TELIT (_self);
+ ctx = g_task_get_task_data (task);
+
+ /* Ignore errors */
+ response = mm_base_modem_at_command_finish (_self, res, &error);
+ if (!response) {
+ if (g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED)) {
+ self->priv->csim_lock_support = FEATURE_NOT_SUPPORTED;
+ }
+ mm_obj_warn (self, "couldn't unlock SIM card: %s", error->message);
+ g_error_free (error);
+ }
+
+ if (self->priv->csim_lock_support != FEATURE_NOT_SUPPORTED)
+ self->priv->csim_lock_support = FEATURE_SUPPORTED;
+
+ ctx->step++;
+ load_unlock_retries_step (task);
+}
+
+static void
+parent_load_unlock_retries_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ LoadUnlockRetriesContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!(ctx->retries = iface_modem_parent->load_unlock_retries_finish (self, res, &error))) {
+ mm_obj_warn (self, "couldn't load unlock retries with generic logic: %s", error->message);
+ g_error_free (error);
+ }
+
+ ctx->step++;
+ load_unlock_retries_step (task);
+}
+
+static void
+csim_lock_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ MMBroadbandModemTelit *self;
+ LoadUnlockRetriesContext *ctx;
+
+ self = MM_BROADBAND_MODEM_TELIT (_self);
+ ctx = g_task_get_task_data (task);
+
+ response = mm_base_modem_at_command_finish (_self, res, &error);
+ if (!response) {
+ if (g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED) ||
+ g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN)) {
+ self->priv->csim_lock_support = FEATURE_NOT_SUPPORTED;
+ mm_obj_warn (self, "couldn't lock SIM card: %s; continuing without CSIM lock", error->message);
+ g_error_free (error);
+ } else {
+ g_prefix_error (&error, "Couldn't lock SIM card: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ } else {
+ self->priv->csim_lock_state = CSIM_LOCK_STATE_LOCK_REQUESTED;
+ }
+
+ if (self->priv->csim_lock_support != FEATURE_NOT_SUPPORTED) {
+ self->priv->csim_lock_support = FEATURE_SUPPORTED;
+ }
+
+ ctx->step++;
+ load_unlock_retries_step (task);
+}
+
+static void
+handle_csim_locking (GTask *task,
+ gboolean is_lock)
+{
+ MMBroadbandModemTelit *self;
+ LoadUnlockRetriesContext *ctx;
+
+ self = MM_BROADBAND_MODEM_TELIT (g_task_get_source_object (task));
+ ctx = g_task_get_task_data (task);
+
+ switch (self->priv->csim_lock_support) {
+ case FEATURE_SUPPORT_UNKNOWN:
+ case FEATURE_SUPPORTED:
+ if (is_lock) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ CSIM_LOCK_STR,
+ CSIM_QUERY_TIMEOUT,
+ FALSE,
+ (GAsyncReadyCallback) csim_lock_ready,
+ task);
+ } else {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ CSIM_UNLOCK_STR,
+ CSIM_QUERY_TIMEOUT,
+ FALSE,
+ (GAsyncReadyCallback) csim_unlock_ready,
+ task);
+ }
+ break;
+ case FEATURE_NOT_SUPPORTED:
+ mm_obj_dbg (self, "CSIM lock not supported by this modem; skipping %s command",
+ is_lock ? "lock" : "unlock");
+ ctx->step++;
+ load_unlock_retries_step (task);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+pending_csim_unlock_complete (MMBroadbandModemTelit *self)
+{
+ LoadUnlockRetriesContext *ctx;
+
+ ctx = g_task_get_task_data (self->priv->csim_lock_task);
+
+ if (!ctx->retries) {
+ g_task_return_new_error (self->priv->csim_lock_task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not get any of the SIM unlock retries values");
+ } else {
+ g_task_return_pointer (self->priv->csim_lock_task, g_object_ref (ctx->retries), g_object_unref);
+ }
+
+ g_clear_object (&self->priv->csim_lock_task);
+}
+
+static gboolean
+csim_unlock_periodic_check (MMBroadbandModemTelit *self)
+{
+ if (self->priv->csim_lock_state != CSIM_LOCK_STATE_UNLOCKED)
+ mm_obj_warn (self, "CSIM is still locked after %d seconds; trying to continue anyway", CSIM_UNLOCK_MAX_TIMEOUT);
+
+ self->priv->csim_lock_timeout_id = 0;
+ pending_csim_unlock_complete (self);
+ g_object_unref (self);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+load_unlock_retries_step (GTask *task)
+{
+ MMBroadbandModemTelit *self;
+ LoadUnlockRetriesContext *ctx;
+
+ self = MM_BROADBAND_MODEM_TELIT (g_task_get_source_object (task));
+ ctx = g_task_get_task_data (task);
+ switch (ctx->step) {
+ case LOAD_UNLOCK_RETRIES_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+ case LOAD_UNLOCK_RETRIES_STEP_LOCK:
+ handle_csim_locking (task, TRUE);
+ break;
+ case LOAD_UNLOCK_RETRIES_STEP_PARENT:
+ iface_modem_parent->load_unlock_retries (
+ MM_IFACE_MODEM (self),
+ (GAsyncReadyCallback)parent_load_unlock_retries_ready,
+ task);
+ break;
+ case LOAD_UNLOCK_RETRIES_STEP_UNLOCK:
+ handle_csim_locking (task, FALSE);
+ break;
+ case LOAD_UNLOCK_RETRIES_STEP_LAST:
+ self->priv->csim_lock_task = task;
+ if (self->priv->csim_lock_state == CSIM_LOCK_STATE_LOCKED) {
+ mm_obj_dbg (self, "CSIM is locked, waiting for #QSS=1");
+ self->priv->csim_lock_timeout_id = g_timeout_add_seconds (CSIM_UNLOCK_MAX_TIMEOUT,
+ (GSourceFunc) csim_unlock_periodic_check,
+ g_object_ref(self));
+ } else {
+ self->priv->csim_lock_state = CSIM_LOCK_STATE_UNLOCKED;
+ pending_csim_unlock_complete (self);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+modem_load_unlock_retries (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ LoadUnlockRetriesContext *ctx;
+
+ g_assert (iface_modem_parent->load_unlock_retries);
+ g_assert (iface_modem_parent->load_unlock_retries_finish);
+
+ ctx = g_slice_new0 (LoadUnlockRetriesContext);
+ ctx->step = LOAD_UNLOCK_RETRIES_STEP_FIRST;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)load_unlock_retries_context_free);
+
+ load_unlock_retries_step (task);
+}
+
+/*****************************************************************************/
+/* Modem after power up (Modem interface) */
+
+static gboolean
+modem_after_power_up_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+modem_after_power_up (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ MMBroadbandModemTelit *modem = MM_BROADBAND_MODEM_TELIT (self);
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_obj_dbg (self, "stop ignoring #QSS");
+ modem->priv->parse_qss = TRUE;
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Modem power down (Modem interface) */
+
+static gboolean
+modem_power_down_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+telit_modem_power_down_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (mm_base_modem_at_command_finish (self, res, &error)) {
+ mm_obj_dbg (self, "sgnore #QSS unsolicited during power down/low");
+ MM_BROADBAND_MODEM_TELIT (self)->priv->parse_qss = FALSE;
+ }
+
+ if (error) {
+ mm_obj_warn (self, "failed modem power down: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_power_down (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),
+ "+CFUN=4",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback) telit_modem_power_down_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Reset (Modem interface) */
+
+static gboolean
+modem_reset_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+}
+
+static void
+modem_reset (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "AT#REBOOT",
+ 8,
+ FALSE,
+ callback,
+ user_data);
+}
+/*****************************************************************************/
+/* Load access technologies (Modem interface) */
+
+static gboolean
+load_access_technologies_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemAccessTechnology *access_technologies,
+ guint *mask,
+ GError **error)
+{
+ GVariant *result;
+
+ result = mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, error);
+ if (!result) {
+ if (error)
+ g_assert (*error);
+ return FALSE;
+ }
+
+ *access_technologies = (MMModemAccessTechnology) g_variant_get_uint32 (result);
+ *mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY;
+ return TRUE;
+}
+
+static MMBaseModemAtResponseProcessorResult
+response_processor_cops_ignore_at_errors (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
+{
+ g_autoptr(GMatchInfo) match_info = NULL;
+ g_autoptr(GRegex) r = NULL;
+ guint actval = 0;
+ guint mode = 0;
+ guint vid;
+ guint pid;
+
+ *result = NULL;
+ *result_error = NULL;
+
+ if (error) {
+ /* Ignore AT errors (ie, ERROR or CMx ERROR) */
+ if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) {
+ *result_error = g_error_copy (error);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+ }
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
+ }
+
+ vid = mm_base_modem_get_vendor_id (self);
+ pid = mm_base_modem_get_product_id (self);
+
+ if (!(vid == 0x1bc7 && (pid == 0x110a || pid == 0x110b))) {
+ /* AcT for non-LPWA modems would be checked by other command */
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
+ }
+
+ r = g_regex_new ("\\+COPS:\\s*(\\d+),(\\d+),([^,]*)(?:,(\\d+))?(?:\\r\\n)?",
+ 0,
+ 0,
+ NULL);
+ g_assert (r != NULL);
+
+ if (!g_regex_match (r, response, 0, &match_info)) {
+ g_set_error (result_error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Can't match +COPS? response: '%s'",
+ response);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+ }
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &mode)) {
+ g_set_error (result_error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse mode in +COPS? response: '%s'",
+ response);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+ }
+
+ if (mode == 2) {
+ g_set_error (result_error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Modem deregistered from the network: aborting AcT query");
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+ }
+
+ if (!mm_get_uint_from_match_info (match_info, 4, &actval)) {
+ g_set_error (result_error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse act in +COPS? response: '%s'",
+ response);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+ }
+
+ switch (actval) {
+ case 0:
+ *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_GSM);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+ case 8:
+ *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_LTE_CAT_M);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+ case 9:
+ *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_LTE_NB_IOT);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+ default:
+ break;
+ }
+
+ g_set_error (result_error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to map act in +COPS? response: '%s'",
+ response);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+}
+
+static MMBaseModemAtResponseProcessorResult
+response_processor_psnt_ignore_at_errors (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
+{
+ const gchar *psnt;
+ const gchar *mode;
+
+ *result = NULL;
+ *result_error = NULL;
+
+ if (error) {
+ /* Ignore AT errors (ie, ERROR or CMx ERROR) */
+ if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) {
+ *result_error = g_error_copy (error);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+ }
+
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
+ }
+
+ psnt = mm_strip_tag (response, "#PSNT:");
+ mode = strchr (psnt, ',');
+ if (mode) {
+ switch (atoi (++mode)) {
+ case 0:
+ *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_GPRS);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+ case 1:
+ *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_EDGE);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+ case 2:
+ *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_UMTS);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+ case 3:
+ *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_HSDPA);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+ case 4:
+ if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self)))
+ *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_LTE);
+ else
+ *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+ case 5:
+ if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) {
+ *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+ }
+ /* Fall-through since #PSNT: 5 is not supported in other than lte modems */
+ default:
+ break;
+ }
+ }
+
+ g_set_error (result_error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse #PSNT response: '%s'",
+ response);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+}
+
+static MMBaseModemAtResponseProcessorResult
+response_processor_service_ignore_at_errors (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
+{
+ const gchar *service;
+
+ *result = NULL;
+ *result_error = NULL;
+
+ if (error) {
+ /* Ignore AT errors (ie, ERROR or CMx ERROR) */
+ if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) {
+ *result_error = g_error_copy (error);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+ }
+
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
+ }
+
+ service = mm_strip_tag (response, "+SERVICE:");
+ if (service) {
+ switch (atoi (service)) {
+ case 1:
+ *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_1XRTT);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+ case 2:
+ *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_EVDO0);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+ case 3:
+ *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_EVDOA);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+ default:
+ break;
+ }
+ }
+
+ g_set_error (result_error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse +SERVICE response: '%s'",
+ response);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+}
+
+static const MMBaseModemAtCommand access_tech_commands[] = {
+ { "+COPS?", 3, FALSE, response_processor_cops_ignore_at_errors },
+ { "#PSNT?", 3, FALSE, response_processor_psnt_ignore_at_errors },
+ { "+SERVICE?", 3, FALSE, response_processor_service_ignore_at_errors },
+ { NULL }
+};
+
+static void
+load_access_technologies (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_sequence (
+ MM_BASE_MODEM (self),
+ access_tech_commands,
+ NULL, /* response_processor_context */
+ NULL, /* response_processor_context_free */
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Load supported modes (Modem interface) */
+
+static GArray *
+load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return (GArray *) g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+parent_load_supported_modes_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ GArray *all;
+ GArray *combinations;
+ GArray *filtered;
+ MMSharedTelit *shared = MM_SHARED_TELIT (self);
+
+ all = iface_modem_parent->load_supported_modes_finish (self, res, &error);
+ if (!all) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* CDMA-only modems don't support changing modes, default to parent's */
+ if (!mm_iface_modem_is_3gpp (self)) {
+ g_task_return_pointer (task, all, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Filter out those unsupported modes */
+ combinations = mm_telit_build_modes_list();
+ filtered = mm_filter_supported_modes (all, combinations, self);
+ g_array_unref (all);
+ g_array_unref (combinations);
+
+ mm_shared_telit_store_supported_modes (shared, filtered);
+ g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
+}
+
+static void
+load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ /* Run parent's loading */
+ iface_modem_parent->load_supported_modes (
+ MM_IFACE_MODEM (self),
+ (GAsyncReadyCallback)parent_load_supported_modes_ready,
+ g_task_new (self, NULL, callback, user_data));
+}
+
+/*****************************************************************************/
+/* Enabling unsolicited events (3GPP interface) */
+
+static gboolean
+modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+cind_set_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't enable custom +CIND settings: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't enable parent 3GPP unsolicited events: %s", error->message);
+ g_error_free (error);
+ }
+
+ /* Our own enable now */
+ mm_base_modem_at_command_full (
+ MM_BASE_MODEM (self),
+ mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)),
+ /* Enable +CIEV only for: signal, service, roam */
+ "AT+CIND=0,1,1,0,0,0,1,0,0",
+ 5,
+ FALSE,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)cind_set_ready,
+ task);
+}
+
+static void
+modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Chain up parent's enable */
+ iface_modem_3gpp_parent->enable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_enable_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+MMBroadbandModemTelit *
+mm_broadband_modem_telit_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id)
+{
+ return g_object_new (MM_TYPE_BROADBAND_MODEM_TELIT,
+ MM_BASE_MODEM_DEVICE, device,
+ MM_BASE_MODEM_DRIVERS, drivers,
+ MM_BASE_MODEM_PLUGIN, plugin,
+ MM_BASE_MODEM_VENDOR_ID, vendor_id,
+ MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports AT only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
+ NULL);
+}
+
+static void
+mm_broadband_modem_telit_init (MMBroadbandModemTelit *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_BROADBAND_MODEM_TELIT,
+ MMBroadbandModemTelitPrivate);
+
+ self->priv->csim_lock_support = FEATURE_SUPPORT_UNKNOWN;
+ self->priv->csim_lock_state = CSIM_LOCK_STATE_UNKNOWN;
+ self->priv->qss_status = QSS_STATUS_UNKNOWN;
+ self->priv->parse_qss = TRUE;
+}
+
+static void
+iface_modem_init (MMIfaceModem *iface)
+{
+ iface_modem_parent = g_type_interface_peek_parent (iface);
+
+ iface->set_current_bands = mm_shared_telit_modem_set_current_bands;
+ iface->set_current_bands_finish = mm_shared_telit_modem_set_current_bands_finish;
+ iface->load_current_bands = mm_shared_telit_modem_load_current_bands;
+ iface->load_current_bands_finish = mm_shared_telit_modem_load_current_bands_finish;
+ iface->load_revision = mm_shared_telit_modem_load_revision;
+ iface->load_revision_finish = mm_shared_telit_modem_load_revision_finish;
+ iface->load_supported_bands = mm_shared_telit_modem_load_supported_bands;
+ iface->load_supported_bands_finish = mm_shared_telit_modem_load_supported_bands_finish;
+ iface->load_unlock_retries_finish = modem_load_unlock_retries_finish;
+ iface->load_unlock_retries = modem_load_unlock_retries;
+ iface->reset = modem_reset;
+ iface->reset_finish = modem_reset_finish;
+ iface->modem_after_power_up = modem_after_power_up;
+ iface->modem_after_power_up_finish = modem_after_power_up_finish;
+ iface->modem_power_down = modem_power_down;
+ iface->modem_power_down_finish = modem_power_down_finish;
+ iface->load_access_technologies = load_access_technologies;
+ iface->load_access_technologies_finish = load_access_technologies_finish;
+ iface->load_supported_modes = load_supported_modes;
+ iface->load_supported_modes_finish = load_supported_modes_finish;
+ iface->load_current_modes = mm_shared_telit_load_current_modes;
+ iface->load_current_modes_finish = mm_shared_telit_load_current_modes_finish;
+ iface->set_current_modes = mm_shared_telit_set_current_modes;
+ iface->set_current_modes_finish = mm_shared_telit_set_current_modes_finish;
+ iface->setup_sim_hot_swap = modem_setup_sim_hot_swap;
+ iface->setup_sim_hot_swap_finish = modem_setup_sim_hot_swap_finish;
+ iface->cleanup_sim_hot_swap = modem_cleanup_sim_hot_swap;
+}
+
+static void
+iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
+{
+ iface_modem_3gpp_parent = g_type_interface_peek_parent (iface);
+
+ iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish;
+}
+
+static void
+shared_telit_init (MMSharedTelit *iface)
+{
+}
+
+static void
+iface_modem_location_init (MMIfaceModemLocation *iface)
+{
+ iface_modem_location_parent = g_type_interface_peek_parent (iface);
+
+ iface->load_capabilities = location_load_capabilities;
+ iface->load_capabilities_finish = location_load_capabilities_finish;
+ iface->enable_location_gathering = enable_location_gathering;
+ iface->enable_location_gathering_finish = enable_location_gathering_finish;
+ iface->disable_location_gathering = disable_location_gathering;
+ iface->disable_location_gathering_finish = disable_location_gathering_finish;
+}
+
+static void
+mm_broadband_modem_telit_class_init (MMBroadbandModemTelitClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMBroadbandModemTelitPrivate));
+ broadband_modem_class->setup_ports = setup_ports;
+}
diff --git a/src/plugins/telit/mm-broadband-modem-telit.h b/src/plugins/telit/mm-broadband-modem-telit.h
new file mode 100644
index 00000000..f68465e7
--- /dev/null
+++ b/src/plugins/telit/mm-broadband-modem-telit.h
@@ -0,0 +1,51 @@
+/* -*- 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) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2013 Red Hat, Inc.
+ * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#ifndef MM_BROADBAND_MODEM_TELIT_H
+#define MM_BROADBAND_MODEM_TELIT_H
+
+#include "mm-broadband-modem.h"
+
+#define MM_TYPE_BROADBAND_MODEM_TELIT (mm_broadband_modem_telit_get_type ())
+#define MM_BROADBAND_MODEM_TELIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_TELIT, MMBroadbandModemTelit))
+#define MM_BROADBAND_MODEM_TELIT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_TELIT, MMBroadbandModemTelitClass))
+#define MM_IS_BROADBAND_MODEM_TELIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_TELIT))
+#define MM_IS_BROADBAND_MODEM_TELIT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_TELIT))
+#define MM_BROADBAND_MODEM_TELIT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_TELIT, MMBroadbandModemTelitClass))
+
+typedef struct _MMBroadbandModemTelit MMBroadbandModemTelit;
+typedef struct _MMBroadbandModemTelitClass MMBroadbandModemTelitClass;
+typedef struct _MMBroadbandModemTelitPrivate MMBroadbandModemTelitPrivate;
+
+struct _MMBroadbandModemTelit {
+ MMBroadbandModem parent;
+ MMBroadbandModemTelitPrivate *priv;
+};
+
+struct _MMBroadbandModemTelitClass{
+ MMBroadbandModemClass parent;
+};
+
+GType mm_broadband_modem_telit_get_type (void);
+
+MMBroadbandModemTelit *mm_broadband_modem_telit_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id);
+
+#endif /* MM_BROADBAND_MODEM_TELIT_H */
diff --git a/src/plugins/telit/mm-common-telit.c b/src/plugins/telit/mm-common-telit.c
new file mode 100644
index 00000000..911c605b
--- /dev/null
+++ b/src/plugins/telit/mm-common-telit.c
@@ -0,0 +1,373 @@
+/* -*- 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) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <string.h>
+
+#include "mm-common-telit.h"
+#include "mm-log-object.h"
+#include "mm-serial-parsers.h"
+
+/*****************************************************************************/
+
+#define TAG_GETPORTCFG_SUPPORTED "getportcfg-supported"
+
+#define TAG_TELIT_MODEM_PORT "ID_MM_TELIT_PORT_TYPE_MODEM"
+#define TAG_TELIT_AUX_PORT "ID_MM_TELIT_PORT_TYPE_AUX"
+#define TAG_TELIT_NMEA_PORT "ID_MM_TELIT_PORT_TYPE_NMEA"
+
+#define TELIT_GE910_FAMILY_PID 0x0022
+
+/* The following number of retries of the port responsiveness
+ * check allows having up to 30 seconds of wait, that should
+ * be fine for most of the modems */
+#define TELIT_PORT_CHECK_RETRIES 6
+
+gboolean
+telit_grab_port (MMPlugin *self,
+ MMBaseModem *modem,
+ MMPortProbe *probe,
+ GError **error)
+{
+ MMKernelDevice *port;
+ MMDevice *device;
+ MMPortType ptype;
+ MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
+ const gchar *subsys;
+
+ port = mm_port_probe_peek_port (probe);
+ ptype = mm_port_probe_get_port_type (probe);
+ device = mm_port_probe_peek_device (probe);
+ subsys = mm_port_probe_get_port_subsys (probe);
+
+ /* Just skip custom port identification for subsys different than tty */
+ if (!g_str_equal (subsys, "tty"))
+ goto out;
+
+ /* AT#PORTCFG (if supported) can be used for identifying the port layout */
+ if (g_object_get_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED) != NULL) {
+ guint usbif;
+
+ usbif = (guint) mm_kernel_device_get_interface_number (port);
+ if (usbif == GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT))) {
+ mm_obj_dbg (self, "AT port '%s/%s' flagged as primary",
+ mm_port_probe_get_port_subsys (probe),
+ mm_port_probe_get_port_name (probe));
+ pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
+ } else if (usbif == GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (device), TAG_TELIT_AUX_PORT))) {
+ mm_obj_dbg (self, "AT port '%s/%s' flagged as secondary",
+ mm_port_probe_get_port_subsys (probe),
+ mm_port_probe_get_port_name (probe));
+ pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
+ } else if (usbif == GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (device), TAG_TELIT_NMEA_PORT))) {
+ mm_obj_dbg (self, "port '%s/%s' flagged as NMEA",
+ mm_port_probe_get_port_subsys (probe),
+ mm_port_probe_get_port_name (probe));
+ ptype = MM_PORT_TYPE_GPS;
+ } else
+ ptype = MM_PORT_TYPE_IGNORED;
+ }
+
+out:
+ return mm_base_modem_grab_port (modem,
+ port,
+ ptype,
+ pflags,
+ error);
+}
+
+/*****************************************************************************/
+/* Custom init */
+
+typedef struct {
+ MMPortSerialAt *port;
+ gboolean getportcfg_done;
+ guint getportcfg_retries;
+ guint port_responsive_retries;
+} TelitCustomInitContext;
+
+gboolean
+telit_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void telit_custom_init_step (GTask *task);
+
+static gboolean
+cache_port_mode (MMPortProbe *probe,
+ MMDevice *device,
+ const gchar *reply)
+{
+ g_autoptr(GRegex) r = NULL;
+ g_autoptr(GMatchInfo) match_info = NULL;
+ GRegexCompileFlags flags = G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW;
+ GError *error = NULL;
+ gboolean ret = FALSE;
+ guint portcfg_current;
+
+ /* #PORTCFG: <requested>,<active> */
+ r = g_regex_new ("#PORTCFG:\\s*(\\d+),(\\d+)", flags, 0, NULL);
+ g_assert (r != NULL);
+
+ if (!g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, &error))
+ goto out;
+
+ if (!mm_get_uint_from_match_info (match_info, 2, &portcfg_current)) {
+ mm_obj_dbg (probe, "unrecognized #PORTCFG <active> value");
+ goto out;
+ }
+
+ /* Reference for port configurations:
+ * HE910/UE910/UL865 Families Ports Arrangements User Guide
+ * GE910 Family Ports Arrangements User Guide
+ */
+ switch (portcfg_current) {
+ case 0:
+ case 1:
+ case 4:
+ case 5:
+ case 7:
+ case 9:
+ case 10:
+ case 11:
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT, GUINT_TO_POINTER (0x00));
+ if (mm_device_get_product (device) == TELIT_GE910_FAMILY_PID)
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, GUINT_TO_POINTER (0x02));
+ else
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, GUINT_TO_POINTER (0x06));
+ break;
+ case 2:
+ case 3:
+ case 6:
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT, GUINT_TO_POINTER (0x00));
+ break;
+ case 8:
+ case 12:
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT, GUINT_TO_POINTER (0x00));
+ if (mm_device_get_product (device) == TELIT_GE910_FAMILY_PID) {
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, GUINT_TO_POINTER (0x02));
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_NMEA_PORT, GUINT_TO_POINTER (0x04));
+ } else {
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, GUINT_TO_POINTER (0x06));
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_NMEA_PORT, GUINT_TO_POINTER (0x0a));
+ }
+ break;
+ default:
+ /* portcfg value not supported */
+ goto out;
+ }
+ ret = TRUE;
+
+out:
+ if (error) {
+ mm_obj_dbg (probe, "error while matching #PORTCFG: %s", error->message);
+ g_error_free (error);
+ }
+ return ret;
+}
+
+static void
+getportcfg_ready (MMPortSerialAt *port,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ MMPortProbe *probe;
+ TelitCustomInitContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ probe = g_task_get_source_object (task);
+
+ response = mm_port_serial_at_command_finish (port, res, &error);
+ if (error) {
+ mm_obj_dbg (probe, "couldn't get telit port mode: '%s'", error->message);
+
+ /* If ERROR or COMMAND NOT SUPPORT occur then do not retry the
+ * command.
+ */
+ if (g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN))
+ ctx->getportcfg_done = TRUE;
+ } else {
+ MMDevice *device;
+
+ device = mm_port_probe_peek_device (probe);
+
+ /* Results are cached in the parent device object */
+ if (g_object_get_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED) == NULL) {
+ mm_obj_dbg (probe, "retrieving telit port mode layout");
+ if (cache_port_mode (probe, device, response)) {
+ g_object_set_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED, GUINT_TO_POINTER (TRUE));
+ ctx->getportcfg_done = TRUE;
+ }
+ }
+
+ /* Port answered to #PORTCFG, so mark it as being AT already */
+ mm_port_probe_set_result_at (probe, TRUE);
+ }
+
+ if (error)
+ g_error_free (error);
+
+ telit_custom_init_step (task);
+}
+
+static void
+telit_custom_init_context_free (TelitCustomInitContext *ctx)
+{
+ g_object_unref (ctx->port);
+ g_slice_free (TelitCustomInitContext, ctx);
+}
+
+static void
+telit_custom_init_step (GTask *task)
+{
+ MMKernelDevice *port;
+ MMPortProbe *probe;
+ TelitCustomInitContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ probe = g_task_get_source_object (task);
+
+ /* If cancelled, end */
+ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) {
+ mm_obj_dbg (probe, "no need to keep on running custom init");
+ goto out;
+ }
+
+ /* Try to get a port configuration from the modem: usb interface 00
+ * is always linked to an AT port
+ */
+ port = mm_port_probe_peek_port (probe);
+ if (!ctx->getportcfg_done && mm_kernel_device_get_interface_number (port) == 0) {
+ if (ctx->getportcfg_retries == 0)
+ goto out;
+ ctx->getportcfg_retries--;
+
+ mm_port_serial_at_command (
+ ctx->port,
+ "AT#PORTCFG?",
+ 2,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)getportcfg_ready,
+ task);
+ return;
+ }
+
+out:
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void at_ready (MMPortSerialAt *port,
+ GAsyncResult *res,
+ GTask *task);
+
+static void
+wait_for_ready (GTask *task)
+{
+ TelitCustomInitContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->port_responsive_retries == 0) {
+ telit_custom_init_step (task);
+ return;
+ }
+ ctx->port_responsive_retries--;
+
+ mm_port_serial_at_command (
+ ctx->port,
+ "AT",
+ 5,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)at_ready,
+ task);
+}
+
+static void
+at_ready (MMPortSerialAt *port,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMPortProbe *probe;
+ g_autoptr(GError) error = NULL;
+
+ probe = g_task_get_source_object (task);
+
+ mm_port_serial_at_command_finish (port, res, &error);
+ if (error) {
+ /* On a timeout or send error, wait */
+ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT) ||
+ g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED)) {
+ wait_for_ready (task);
+ return;
+ }
+ /* On an unknown error, make it fatal */
+ if (!mm_serial_parser_v1_is_known_error (error)) {
+ mm_obj_warn (probe, "custom port initialization logic failed: %s", error->message);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+ }
+
+ /* When successful mark the port as AT and continue checking #PORTCFG */
+ mm_obj_dbg (probe, "port is AT");
+ mm_port_probe_set_result_at (probe, TRUE);
+ telit_custom_init_step (task);
+}
+
+void
+telit_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ TelitCustomInitContext *ctx;
+ GTask *task;
+ gboolean wait_needed;
+
+ ctx = g_slice_new (TelitCustomInitContext);
+ ctx->port = g_object_ref (port);
+ ctx->getportcfg_done = FALSE;
+ ctx->getportcfg_retries = 3;
+ ctx->port_responsive_retries = TELIT_PORT_CHECK_RETRIES;
+ task = g_task_new (probe, cancellable, callback, user_data);
+ g_task_set_check_cancellable (task, FALSE);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)telit_custom_init_context_free);
+
+ /* Some Telit modems require an initial delay for the ports to be responsive
+ * If no explicit tag is present, the modem does not need this step
+ * and can directly look for #PORTCFG support */
+ wait_needed = mm_kernel_device_get_global_property_as_boolean (mm_port_probe_peek_port (probe),
+ "ID_MM_TELIT_PORT_DELAY");
+ if (wait_needed) {
+ mm_obj_dbg (probe, "Start polling for port responsiveness");
+ wait_for_ready (task);
+ return;
+ }
+
+ telit_custom_init_step (task);
+}
diff --git a/src/plugins/telit/mm-common-telit.h b/src/plugins/telit/mm-common-telit.h
new file mode 100644
index 00000000..0c5dbe7a
--- /dev/null
+++ b/src/plugins/telit/mm-common-telit.h
@@ -0,0 +1,40 @@
+/* -*- 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) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_COMMON_TELIT_H
+#define MM_COMMON_TELIT_H
+
+#include "glib.h"
+#include "mm-plugin.h"
+
+gboolean
+telit_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error);
+
+void
+telit_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean
+telit_grab_port (MMPlugin *self,
+ MMBaseModem *modem,
+ MMPortProbe *probe,
+ GError **error);
+
+#endif /* MM_COMMON_TELIT_H */
diff --git a/src/plugins/telit/mm-modem-helpers-telit.c b/src/plugins/telit/mm-modem-helpers-telit.c
new file mode 100644
index 00000000..c0df8093
--- /dev/null
+++ b/src/plugins/telit/mm-modem-helpers-telit.c
@@ -0,0 +1,967 @@
+/* -*- 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) 2015-2019 Telit.
+ * Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MMCLI
+#include <libmm-glib.h>
+
+#include "mm-log-object.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-telit.h"
+
+/*****************************************************************************/
+/* AT#BND 2G values */
+
+#define MM_MODEM_BAND_TELIT_2G_FIRST MM_MODEM_BAND_EGSM
+#define MM_MODEM_BAND_TELIT_2G_LAST MM_MODEM_BAND_G850
+
+#define B2G_FLAG(band) (1 << (band - MM_MODEM_BAND_TELIT_2G_FIRST))
+
+/* Index of the array is the telit 2G band value [0-5]
+ * The bitmask value here is built from the 2G MMModemBand values right away. */
+static const guint32 telit_2g_to_mm_band_mask[] = {
+ [0] = B2G_FLAG (MM_MODEM_BAND_EGSM) + B2G_FLAG (MM_MODEM_BAND_DCS),
+ [1] = B2G_FLAG (MM_MODEM_BAND_EGSM) + B2G_FLAG (MM_MODEM_BAND_PCS),
+ [2] = B2G_FLAG (MM_MODEM_BAND_DCS) + B2G_FLAG (MM_MODEM_BAND_G850),
+ [3] = B2G_FLAG (MM_MODEM_BAND_PCS) + B2G_FLAG (MM_MODEM_BAND_G850),
+ [4] = B2G_FLAG (MM_MODEM_BAND_EGSM) + B2G_FLAG (MM_MODEM_BAND_DCS) + B2G_FLAG (MM_MODEM_BAND_PCS),
+ [5] = B2G_FLAG (MM_MODEM_BAND_EGSM) + B2G_FLAG (MM_MODEM_BAND_DCS) + B2G_FLAG (MM_MODEM_BAND_PCS) + B2G_FLAG (MM_MODEM_BAND_G850),
+};
+
+/*****************************************************************************/
+/* AT#BND 3G values */
+
+/* NOTE: UTRAN_1 to UTRAN_9 enum values are NOT IN ORDER!
+ * E.g. numerically UTRAN_7 is after UTRAN_9...
+ *
+ * This array allows us to iterate them in a way which is a bit
+ * more friendly.
+ */
+static const guint band_utran_index[] = {
+ [MM_MODEM_BAND_UTRAN_1] = 1,
+ [MM_MODEM_BAND_UTRAN_2] = 2,
+ [MM_MODEM_BAND_UTRAN_3] = 3,
+ [MM_MODEM_BAND_UTRAN_4] = 4,
+ [MM_MODEM_BAND_UTRAN_5] = 5,
+ [MM_MODEM_BAND_UTRAN_6] = 6,
+ [MM_MODEM_BAND_UTRAN_7] = 7,
+ [MM_MODEM_BAND_UTRAN_8] = 8,
+ [MM_MODEM_BAND_UTRAN_9] = 9,
+ [MM_MODEM_BAND_UTRAN_10] = 10,
+ [MM_MODEM_BAND_UTRAN_11] = 11,
+ [MM_MODEM_BAND_UTRAN_12] = 12,
+ [MM_MODEM_BAND_UTRAN_13] = 13,
+ [MM_MODEM_BAND_UTRAN_14] = 14,
+ [MM_MODEM_BAND_UTRAN_19] = 19,
+ [MM_MODEM_BAND_UTRAN_20] = 20,
+ [MM_MODEM_BAND_UTRAN_21] = 21,
+ [MM_MODEM_BAND_UTRAN_22] = 22,
+ [MM_MODEM_BAND_UTRAN_25] = 25,
+ [MM_MODEM_BAND_UTRAN_26] = 26,
+ [MM_MODEM_BAND_UTRAN_32] = 32,
+};
+
+#define MM_MODEM_BAND_TELIT_3G_FIRST MM_MODEM_BAND_UTRAN_1
+#define MM_MODEM_BAND_TELIT_3G_LAST MM_MODEM_BAND_UTRAN_19
+
+#define B3G_NUM(band) band_utran_index[band]
+#define B3G_FLAG(band) ((B3G_NUM (band) > 0) ? (1LL << (B3G_NUM (band) - B3G_NUM (MM_MODEM_BAND_TELIT_3G_FIRST))) : 0)
+
+/* Index of the arrays is the telit 3G band value.
+ * The bitmask value here is built from the 3G MMModemBand values right away.
+ *
+ * We have 2 different sets of bands that are different for values >=12, because
+ * the LM940/960 models support a different set of 3G bands.
+ */
+
+#define TELIT_3G_TO_MM_BAND_MASK_DEFAULT_N_ELEMENTS 27
+static guint64 telit_3g_to_mm_band_mask_default[TELIT_3G_TO_MM_BAND_MASK_DEFAULT_N_ELEMENTS];
+
+#define TELIT_3G_TO_MM_BAND_MASK_ALTERNATE_N_ELEMENTS 20
+static guint64 telit_3g_to_mm_band_mask_alternate[TELIT_3G_TO_MM_BAND_MASK_ALTERNATE_N_ELEMENTS];
+
+static void
+initialize_telit_3g_to_mm_band_masks (void)
+{
+ static gboolean initialized = FALSE;
+
+ /* We need to initialize the arrays in runtime because gcc < 8 doesn't like initializing
+ * with operations that are using the band_utran_index constant array elements */
+
+ if (initialized)
+ return;
+
+ telit_3g_to_mm_band_mask_default[0] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1);
+ telit_3g_to_mm_band_mask_default[1] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2);
+ telit_3g_to_mm_band_mask_default[2] = B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_default[3] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_default[4] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_default[5] = B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_default[6] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_default[7] = B3G_FLAG (MM_MODEM_BAND_UTRAN_4);
+ telit_3g_to_mm_band_mask_default[8] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_default[9] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_default[10] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_default[11] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_default[12] = B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
+ telit_3g_to_mm_band_mask_default[13] = B3G_FLAG (MM_MODEM_BAND_UTRAN_3);
+ telit_3g_to_mm_band_mask_default[14] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
+ telit_3g_to_mm_band_mask_default[15] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_default[16] = B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_default[17] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
+ telit_3g_to_mm_band_mask_default[18] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_default[19] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
+ telit_3g_to_mm_band_mask_default[20] = B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
+ telit_3g_to_mm_band_mask_default[21] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
+ telit_3g_to_mm_band_mask_default[22] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_default[23] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3);
+ telit_3g_to_mm_band_mask_default[24] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_default[25] = B3G_FLAG (MM_MODEM_BAND_UTRAN_19);
+ telit_3g_to_mm_band_mask_default[26] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_19);
+
+ /* Initialize alternate 3G band set */
+ telit_3g_to_mm_band_mask_alternate[0] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1);
+ telit_3g_to_mm_band_mask_alternate[1] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2);
+ telit_3g_to_mm_band_mask_alternate[2] = B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_alternate[3] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_alternate[4] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_alternate[5] = B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_alternate[6] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_alternate[7] = B3G_FLAG (MM_MODEM_BAND_UTRAN_4);
+ telit_3g_to_mm_band_mask_alternate[8] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_alternate[9] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_alternate[10] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_alternate[11] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_alternate[12] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_alternate[13] = B3G_FLAG (MM_MODEM_BAND_UTRAN_3);
+ telit_3g_to_mm_band_mask_alternate[14] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_alternate[15] = B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_alternate[16] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_alternate[17] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_alternate[18] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_9) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_19);
+ telit_3g_to_mm_band_mask_alternate[19] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_9) + B3G_FLAG (MM_MODEM_BAND_UTRAN_19);
+}
+
+/*****************************************************************************/
+/* AT#BND 4G values
+ *
+ * The Telit-specific value for 4G bands is a bitmask of the band values, given
+ * in hexadecimal or decimal format.
+ */
+
+#define MM_MODEM_BAND_TELIT_4G_FIRST MM_MODEM_BAND_EUTRAN_1
+#define MM_MODEM_BAND_TELIT_4G_LAST MM_MODEM_BAND_EUTRAN_64
+
+#define B4G_FLAG(band) (((guint64) 1) << (band - MM_MODEM_BAND_TELIT_4G_FIRST))
+
+#define MM_MODEM_BAND_TELIT_EXT_4G_FIRST MM_MODEM_BAND_EUTRAN_65
+#define MM_MODEM_BAND_TELIT_EXT_4G_LAST MM_MODEM_BAND_EUTRAN_85
+
+#define B4G_FLAG_EXT(band) (((guint64) 1) << (band - MM_MODEM_BAND_TELIT_EXT_4G_FIRST))
+
+/*****************************************************************************/
+/* Set current bands helpers */
+
+gchar *
+mm_telit_build_bnd_request (GArray *bands_array,
+ MMTelitBNDParseConfig *config,
+ GError **error)
+{
+ guint32 mask2g = 0;
+ guint64 mask3g = 0;
+ guint64 mask4g = 0;
+ guint64 mask4gext = 0;
+ guint i;
+ gint flag2g = -1;
+ gint64 flag3g = -1;
+ gint64 flag4g = -1;
+ gchar *cmd;
+ gboolean modem_is_2g = config->modem_is_2g;
+ gboolean modem_is_3g = config->modem_is_3g;
+ gboolean modem_is_4g = config->modem_is_4g;
+
+ for (i = 0; i < bands_array->len; i++) {
+ MMModemBand band;
+
+ band = g_array_index (bands_array, MMModemBand, i);
+
+ /* Convert 2G bands into a bitmask, to match against telit_2g_to_mm_band_mask. */
+ if (modem_is_2g && mm_common_band_is_gsm (band) &&
+ (band >= MM_MODEM_BAND_TELIT_2G_FIRST) && (band <= MM_MODEM_BAND_TELIT_2G_LAST))
+ mask2g += B2G_FLAG (band);
+
+ /* Convert 3G bands into a bitmask, to match against telit_3g_to_mm_band_mask. We use
+ * a 64-bit explicit bitmask so that all values fit correctly. */
+ if (modem_is_3g && mm_common_band_is_utran (band) &&
+ (B3G_NUM (band) >= B3G_NUM (MM_MODEM_BAND_TELIT_3G_FIRST)) && (B3G_NUM (band) <= B3G_NUM (MM_MODEM_BAND_TELIT_3G_LAST)))
+ mask3g += B3G_FLAG (band);
+
+ /* Convert 4G bands into a bitmask. We use a 64bit explicit bitmask so that
+ * all values fit correctly. */
+ if (modem_is_4g && mm_common_band_is_eutran (band)) {
+ if (band >= MM_MODEM_BAND_TELIT_4G_FIRST && band <= MM_MODEM_BAND_TELIT_4G_LAST)
+ mask4g += B4G_FLAG (band);
+ else if (band >= MM_MODEM_BAND_TELIT_EXT_4G_FIRST && band <= MM_MODEM_BAND_TELIT_EXT_4G_LAST)
+ mask4gext += B4G_FLAG_EXT (band);
+ }
+ }
+
+ /* Get 2G-specific telit value */
+ if (mask2g) {
+ for (i = 0; i < G_N_ELEMENTS (telit_2g_to_mm_band_mask); i++) {
+ if (mask2g == telit_2g_to_mm_band_mask[i]) {
+ flag2g = i;
+ break;
+ }
+ }
+ if (flag2g == -1) {
+ gchar *bands_str;
+
+ bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)(bands_array->data), bands_array->len);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't find matching 2G bands Telit value for band combination: '%s'", bands_str);
+ g_free (bands_str);
+ return NULL;
+ }
+ }
+
+ /* Get 3G-specific telit value */
+ if (mask3g) {
+ const guint64 *telit_3g_to_mm_band_mask;
+ guint telit_3g_to_mm_band_mask_n_elements;
+
+ initialize_telit_3g_to_mm_band_masks ();
+
+ /* Select correct 3G band mask */
+ if (config->modem_alternate_3g_bands) {
+ telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_alternate;
+ telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_alternate);
+ } else {
+ telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_default;
+ telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_default);
+ }
+ for (i = 0; i < telit_3g_to_mm_band_mask_n_elements; i++) {
+ if (mask3g == telit_3g_to_mm_band_mask[i]) {
+ flag3g = i;
+ break;
+ }
+ }
+ if (flag3g == -1) {
+ gchar *bands_str;
+
+ bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)(bands_array->data), bands_array->len);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't find matching 3G bands Telit value for band combination: '%s'", bands_str);
+ g_free (bands_str);
+ return NULL;
+ }
+ }
+
+ /* Get 4G-specific telit band bitmask */
+ flag4g = (mask4g != 0) ? ((gint64)mask4g) : -1;
+
+ /* If the modem supports a given access tech, we must always give band settings
+ * for the specific tech */
+ if (modem_is_2g && flag2g == -1) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "None or invalid 2G bands combination in the provided list");
+ return NULL;
+ }
+ if (modem_is_3g && flag3g == -1) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "None or invalid 3G bands combination in the provided list");
+ return NULL;
+ }
+ if (modem_is_4g && mask4g == 0 && mask4gext == 0) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "None or invalid 4G bands combination in the provided list");
+ return NULL;
+ }
+
+ if (modem_is_2g && !modem_is_3g && !modem_is_4g)
+ cmd = g_strdup_printf ("#BND=%d", flag2g);
+ else if (!modem_is_2g && modem_is_3g && !modem_is_4g)
+ cmd = g_strdup_printf ("#BND=0,%" G_GINT64_FORMAT, flag3g);
+ else if (modem_is_2g && modem_is_3g && !modem_is_4g)
+ cmd = g_strdup_printf ("#BND=%d,%" G_GINT64_FORMAT, flag2g, flag3g);
+ else if (!modem_is_2g && !modem_is_3g && modem_is_4g) {
+ if (!config->modem_ext_4g_bands)
+ cmd = g_strdup_printf ("#BND=0,0,%" G_GINT64_FORMAT, flag4g);
+ else
+ cmd = g_strdup_printf ("#BND=0,0,%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", mask4g, mask4gext);
+ } else if (!modem_is_2g && modem_is_3g && modem_is_4g) {
+ if (!config->modem_ext_4g_bands)
+ cmd = g_strdup_printf ("#BND=0,%" G_GINT64_FORMAT ",%" G_GINT64_FORMAT, flag3g, flag4g);
+ else
+ cmd = g_strdup_printf ("#BND=0,%" G_GINT64_FORMAT ",%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", flag3g, mask4g, mask4gext);
+ } else if (modem_is_2g && !modem_is_3g && modem_is_4g) {
+ if (!config->modem_ext_4g_bands)
+ cmd = g_strdup_printf ("#BND=%d,0,%" G_GINT64_FORMAT, flag2g, flag4g);
+ else
+ cmd = g_strdup_printf ("#BND=%d,0,%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", flag2g, mask4g, mask4gext);
+ } else if (modem_is_2g && modem_is_3g && modem_is_4g) {
+ if (!config->modem_ext_4g_bands)
+ cmd = g_strdup_printf ("#BND=%d,%" G_GINT64_FORMAT ",%" G_GINT64_FORMAT, flag2g, flag3g, flag4g);
+ else
+ cmd = g_strdup_printf ("#BND=%d,%" G_GINT64_FORMAT ",%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", flag2g, flag3g, mask4g, mask4gext);
+ } else
+ g_assert_not_reached ();
+
+ return cmd;
+}
+
+/*****************************************************************************/
+/* #BND response parser
+ *
+ * AT#BND=?
+ * #BND: <2G band flags range>,<3G band flags range>[, <4G band flags range>]
+ *
+ * where "band flags" is a list of numbers defining the supported bands.
+ * Note that the one Telit band flag may represent more than one MM band.
+ *
+ * e.g.
+ *
+ * #BND: (0-2),(3,4)
+ *
+ * (0,2) = 2G band flag 0 is EGSM + DCS
+ * = 2G band flag 1 is EGSM + PCS
+ * = 2G band flag 2 is DCS + G850
+ * (3,4) = 3G band flag 3 is U2100 + U1900 + U850
+ * = 3G band flag 4 is U1900 + U850
+ *
+ * Modems that supports 4G bands, return a range value(X-Y) where
+ * X: represent the lower supported band, such as X = 2^(B-1), being B = B1, B2,..., B32
+ * Y: is a 32 bit number resulting from a mask of all the supported bands:
+ * 1 - B1
+ * 2 - B2
+ * 4 - B3
+ * 8 - B4
+ * ...
+ * i - B(2exp(i-1))
+ * ...
+ * 2147483648 - B32
+ *
+ * e.g.
+ * (2-4106)
+ * 2 = 2^1 --> lower supported band B2
+ * 4106 = 2^1 + 2^3 + 2^12 --> the supported bands are B2, B4, B13
+ *
+ *
+ * AT#BND?
+ * #BND: <2G band flags>,<3G band flags>[, <4G band flags>]
+ *
+ * where "band flags" is a number defining the current bands.
+ * Note that the one Telit band flag may represent more than one MM band.
+ *
+ * e.g.
+ *
+ * #BND: 0,4
+ *
+ * 0 = 2G band flag 0 is EGSM + DCS
+ * 4 = 3G band flag 4 is U1900 + U850
+ *
+ * ----------------
+ *
+ * For modems such as LN920 the #BND configuration/response for LTE bands is different
+ * from what is explained above:
+ *
+ * AT#BND=<GSM_band>[,<UMTS_band>[,<LTE_band>[,<LTE_band_ext>]]]
+ *
+ * <LTE_band>: hex: Indicates the LTE supported bands expressed as the sum of Band number (1+2+8 ...) calculated as shown in the table (mask of 64 bits):
+ *
+ * Band number(Hex) Band i
+ * 0 disable
+ * 1 B1
+ * 2 B2
+ * 4 B3
+ * 8 B4
+ * ...
+ * ...
+ * 80000000 B32
+ * ...
+ * ...
+ * 800000000000 B48
+ *
+ * It can take value, 0 - 87A03B0F38DF: range of the sum of Band number (1+2+4 ...)
+ *
+ * <LTE_band_ext>: hex: Indicates the LTE supported bands from B65 expressed as the sum of Band number (1+2+8 ...) calculated as shown in the table (mask of 64 bits):
+ *
+ * Band number(Hex) Band i
+ * 0 disable
+ * 2 B66
+ * 40 B71
+ *
+ * It can take value, 0 - 42: range of the sum of Band number (2+40)
+ *
+ * Note: LTE_band and LTE_band_ext cannot be 0 at the same time
+ *
+ * Example output:
+ *
+ * AT#BND=?
+ * #BND: (0),(0-11,17,18),(87A03B0F38DF),(42)
+ *
+ * AT#BND?
+ * #BND: 0,18,87A03B0F38DF,42
+ *
+ */
+
+static gboolean
+telit_get_2g_mm_bands (GMatchInfo *match_info,
+ gpointer log_object,
+ GArray **bands,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ GArray *values = NULL;
+ gchar *match_str = NULL;
+ guint i;
+
+ match_str = g_match_info_fetch_named (match_info, "Bands2G");
+ if (!match_str || match_str[0] == '\0') {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not find 2G band values from response");
+ goto out;
+ }
+
+ values = mm_parse_uint_list (match_str, &inner_error);
+ if (!values)
+ goto out;
+
+ for (i = 0; i < values->len; i++) {
+ guint value;
+
+ value = g_array_index (values, guint, i);
+ if (value < G_N_ELEMENTS (telit_2g_to_mm_band_mask)) {
+ guint j;
+
+ for (j = MM_MODEM_BAND_TELIT_2G_FIRST; j <= MM_MODEM_BAND_TELIT_2G_LAST; j++) {
+ if ((telit_2g_to_mm_band_mask[value] & B2G_FLAG (j)) && !mm_common_bands_garray_lookup (*bands, j))
+ *bands = g_array_append_val (*bands, j);
+ }
+ } else
+ mm_obj_dbg (log_object, "unhandled telit 2G band value configuration: %u", value);
+ }
+
+out:
+ g_free (match_str);
+ g_clear_pointer (&values, g_array_unref);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+telit_get_3g_mm_bands (GMatchInfo *match_info,
+ gpointer log_object,
+ gboolean modem_alternate_3g_bands,
+ GArray **bands,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ GArray *values = NULL;
+ gchar *match_str = NULL;
+ guint i;
+ const guint64 *telit_3g_to_mm_band_mask;
+ guint telit_3g_to_mm_band_mask_n_elements;
+
+ initialize_telit_3g_to_mm_band_masks ();
+
+ /* Select correct 3G band mask */
+ if (modem_alternate_3g_bands) {
+ telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_alternate;
+ telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_alternate);
+ } else {
+ telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_default;
+ telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_default);
+ }
+
+ match_str = g_match_info_fetch_named (match_info, "Bands3G");
+ if (!match_str || match_str[0] == '\0') {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not find 3G band values from response");
+ goto out;
+ }
+
+ values = mm_parse_uint_list (match_str, &inner_error);
+ if (!values)
+ goto out;
+
+ for (i = 0; i < values->len; i++) {
+ guint value;
+
+ value = g_array_index (values, guint, i);
+
+ if (value < telit_3g_to_mm_band_mask_n_elements) {
+ guint j;
+
+ for (j = 0; j < G_N_ELEMENTS (band_utran_index); j++) {
+ /* ignore non-3G bands */
+ if (band_utran_index[j] == 0)
+ continue;
+
+ if ((telit_3g_to_mm_band_mask[value] & B3G_FLAG (j)) && !mm_common_bands_garray_lookup (*bands, j))
+ *bands = g_array_append_val (*bands, j);
+ }
+ } else
+ mm_obj_dbg (log_object, "unhandled telit 3G band value configuration: %u", value);
+ }
+
+out:
+ g_free (match_str);
+ g_clear_pointer (&values, g_array_unref);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+telit_get_4g_mm_bands (GMatchInfo *match_info,
+ GArray **bands,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ MMModemBand band;
+ gchar *match_str = NULL;
+ guint64 value;
+ gchar **tokens = NULL;
+ gboolean hex_format = FALSE;
+ gboolean ok;
+
+ match_str = g_match_info_fetch_named (match_info, "Bands4GDec");
+ if (!match_str) {
+ match_str = g_match_info_fetch_named (match_info, "Bands4GHex");
+ hex_format = match_str != NULL;
+ }
+
+ if (!match_str || match_str[0] == '\0') {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not find 4G band flags from response");
+ goto out;
+ }
+
+ /* splitting will never return NULL as string is not empty */
+ tokens = g_strsplit (match_str, "-", -1);
+
+ /* If this is a range, get upper threshold, which contains the total supported mask */
+ ok = hex_format?
+ mm_get_u64_from_hex_str (tokens[1] ? tokens[1] : tokens[0], &value):
+ mm_get_u64_from_str (tokens[1] ? tokens[1] : tokens[0], &value);
+ if (!ok) {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not parse 4G band mask from string: '%s'", match_str);
+ goto out;
+ }
+
+ for (band = MM_MODEM_BAND_TELIT_4G_FIRST; band <= MM_MODEM_BAND_TELIT_4G_LAST; band++) {
+ if ((value & B4G_FLAG (band)) && !mm_common_bands_garray_lookup (*bands, band))
+ g_array_append_val (*bands, band);
+ }
+
+out:
+ g_strfreev (tokens);
+ g_free (match_str);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+telit_get_ext_4g_mm_bands (GMatchInfo *match_info,
+ GArray **bands,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ MMModemBand band;
+ gchar *match_str = NULL;
+ gchar *match_str_ext = NULL;
+ guint64 value;
+
+ match_str = g_match_info_fetch_named (match_info, "Bands4GHex");
+ if (!match_str || match_str[0] == '\0') {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not find 4G band hex mask flag from response");
+ goto out;
+ }
+
+ if (!mm_get_u64_from_hex_str (match_str, &value)) {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not parse 4G band hex mask from string: '%s'", match_str);
+ goto out;
+ }
+
+ for (band = MM_MODEM_BAND_TELIT_4G_FIRST; band <= MM_MODEM_BAND_TELIT_4G_LAST; band++) {
+ if ((value & B4G_FLAG (band)) && !mm_common_bands_garray_lookup (*bands, band))
+ g_array_append_val (*bands, band);
+ }
+
+ /* extended bands */
+ match_str_ext = g_match_info_fetch_named (match_info, "Bands4GExt");
+ if (match_str_ext) {
+
+ if (!mm_get_u64_from_hex_str (match_str_ext, &value)) {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not parse 4G ext band mask from string: '%s'", match_str_ext);
+ goto out;
+ }
+
+ for (band = MM_MODEM_BAND_TELIT_EXT_4G_FIRST; band <= MM_MODEM_BAND_TELIT_EXT_4G_LAST; band++) {
+ if ((value & B4G_FLAG_EXT (band)) && !mm_common_bands_garray_lookup (*bands, band))
+ g_array_append_val (*bands, band);
+ }
+ }
+
+out:
+ g_free (match_str);
+ g_free (match_str_ext);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+typedef enum {
+ LOAD_BANDS_TYPE_SUPPORTED,
+ LOAD_BANDS_TYPE_CURRENT,
+} LoadBandsType;
+
+/* Regex tokens for #BND parsing */
+#define MM_SUPPORTED_BANDS_2G "\\s*\\((?P<Bands2G>[0-9\\-,]*)\\)"
+#define MM_SUPPORTED_BANDS_3G "(,\\s*\\((?P<Bands3G>[0-9\\-,]*)\\))?"
+#define MM_SUPPORTED_BANDS_4G_HEX "(,\\s*\\((?P<Bands4GHex>[0-9A-F\\-,]*)\\))?"
+#define MM_SUPPORTED_BANDS_4G_DEC "(,\\s*\\((?P<Bands4GDec>[0-9\\-,]*)\\))?"
+#define MM_SUPPORTED_BANDS_4G_EXT "(,\\s*\\((?P<Bands4GHex>[0-9A-F]+)\\))?(,\\s*\\((?P<Bands4GExt>[0-9A-F]+)\\))?"
+#define MM_CURRENT_BANDS_2G "\\s*(?P<Bands2G>\\d+)"
+#define MM_CURRENT_BANDS_3G "(,\\s*(?P<Bands3G>\\d+))?"
+#define MM_CURRENT_BANDS_4G_HEX "(,\\s*(?P<Bands4GHex>[0-9A-F]+))?"
+#define MM_CURRENT_BANDS_4G_DEC "(,\\s*(?P<Bands4GDec>\\d+))?"
+#define MM_CURRENT_BANDS_4G_EXT "(,\\s*(?P<Bands4GHex>[0-9A-F]+))?(,\\s*(?P<Bands4GExt>[0-9A-F]+))?"
+
+static GArray *
+common_parse_bnd_response (const gchar *response,
+ MMTelitBNDParseConfig *config,
+ LoadBandsType load_type,
+ gpointer log_object,
+ GError **error)
+{
+ g_autoptr(GMatchInfo) match_info = NULL;
+ g_autoptr(GRegex) r = NULL;
+ GError *inner_error = NULL;
+ GArray *bands = NULL;
+ const gchar *load_bands_regex = NULL;
+
+ static const gchar *load_bands_regex_4g_hex[] = {
+ [LOAD_BANDS_TYPE_SUPPORTED] = "#BND:"MM_SUPPORTED_BANDS_2G MM_SUPPORTED_BANDS_3G MM_SUPPORTED_BANDS_4G_HEX,
+ [LOAD_BANDS_TYPE_CURRENT] = "#BND:"MM_CURRENT_BANDS_2G MM_CURRENT_BANDS_3G MM_CURRENT_BANDS_4G_HEX,
+
+ };
+ static const gchar *load_bands_regex_4g_dec[] = {
+ [LOAD_BANDS_TYPE_SUPPORTED] = "#BND:"MM_SUPPORTED_BANDS_2G MM_SUPPORTED_BANDS_3G MM_SUPPORTED_BANDS_4G_DEC,
+ [LOAD_BANDS_TYPE_CURRENT] = "#BND:"MM_CURRENT_BANDS_2G MM_CURRENT_BANDS_3G MM_CURRENT_BANDS_4G_DEC,
+ };
+ static const gchar *load_bands_regex_4g_ext[] = {
+ [LOAD_BANDS_TYPE_SUPPORTED] = "#BND:"MM_SUPPORTED_BANDS_2G MM_SUPPORTED_BANDS_3G MM_SUPPORTED_BANDS_4G_EXT,
+ [LOAD_BANDS_TYPE_CURRENT] = "#BND:"MM_CURRENT_BANDS_2G MM_CURRENT_BANDS_3G MM_CURRENT_BANDS_4G_EXT,
+ };
+
+ if (config->modem_ext_4g_bands)
+ load_bands_regex = load_bands_regex_4g_ext[load_type];
+ else if (config->modem_has_hex_format_4g_bands)
+ load_bands_regex = load_bands_regex_4g_hex[load_type];
+ else
+ load_bands_regex = load_bands_regex_4g_dec[load_type];
+
+ r = g_regex_new (load_bands_regex, G_REGEX_RAW, 0, NULL);
+ g_assert (r);
+
+ if (!g_regex_match (r, response, 0, &match_info)) {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not parse response '%s'", response);
+ goto out;
+ }
+
+ if (!g_match_info_matches (match_info)) {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not find matches in response '%s'", response);
+ goto out;
+ }
+
+ bands = g_array_new (TRUE, TRUE, sizeof (MMModemBand));
+
+ if (config->modem_is_2g && !telit_get_2g_mm_bands (match_info, log_object, &bands, &inner_error))
+ goto out;
+
+ if (config->modem_is_3g && !telit_get_3g_mm_bands (match_info, log_object, config->modem_alternate_3g_bands, &bands, &inner_error))
+ goto out;
+
+ if (config->modem_is_4g) {
+ gboolean ok;
+
+ ok = config->modem_ext_4g_bands?
+ telit_get_ext_4g_mm_bands (match_info, &bands, &inner_error) :
+ telit_get_4g_mm_bands (match_info, &bands, &inner_error);
+ if (!ok)
+ goto out;
+ }
+out:
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ g_clear_pointer (&bands, g_array_unref);
+ return NULL;
+ }
+
+ return bands;
+}
+
+GArray *
+mm_telit_parse_bnd_query_response (const gchar *response,
+ MMTelitBNDParseConfig *config,
+ gpointer log_object,
+ GError **error)
+{
+ return common_parse_bnd_response (response,
+ config,
+ LOAD_BANDS_TYPE_CURRENT,
+ log_object,
+ error);
+}
+
+GArray *
+mm_telit_parse_bnd_test_response (const gchar *response,
+ MMTelitBNDParseConfig *config,
+ gpointer log_object,
+ GError **error)
+{
+ return common_parse_bnd_response (response,
+ config,
+ LOAD_BANDS_TYPE_SUPPORTED,
+ log_object,
+ error);
+}
+
+/*****************************************************************************/
+/* #QSS? response parser */
+
+MMTelitQssStatus
+mm_telit_parse_qss_query (const gchar *response,
+ GError **error)
+{
+ gint qss_status;
+ gint qss_mode;
+
+ qss_status = QSS_STATUS_UNKNOWN;
+ if (sscanf (response, "#QSS: %d,%d", &qss_mode, &qss_status) != 2) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not parse \"#QSS?\" response: %s", response);
+ return QSS_STATUS_UNKNOWN;
+ }
+
+ switch (qss_status) {
+ case QSS_STATUS_SIM_REMOVED:
+ case QSS_STATUS_SIM_INSERTED:
+ case QSS_STATUS_SIM_INSERTED_AND_UNLOCKED:
+ case QSS_STATUS_SIM_INSERTED_AND_READY:
+ return (MMTelitQssStatus) qss_status;
+ default:
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unknown QSS status value given: %d", qss_status);
+ return QSS_STATUS_UNKNOWN;
+ }
+}
+
+/*****************************************************************************/
+/* Supported modes list helper */
+
+GArray *
+mm_telit_build_modes_list (void)
+{
+ GArray *combinations;
+ MMModemModeCombination mode;
+
+ /* Build list of combinations for 3GPP devices */
+ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 7);
+
+ /* 2G only */
+ mode.allowed = MM_MODEM_MODE_2G;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ /* 3G only */
+ mode.allowed = MM_MODEM_MODE_3G;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ /* 2G and 3G */
+ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ /* 4G only */
+ mode.allowed = MM_MODEM_MODE_4G;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ /* 2G and 4G */
+ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G);
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ /* 3G and 4G */
+ mode.allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ /* 2G, 3G and 4G */
+ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+
+ return combinations;
+}
+
+/*****************************************************************************/
+/* Software Package version response parser */
+
+gchar *
+mm_telit_parse_swpkgv_response (const gchar *response)
+{
+ gchar *version = NULL;
+ g_autofree gchar *base_version = NULL;
+ g_autoptr(GRegex) r = NULL;
+ g_autoptr(GMatchInfo) match_info = NULL;
+ guint matches;
+
+ /* We are interested only in the first line of the response */
+ r = g_regex_new ("(?P<Base>\\d{2}.\\d{2}.*)",
+ G_REGEX_RAW | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF,
+ G_REGEX_MATCH_NEWLINE_CR,
+ NULL);
+ g_assert (r != NULL);
+
+ if (!g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, NULL)) {
+ return NULL;
+ }
+
+ matches = g_match_info_get_match_count (match_info);
+ if (matches < 2 || matches > 4) {
+ return NULL;
+ }
+
+ base_version = g_match_info_fetch_named (match_info, "Base");
+ if (base_version)
+ version = g_strdup (base_version);
+
+ return version;
+}
+
+/*****************************************************************************/
+/* MM Telit Model from revision string */
+
+MMTelitModel
+mm_telit_model_from_revision (const gchar *revision)
+{
+ guint i;
+ static const struct {
+ const gchar *revision_prefix;
+ MMTelitModel model;
+ } revision_to_model_map [] = {
+ {"24.01", MM_TELIT_MODEL_LM940},
+ {"25.", MM_TELIT_MODEL_LE910C1},
+ {"32.", MM_TELIT_MODEL_LM960},
+ {"38.", MM_TELIT_MODEL_FN980},
+ {"40.", MM_TELIT_MODEL_LN920},
+ {"45.00", MM_TELIT_MODEL_FN990},
+ };
+
+ if (!revision)
+ return MM_TELIT_MODEL_DEFAULT;
+
+ for (i = 0; i < G_N_ELEMENTS (revision_to_model_map); ++i) {
+ if (g_str_has_prefix (revision, revision_to_model_map[i].revision_prefix))
+ return revision_to_model_map[i].model;
+ }
+
+ return MM_TELIT_MODEL_DEFAULT;
+}
+
+static MMTelitSwRevCmp lm9x0_software_revision_cmp (const gchar *revision_a,
+ const gchar *revision_b)
+{
+ /* LM940 and LM960 share the same software revision format
+ * WW.XY.ABC[-ZZZZ], where WW is the chipset code and C the major version.
+ * If WW is the same, the other values X, Y, A and B are also the same, so
+ * we can limit the comparison to C only. ZZZZ is the minor version (it
+ * includes if version is beta, test, or alpha), but at this stage we are
+ * not interested in compare it. */
+ guint chipset_a, chipset_b;
+ guint major_a, major_b;
+ guint x, y, a, b;
+
+ g_return_val_if_fail (
+ sscanf (revision_a, "%2u.%1u%1u.%1u%1u%1u", &chipset_a, &x, &y, &a, &b, &major_a) == 6,
+ MM_TELIT_SW_REV_CMP_INVALID);
+ g_return_val_if_fail (
+ sscanf (revision_b, "%2u.%1u%1u.%1u%1u%1u", &chipset_b, &x, &y, &a, &b, &major_b) == 6,
+ MM_TELIT_SW_REV_CMP_INVALID);
+
+ if (chipset_a != chipset_b)
+ return MM_TELIT_SW_REV_CMP_INVALID;
+ if (major_a > major_b)
+ return MM_TELIT_SW_REV_CMP_NEWER;
+ if (major_a < major_b)
+ return MM_TELIT_SW_REV_CMP_OLDER;
+ return MM_TELIT_SW_REV_CMP_EQUAL;
+}
+
+MMTelitSwRevCmp mm_telit_software_revision_cmp (const gchar *revision_a,
+ const gchar *revision_b)
+{
+ MMTelitModel model_a;
+ MMTelitModel model_b;
+
+ model_a = mm_telit_model_from_revision (revision_a);
+ model_b = mm_telit_model_from_revision (revision_b);
+
+ if ((model_a == MM_TELIT_MODEL_LM940 || model_a == MM_TELIT_MODEL_LM960) &&
+ (model_b == MM_TELIT_MODEL_LM940 || model_b == MM_TELIT_MODEL_LM960)) {
+ return lm9x0_software_revision_cmp (revision_a, revision_b);
+ }
+
+ return MM_TELIT_SW_REV_CMP_UNSUPPORTED;
+}
diff --git a/src/plugins/telit/mm-modem-helpers-telit.h b/src/plugins/telit/mm-modem-helpers-telit.h
new file mode 100644
index 00000000..38449769
--- /dev/null
+++ b/src/plugins/telit/mm-modem-helpers-telit.h
@@ -0,0 +1,90 @@
+/* -*- 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) 2015-2019 Telit.
+ * Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+#ifndef MM_MODEM_HELPERS_TELIT_H
+#define MM_MODEM_HELPERS_TELIT_H
+
+#include <glib.h>
+#include "ModemManager.h"
+
+typedef enum {
+ MM_TELIT_MODEL_DEFAULT,
+ MM_TELIT_MODEL_FN980,
+ MM_TELIT_MODEL_LE910C1,
+ MM_TELIT_MODEL_LM940,
+ MM_TELIT_MODEL_LM960,
+ MM_TELIT_MODEL_LN920,
+ MM_TELIT_MODEL_FN990,
+} MMTelitModel;
+
+typedef struct {
+ gboolean modem_is_2g;
+ gboolean modem_is_3g;
+ gboolean modem_is_4g;
+ gboolean modem_alternate_3g_bands;
+ gboolean modem_has_hex_format_4g_bands;
+ gboolean modem_ext_4g_bands;
+} MMTelitBNDParseConfig;
+
+typedef enum {
+ MM_TELIT_SW_REV_CMP_INVALID,
+ MM_TELIT_SW_REV_CMP_UNSUPPORTED,
+ MM_TELIT_SW_REV_CMP_OLDER,
+ MM_TELIT_SW_REV_CMP_EQUAL,
+ MM_TELIT_SW_REV_CMP_NEWER,
+} MMTelitSwRevCmp;
+
+/* #BND response parsers and request builder */
+GArray *mm_telit_parse_bnd_query_response (const gchar *response,
+ MMTelitBNDParseConfig *config,
+ gpointer log_object,
+ GError **error);
+GArray *mm_telit_parse_bnd_test_response (const gchar *response,
+ MMTelitBNDParseConfig *config,
+ gpointer log_object,
+ GError **error);
+gchar *mm_telit_build_bnd_request (GArray *bands_array,
+ MMTelitBNDParseConfig *config,
+ GError **error);
+
+/* #QSS? response parser */
+typedef enum { /*< underscore_name=mm_telit_qss_status >*/
+ QSS_STATUS_UNKNOWN = -1,
+ QSS_STATUS_SIM_REMOVED,
+ QSS_STATUS_SIM_INSERTED,
+ QSS_STATUS_SIM_INSERTED_AND_UNLOCKED,
+ QSS_STATUS_SIM_INSERTED_AND_READY,
+} MMTelitQssStatus;
+
+MMTelitQssStatus mm_telit_parse_qss_query (const gchar *response, GError **error);
+
+/* CSIM lock state */
+typedef enum { /*< underscore_name=mm_telit_csim_lock_state >*/
+ CSIM_LOCK_STATE_UNKNOWN,
+ CSIM_LOCK_STATE_UNLOCKED,
+ CSIM_LOCK_STATE_LOCK_REQUESTED,
+ CSIM_LOCK_STATE_LOCKED,
+} MMTelitCsimLockState;
+
+GArray *mm_telit_build_modes_list (void);
+
+gchar *mm_telit_parse_swpkgv_response (const gchar *response);
+
+MMTelitModel mm_telit_model_from_revision (const gchar *revision);
+
+MMTelitSwRevCmp mm_telit_software_revision_cmp (const gchar *reference,
+ const gchar *revision);
+
+#endif /* MM_MODEM_HELPERS_TELIT_H */
diff --git a/src/plugins/telit/mm-plugin-telit.c b/src/plugins/telit/mm-plugin-telit.c
new file mode 100644
index 00000000..d19966d6
--- /dev/null
+++ b/src/plugins/telit/mm-plugin-telit.c
@@ -0,0 +1,132 @@
+/* -*- 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) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2013 Red Hat, Inc.
+ * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include <string.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-object.h"
+#include "mm-modem-helpers.h"
+#include "mm-plugin-telit.h"
+#include "mm-common-telit.h"
+#include "mm-broadband-modem-telit.h"
+
+
+#if defined WITH_QMI
+# include "mm-broadband-modem-qmi.h"
+#endif
+
+#if defined WITH_MBIM
+# include "mm-broadband-modem-mbim-telit.h"
+#endif
+
+G_DEFINE_TYPE (MMPluginTelit, mm_plugin_telit, MM_TYPE_PLUGIN)
+
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
+
+/*****************************************************************************/
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+ const gchar *uid,
+ const gchar **drivers,
+ guint16 vendor,
+ guint16 product,
+ guint16 subsystem_vendor,
+ GList *probes,
+ GError **error)
+{
+#if defined WITH_QMI
+ if (mm_port_probe_list_has_qmi_port (probes)) {
+ mm_obj_dbg (self, "QMI-powered Telit modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+#if defined WITH_MBIM
+ if (mm_port_probe_list_has_mbim_port (probes)) {
+ mm_obj_dbg (self, "MBIM-powered Telit modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_telit_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product,
+ subsystem_vendor));
+ }
+#endif
+
+ return MM_BASE_MODEM (mm_broadband_modem_telit_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", "wwan", NULL };
+ /* Vendors: Telit */
+ static const guint16 vendor_ids[] = { 0x1bc7, 0 };
+ static const mm_uint16_pair subsystem_vendor_ids[] = {
+ { 0x17cb, 0x1c5d }, /* FN990 */
+ { 0, 0 }
+ };
+ static const gchar *vendor_strings[] = { "telit", NULL };
+ /* Custom init for port identification */
+ static const MMAsyncMethod custom_init = {
+ .async = G_CALLBACK (telit_custom_init),
+ .finish = G_CALLBACK (telit_custom_init_finish),
+ };
+
+ return MM_PLUGIN (
+ g_object_new (MM_TYPE_PLUGIN_TELIT,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
+ MM_PLUGIN_ALLOWED_SUBSYSTEM_VENDOR_IDS, subsystem_vendor_ids,
+ MM_PLUGIN_ALLOWED_VENDOR_STRINGS, vendor_strings,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ MM_PLUGIN_ALLOWED_QMI, TRUE,
+ MM_PLUGIN_ALLOWED_MBIM, TRUE,
+ MM_PLUGIN_ALLOWED_QCDM, TRUE,
+ MM_PLUGIN_CUSTOM_INIT, &custom_init,
+ NULL));
+}
+
+static void
+mm_plugin_telit_init (MMPluginTelit *self)
+{
+}
+
+static void
+mm_plugin_telit_class_init (MMPluginTelitClass *klass)
+{
+ MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+ plugin_class->create_modem = create_modem;
+ plugin_class->grab_port = telit_grab_port;
+}
diff --git a/src/plugins/telit/mm-plugin-telit.h b/src/plugins/telit/mm-plugin-telit.h
new file mode 100644
index 00000000..0c61fbb8
--- /dev/null
+++ b/src/plugins/telit/mm-plugin-telit.h
@@ -0,0 +1,42 @@
+/* -*- 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) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2013 Red Hat, Inc.
+ * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#ifndef MM_PLUGIN_TELIT_H
+#define MM_PLUGIN_TELIT_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_TELIT (mm_plugin_telit_get_type ())
+#define MM_PLUGIN_TELIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_TELIT, MMPluginTelit))
+#define MM_PLUGIN_TELIT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_TELIT, MMPluginTelitClass))
+#define MM_IS_PLUGIN_TELIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_TELIT))
+#define MM_IS_PLUGIN_TELIT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_TELIT))
+#define MM_PLUGIN_TELIT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_TELIT, MMPluginTelitClass))
+
+typedef struct {
+ MMPlugin parent;
+} MMPluginTelit;
+
+typedef struct {
+ MMPluginClass parent;
+} MMPluginTelitClass;
+
+GType mm_plugin_telit_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_TELIT_H */
diff --git a/src/plugins/telit/mm-shared-telit.c b/src/plugins/telit/mm-shared-telit.c
new file mode 100644
index 00000000..09c122bb
--- /dev/null
+++ b/src/plugins/telit/mm-shared-telit.c
@@ -0,0 +1,795 @@
+/* -*- 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) 2019 Daniele Palmas <dnlplm@gmail.com>
+ */
+
+#include <config.h>
+
+#include <stdio.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-location.h"
+#include "mm-base-modem.h"
+#include "mm-base-modem-at.h"
+#include "mm-modem-helpers-telit.h"
+#include "mm-shared-telit.h"
+
+/*****************************************************************************/
+/* Private data context */
+
+#define TELIT_LM940_EXT_LTE_BND_SW_REVISION "24.01.516"
+
+#define PRIVATE_TAG "shared-telit-private-tag"
+static GQuark private_quark;
+
+typedef struct {
+ MMIfaceModem *iface_modem_parent;
+ gboolean alternate_3g_bands;
+ gboolean ext_4g_bands;
+ GArray *supported_bands;
+ GArray *supported_modes;
+ gchar *software_package_version;
+} Private;
+
+static void
+private_free (Private *priv)
+{
+ if (priv->supported_bands)
+ g_array_unref (priv->supported_bands);
+ if (priv->supported_modes)
+ g_array_unref (priv->supported_modes);
+ g_free (priv->software_package_version);
+ g_slice_free (Private, priv);
+}
+
+static gboolean
+has_alternate_3g_bands (const gchar *revision)
+{
+ MMTelitModel model;
+
+ model = mm_telit_model_from_revision (revision);
+ return (model == MM_TELIT_MODEL_FN980 ||
+ model == MM_TELIT_MODEL_FN990 ||
+ model == MM_TELIT_MODEL_LM940 ||
+ model == MM_TELIT_MODEL_LM960 ||
+ model == MM_TELIT_MODEL_LN920);
+}
+
+static gboolean
+is_bnd_4g_format_hex (const gchar *revision)
+{
+ MMTelitModel model;
+
+ model = mm_telit_model_from_revision (revision);
+
+ return (model == MM_TELIT_MODEL_FN980 ||
+ model == MM_TELIT_MODEL_LE910C1 ||
+ model == MM_TELIT_MODEL_LM940 ||
+ model == MM_TELIT_MODEL_LM960 ||
+ model == MM_TELIT_MODEL_LN920);
+}
+
+static gboolean
+has_extended_4g_bands (const gchar *revision)
+{
+ MMTelitModel model;
+
+ model = mm_telit_model_from_revision (revision);
+ if (model == MM_TELIT_MODEL_LM940)
+ return mm_telit_software_revision_cmp (revision, TELIT_LM940_EXT_LTE_BND_SW_REVISION) >= MM_TELIT_SW_REV_CMP_EQUAL;
+
+ return (model == MM_TELIT_MODEL_FN980 ||
+ model == MM_TELIT_MODEL_FN990 ||
+ model == MM_TELIT_MODEL_LM960 ||
+ model == MM_TELIT_MODEL_LN920);
+}
+
+static Private *
+get_private (MMSharedTelit *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);
+
+ if (MM_SHARED_TELIT_GET_INTERFACE (self)->peek_parent_modem_interface)
+ priv->iface_modem_parent = MM_SHARED_TELIT_GET_INTERFACE (self)->peek_parent_modem_interface (self);
+
+ g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
+ }
+
+ return priv;
+}
+
+void
+mm_shared_telit_store_supported_modes (MMSharedTelit *self,
+ GArray *modes)
+{
+ Private *priv;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+ priv->supported_modes = g_array_ref (modes);
+}
+
+void
+mm_shared_telit_store_revision (MMSharedTelit *self,
+ const gchar *revision)
+{
+ Private *priv;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+ g_clear_pointer (&priv->software_package_version, g_free);
+ priv->software_package_version = g_strdup (revision);
+ priv->alternate_3g_bands = has_alternate_3g_bands (revision);
+ priv->ext_4g_bands = has_extended_4g_bands (revision);
+}
+
+void
+mm_shared_telit_get_bnd_parse_config (MMIfaceModem *self, MMTelitBNDParseConfig *config)
+{
+ Private *priv;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+
+ config->modem_is_2g = mm_iface_modem_is_2g (self);
+ config->modem_is_3g = mm_iface_modem_is_3g (self);
+ config->modem_is_4g = mm_iface_modem_is_4g (self);
+ config->modem_alternate_3g_bands = priv->alternate_3g_bands;
+ config->modem_has_hex_format_4g_bands = is_bnd_4g_format_hex (priv->software_package_version);
+ config->modem_ext_4g_bands = priv->ext_4g_bands;
+}
+
+/*****************************************************************************/
+/* Load current mode (Modem interface) */
+
+gboolean
+mm_shared_telit_load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error)
+{
+ const gchar *response;
+ const gchar *str;
+ gint a;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!response)
+ return FALSE;
+
+ str = mm_strip_tag (response, "+WS46: ");
+
+ if (!sscanf (str, "%d", &a)) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse +WS46 response: '%s'",
+ response);
+ return FALSE;
+ }
+
+ *preferred = MM_MODEM_MODE_NONE;
+ switch (a) {
+ case 12:
+ *allowed = MM_MODEM_MODE_2G;
+ return TRUE;
+ case 22:
+ *allowed = MM_MODEM_MODE_3G;
+ return TRUE;
+ case 25:
+ if (mm_iface_modem_is_3gpp_lte (self))
+ *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+ else
+ *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ return TRUE;
+ case 28:
+ *allowed = MM_MODEM_MODE_4G;
+ return TRUE;
+ case 29:
+ *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ return TRUE;
+ case 30:
+ *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G);
+ return TRUE;
+ case 31:
+ *allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+ return TRUE;
+ default:
+ break;
+ }
+
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse unexpected +WS46 response: '%s'",
+ response);
+ return FALSE;
+}
+
+void
+mm_shared_telit_load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+WS46?",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Load supported bands (Modem interface) */
+
+GArray *
+mm_shared_telit_modem_load_supported_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_supported_bands_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response)
+ g_task_return_error (task, error);
+ else {
+ GArray *bands;
+ MMTelitBNDParseConfig config;
+
+ mm_shared_telit_get_bnd_parse_config (MM_IFACE_MODEM (self), &config);
+
+ bands = mm_telit_parse_bnd_test_response (response, &config, self, &error);
+ if (!bands)
+ g_task_return_error (task, error);
+ else {
+ /* Store supported bands to be able to build ANY when setting */
+ priv->supported_bands = g_array_ref (bands);
+ if (priv->ext_4g_bands)
+ mm_obj_dbg (self, "telit modem using extended 4G band setup");
+ g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
+ }
+ }
+
+ g_object_unref (task);
+}
+
+static void
+load_supported_bands_at (MMIfaceModem *self,
+ GTask *task)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "#BND=?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback) load_supported_bands_ready,
+ task);
+}
+
+static void
+parent_load_supported_bands_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GArray *bands;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+
+ bands = priv->iface_modem_parent->load_supported_bands_finish (MM_IFACE_MODEM (self), res, &error);
+ if (bands) {
+ g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
+ } else {
+ mm_obj_dbg (self, "parent load supported bands failure, falling back to AT commands");
+ load_supported_bands_at (self, task);
+ g_clear_error (&error);
+ }
+}
+
+void
+mm_shared_telit_modem_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_TELIT (self));
+
+ if (priv->iface_modem_parent &&
+ priv->iface_modem_parent->load_supported_bands &&
+ priv->iface_modem_parent->load_supported_bands_finish) {
+ priv->iface_modem_parent->load_supported_bands (self,
+ (GAsyncReadyCallback) parent_load_supported_bands_ready,
+ task);
+ } else
+ load_supported_bands_at (self, task);
+}
+
+/*****************************************************************************/
+/* Load current bands (Modem interface) */
+
+GArray *
+mm_shared_telit_modem_load_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_current_bands_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response)
+ g_task_return_error (task, error);
+ else {
+ GArray *bands;
+ MMTelitBNDParseConfig config;
+
+ mm_shared_telit_get_bnd_parse_config (MM_IFACE_MODEM (self), &config);
+
+ bands = mm_telit_parse_bnd_query_response (response, &config, self, &error);
+ if (!bands)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
+ }
+
+ g_object_unref (task);
+}
+
+static void
+load_current_bands_at (MMIfaceModem *self,
+ GTask *task)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "#BND?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) load_current_bands_ready,
+ task);
+}
+
+static void
+parent_load_current_bands_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GArray *bands;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+
+ bands = priv->iface_modem_parent->load_current_bands_finish (MM_IFACE_MODEM (self), res, &error);
+ if (bands) {
+ g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
+ } else {
+ mm_obj_dbg (self, "parent load current bands failure, falling back to AT commands");
+ load_current_bands_at (self, task);
+ g_clear_error (&error);
+ }
+}
+
+void
+mm_shared_telit_modem_load_current_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_TELIT (self));
+
+ if (priv->iface_modem_parent &&
+ priv->iface_modem_parent->load_current_bands &&
+ priv->iface_modem_parent->load_current_bands_finish) {
+ priv->iface_modem_parent->load_current_bands (self,
+ (GAsyncReadyCallback) parent_load_current_bands_ready,
+ task);
+ } else
+ load_current_bands_at (self, task);
+}
+
+/*****************************************************************************/
+/* Set current bands (Modem interface) */
+
+gboolean
+mm_shared_telit_modem_set_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+set_current_bands_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ mm_base_modem_at_command_finish (self, res, &error);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
+}
+
+static void
+set_current_bands_at (MMIfaceModem *self,
+ GTask *task)
+{
+ GError *error = NULL;
+ gchar *cmd;
+ GArray *bands_array;
+ MMTelitBNDParseConfig config;
+
+ bands_array = g_task_get_task_data (task);
+ g_assert (bands_array);
+
+ if (bands_array->len == 1 && g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
+ Private *priv;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+ if (!priv->supported_bands) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't build ANY band settings: unknown supported bands");
+ g_object_unref (task);
+ return;
+ }
+ bands_array = priv->supported_bands;
+ }
+
+ mm_shared_telit_get_bnd_parse_config (self, &config);
+ cmd = mm_telit_build_bnd_request (bands_array, &config, &error);
+ if (!cmd) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ cmd,
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)set_current_bands_ready,
+ task);
+ g_free (cmd);
+}
+
+static void
+parent_set_current_bands_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+
+ if (priv->iface_modem_parent->set_current_bands_finish (MM_IFACE_MODEM (self), res, &error)) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ } else {
+ g_clear_error (&error);
+ set_current_bands_at (self, task);
+ }
+}
+
+void
+mm_shared_telit_modem_set_current_bands (MMIfaceModem *self,
+ GArray *bands_array,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, g_array_ref (bands_array), (GDestroyNotify)g_array_unref);
+
+ if (priv->iface_modem_parent &&
+ priv->iface_modem_parent->set_current_bands &&
+ priv->iface_modem_parent->set_current_bands_finish) {
+ priv->iface_modem_parent->set_current_bands (self,
+ bands_array,
+ (GAsyncReadyCallback) parent_set_current_bands_ready,
+ task);
+ } else
+ set_current_bands_at (self, task);
+}
+
+/*****************************************************************************/
+/* Set current modes (Modem interface) */
+
+gboolean
+mm_shared_telit_set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+ws46_set_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ mm_base_modem_at_command_finish (self, res, &error);
+ if (error)
+ /* Let the error be critical. */
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_telit_set_current_modes (MMIfaceModem *self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ gchar *command;
+ Private *priv;
+ gint ws46_mode = -1;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (allowed == MM_MODEM_MODE_ANY && priv->supported_modes) {
+ guint i;
+
+ allowed = MM_MODEM_MODE_NONE;
+ /* Process list of modes to gather supported ones */
+ for (i = 0; i < priv->supported_modes->len; i++) {
+ if (g_array_index (priv->supported_modes, MMModemMode, i) & MM_MODEM_MODE_2G)
+ allowed |= MM_MODEM_MODE_2G;
+ if (g_array_index (priv->supported_modes, MMModemMode, i) & MM_MODEM_MODE_3G)
+ allowed |= MM_MODEM_MODE_3G;
+ if (g_array_index (priv->supported_modes, MMModemMode, i) & MM_MODEM_MODE_4G)
+ allowed |= MM_MODEM_MODE_4G;
+ }
+ }
+
+ if (allowed == MM_MODEM_MODE_2G)
+ ws46_mode = 12;
+ else if (allowed == MM_MODEM_MODE_3G)
+ ws46_mode = 22;
+ else if (allowed == MM_MODEM_MODE_4G)
+ ws46_mode = 28;
+ else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) {
+ if (mm_iface_modem_is_3gpp_lte (self))
+ ws46_mode = 29;
+ else
+ ws46_mode = 25;
+ } else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G))
+ ws46_mode = 30;
+ else if (allowed == (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G))
+ ws46_mode = 31;
+ else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G))
+ ws46_mode = 25;
+
+ /* Telit modems do not support preferred mode selection */
+ if ((ws46_mode < 0) || (preferred != MM_MODEM_MODE_NONE)) {
+ gchar *allowed_str;
+ gchar *preferred_str;
+
+ allowed_str = mm_modem_mode_build_string_from_mask (allowed);
+ preferred_str = mm_modem_mode_build_string_from_mask (preferred);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Requested mode (allowed: '%s', preferred: '%s') not "
+ "supported by the modem.",
+ allowed_str,
+ preferred_str);
+ g_free (allowed_str);
+ g_free (preferred_str);
+
+ g_object_unref (task);
+ return;
+ }
+
+ command = g_strdup_printf ("AT+WS46=%d", ws46_mode);
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ command,
+ 10,
+ FALSE,
+ (GAsyncReadyCallback) ws46_set_ready,
+ task);
+ g_free (command);
+}
+
+/*****************************************************************************/
+/* Revision loading */
+
+gchar *
+mm_shared_telit_modem_load_revision_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_revision_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error;
+ GVariant *result;
+
+ result = mm_base_modem_at_sequence_finish (self, res, NULL, &error);
+ if (!result) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ } else {
+ gchar *revision = NULL;
+
+ revision = g_variant_dup_string (result, NULL);
+ mm_shared_telit_store_revision (MM_SHARED_TELIT (self), revision);
+ g_task_return_pointer (task, revision, g_free);
+ g_object_unref (task);
+ }
+}
+
+/*
+ * parse AT#SWPKGV command
+ * Execution command returns the software package version without #SWPKGV: command echo.
+ * The response is as follows:
+ *
+ * AT#SWPKGV
+ * <Telit Software Package Version>-<Production Parameters Version>
+ * <Modem FW Version> (Usually the same value returned by AT+GMR)
+ * <Production Parameters Version>
+ * <Application FW Version>
+ */
+static MMBaseModemAtResponseProcessorResult
+software_package_version_ready (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
+{
+ gchar *version = NULL;
+
+ if (error) {
+ *result = NULL;
+
+ /* Ignore AT errors (ie, ERROR or CMx ERROR) */
+ if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) {
+ *result_error = g_error_copy (error);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+ }
+
+ *result_error = NULL;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
+ }
+
+ version = mm_telit_parse_swpkgv_response (response);
+ if (!version)
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
+
+ *result = g_variant_new_take_string (version);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+}
+
+static const MMBaseModemAtCommand revisions[] = {
+ { "#SWPKGV", 3, TRUE, software_package_version_ready },
+ { "+CGMR", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors },
+ { "+GMR", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors },
+ { NULL }
+};
+
+void
+mm_shared_telit_modem_load_revision (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_TELIT (self));
+
+ mm_obj_dbg (self, "loading revision...");
+ if (priv->software_package_version) {
+ g_task_return_pointer (task,
+ g_strdup (priv->software_package_version),
+ g_free);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_base_modem_at_sequence (
+ MM_BASE_MODEM (self),
+ revisions,
+ NULL, /* response_processor_context */
+ NULL, /* response_processor_context_free */
+ (GAsyncReadyCallback) load_revision_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+static void
+shared_telit_init (gpointer g_iface)
+{
+}
+
+GType
+mm_shared_telit_get_type (void)
+{
+ static GType shared_telit_type = 0;
+
+ if (!G_UNLIKELY (shared_telit_type)) {
+ static const GTypeInfo info = {
+ sizeof (MMSharedTelit), /* class_size */
+ shared_telit_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ shared_telit_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedTelit", &info, 0);
+ g_type_interface_add_prerequisite (shared_telit_type, MM_TYPE_IFACE_MODEM);
+ }
+
+ return shared_telit_type;
+}
+
diff --git a/src/plugins/telit/mm-shared-telit.h b/src/plugins/telit/mm-shared-telit.h
new file mode 100644
index 00000000..bf093ea5
--- /dev/null
+++ b/src/plugins/telit/mm-shared-telit.h
@@ -0,0 +1,107 @@
+/* -*- 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) 2019 Daniele Palmas <dnlplm@gmail.com>
+ */
+
+#ifndef MM_SHARED_TELIT_H
+#define MM_SHARED_TELIT_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-broadband-modem.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-location.h"
+#include "mm-modem-helpers-telit.h"
+
+#define MM_TYPE_SHARED_TELIT (mm_shared_telit_get_type ())
+#define MM_SHARED_TELIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_TELIT, MMSharedTelit))
+#define MM_IS_SHARED_TELIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_TELIT))
+#define MM_SHARED_TELIT_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_TELIT, MMSharedTelit))
+
+typedef struct _MMSharedTelit MMSharedTelit;
+
+struct _MMSharedTelit {
+ GTypeInterface g_iface;
+
+ /* Peek modem interface of the parent class of the object */
+ MMIfaceModem * (* peek_parent_modem_interface) (MMSharedTelit *self);
+};
+
+GType mm_shared_telit_get_type (void);
+
+void mm_shared_telit_store_supported_modes (MMSharedTelit *self,
+ GArray *modes);
+
+gboolean mm_shared_telit_load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error);
+
+void mm_shared_telit_load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean mm_shared_telit_set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_telit_set_current_modes (MMIfaceModem *self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+void mm_shared_telit_modem_load_supported_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+GArray * mm_shared_telit_modem_load_supported_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_telit_modem_load_current_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+GArray * mm_shared_telit_modem_load_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean mm_shared_telit_modem_set_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_telit_modem_set_current_bands (MMIfaceModem *self,
+ GArray *bands_array,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+void mm_shared_telit_modem_load_revision (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gchar * mm_shared_telit_modem_load_revision_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_telit_store_revision (MMSharedTelit *self,
+ const gchar *revision);
+
+void mm_shared_telit_get_bnd_parse_config (MMIfaceModem *self,
+ MMTelitBNDParseConfig *config);
+#endif /* MM_SHARED_TELIT_H */
diff --git a/src/plugins/telit/mm-shared.c b/src/plugins/telit/mm-shared.c
new file mode 100644
index 00000000..ad2843dd
--- /dev/null
+++ b/src/plugins/telit/mm-shared.c
@@ -0,0 +1,20 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "mm-shared.h"
+
+MM_SHARED_DEFINE_MAJOR_VERSION
+MM_SHARED_DEFINE_MINOR_VERSION
+MM_SHARED_DEFINE_NAME(Telit)
diff --git a/src/plugins/telit/tests/test-mm-modem-helpers-telit.c b/src/plugins/telit/tests/test-mm-modem-helpers-telit.c
new file mode 100644
index 00000000..e14ba6ba
--- /dev/null
+++ b/src/plugins/telit/tests/test-mm-modem-helpers-telit.c
@@ -0,0 +1,695 @@
+/* -*- 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) 2015-2019 Telit
+ * Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ *
+ */
+#include <stdio.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <locale.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-test.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-telit.h"
+
+#include "test-helpers.h"
+
+/******************************************************************************/
+
+#define MAX_BANDS_LIST_LEN 17
+
+typedef struct {
+ const gchar *response;
+ MMTelitBNDParseConfig config;
+ guint mm_bands_len;
+ MMModemBand mm_bands [MAX_BANDS_LIST_LEN];
+} BndResponseTest;
+
+static BndResponseTest supported_band_mapping_tests [] = {
+ {
+ "#BND: (0-3)", {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE}, 4,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_G850 }
+ },
+ {
+ "#BND: (0-3),(0,2,5,6)", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 7,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_8 }
+ },
+ {
+ "#BND: (0,3),(0,2,5,6)", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 7,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_8 }
+ },
+ {
+ "#BND: (0,2),(0,2,5,6)", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 6,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_8 }
+ },
+ {
+ "#BND: (0,2),(0-4,5,6)", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 7,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_2,
+ MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_8 }
+ },
+ {
+ "#BND: (0-3),(0,2,5,6),(1-1)", {TRUE, TRUE, TRUE, FALSE, FALSE, FALSE}, 8,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_8,
+ MM_MODEM_BAND_EUTRAN_1 }
+ },
+ {
+ "#BND: (0),(0),(1-3)", {TRUE, TRUE, TRUE, FALSE, FALSE, FALSE}, 5,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_EUTRAN_1,
+ MM_MODEM_BAND_EUTRAN_2 }
+ },
+ {
+ "#BND: (0),(0),(1-3)", {FALSE, FALSE, TRUE, FALSE, FALSE, FALSE}, 2,
+ { MM_MODEM_BAND_EUTRAN_1,
+ MM_MODEM_BAND_EUTRAN_2 }
+ },
+ /* 3G alternate band settings: default */
+ {
+ "#BND: (0),(0,2,5,6,12,25)", {FALSE, TRUE, FALSE, FALSE, FALSE, FALSE}, 5,
+ { MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_8,
+ MM_MODEM_BAND_UTRAN_6,
+ MM_MODEM_BAND_UTRAN_19 }
+ },
+ /* 3G alternate band settings: alternate */
+ {
+ "#BND: (0),(0,2,5,6,12,13)", {FALSE, TRUE, FALSE, TRUE, FALSE, FALSE}, 4,
+ { MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_3,
+ MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_8 }
+ },
+ /* ME910 (2G+4G device)
+ * 168695967: 0xA0E189F: 0000 1010 0000 1110 0001 1000 1001 1111
+ */
+ {
+ "#BND: (0-5),(0),(1-168695967)", {TRUE, FALSE, TRUE, FALSE, FALSE, FALSE}, 17,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_EUTRAN_1,
+ MM_MODEM_BAND_EUTRAN_2,
+ MM_MODEM_BAND_EUTRAN_3,
+ MM_MODEM_BAND_EUTRAN_4,
+ MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_8,
+ MM_MODEM_BAND_EUTRAN_12,
+ MM_MODEM_BAND_EUTRAN_13,
+ MM_MODEM_BAND_EUTRAN_18,
+ MM_MODEM_BAND_EUTRAN_19,
+ MM_MODEM_BAND_EUTRAN_20,
+ MM_MODEM_BAND_EUTRAN_26,
+ MM_MODEM_BAND_EUTRAN_28 }
+ },
+ /* 4G ext band settings: devices such as LN920 */
+ {
+ "#BND: (0),(0),(1003100185A),(42)", {FALSE, TRUE, TRUE, FALSE, TRUE, TRUE}, 13,
+ { MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_EUTRAN_2,
+ MM_MODEM_BAND_EUTRAN_4,
+ MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7,
+ MM_MODEM_BAND_EUTRAN_12,
+ MM_MODEM_BAND_EUTRAN_13,
+ MM_MODEM_BAND_EUTRAN_25,
+ MM_MODEM_BAND_EUTRAN_29,
+ MM_MODEM_BAND_EUTRAN_30,
+ MM_MODEM_BAND_EUTRAN_41,
+ MM_MODEM_BAND_EUTRAN_66,
+ MM_MODEM_BAND_EUTRAN_71 }
+ },
+ /* 4G band in hex format: devices such as LE910C1-EUX */
+ {
+ "#BND: (0),(0,5,6,13,15,23),(80800C5)", {TRUE, TRUE, TRUE, FALSE, TRUE, FALSE}, 11,
+ {
+ MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_3,
+ MM_MODEM_BAND_UTRAN_8,
+ MM_MODEM_BAND_EUTRAN_1,
+ MM_MODEM_BAND_EUTRAN_3,
+ MM_MODEM_BAND_EUTRAN_7,
+ MM_MODEM_BAND_EUTRAN_8,
+ MM_MODEM_BAND_EUTRAN_20,
+ MM_MODEM_BAND_EUTRAN_28 }
+ }
+};
+
+static void
+test_parse_supported_bands_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (supported_band_mapping_tests); i++) {
+ GError *error = NULL;
+ GArray *bands = NULL;
+
+ bands = mm_telit_parse_bnd_test_response (supported_band_mapping_tests[i].response,
+ &supported_band_mapping_tests[i].config,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (bands);
+
+ mm_test_helpers_compare_bands (bands,
+ supported_band_mapping_tests[i].mm_bands,
+ supported_band_mapping_tests[i].mm_bands_len);
+ g_array_unref (bands);
+ }
+}
+
+static BndResponseTest current_band_mapping_tests [] = {
+ {
+ "#BND: 0", {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE}, 2,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS }
+ },
+ {
+ "#BND: 0,5", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 3,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_UTRAN_8 }
+ },
+ {
+ "#BND: 1,3", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 5,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_2,
+ MM_MODEM_BAND_UTRAN_5 }
+ },
+ {
+ "#BND: 2,7", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 3,
+ { MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_UTRAN_4 }
+ },
+ {
+ "#BND: 3,0,1", {TRUE, TRUE, TRUE, FALSE, FALSE, FALSE}, 4,
+ { MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_EUTRAN_1 }
+ },
+ {
+ "#BND: 0,0,3", {TRUE, FALSE, TRUE, FALSE, FALSE, FALSE}, 4,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_EUTRAN_1,
+ MM_MODEM_BAND_EUTRAN_2 }
+ },
+ {
+ "#BND: 0,0,3", {FALSE, FALSE, TRUE, FALSE, FALSE, FALSE}, 2,
+ { MM_MODEM_BAND_EUTRAN_1,
+ MM_MODEM_BAND_EUTRAN_2 }
+ },
+ /* 3G alternate band settings: default */
+ {
+ "#BND: 0,12", {FALSE, TRUE, FALSE, FALSE, FALSE, FALSE}, 1,
+ { MM_MODEM_BAND_UTRAN_6 }
+ },
+ /* 3G alternate band settings: alternate */
+ {
+ "#BND: 0,12", {FALSE, TRUE, FALSE, TRUE, FALSE, FALSE}, 4,
+ { MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_3,
+ MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_8 }
+ },
+ /* ME910 (2G+4G device)
+ * 168695967: 0xA0E189F: 0000 1010 0000 1110 0001 1000 1001 1111
+ */
+ {
+ "#BND: 5,0,168695967", {TRUE, FALSE, TRUE, FALSE, FALSE, FALSE}, 17,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_EUTRAN_1,
+ MM_MODEM_BAND_EUTRAN_2,
+ MM_MODEM_BAND_EUTRAN_3,
+ MM_MODEM_BAND_EUTRAN_4,
+ MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_8,
+ MM_MODEM_BAND_EUTRAN_12,
+ MM_MODEM_BAND_EUTRAN_13,
+ MM_MODEM_BAND_EUTRAN_18,
+ MM_MODEM_BAND_EUTRAN_19,
+ MM_MODEM_BAND_EUTRAN_20,
+ MM_MODEM_BAND_EUTRAN_26,
+ MM_MODEM_BAND_EUTRAN_28 }
+ },
+ /* 4G ext band settings: devices such as LN920 */
+ {
+ "#BND: 0,0,1003100185A,42", {FALSE, TRUE, TRUE, FALSE, FALSE, TRUE}, 13,
+ { MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_EUTRAN_2,
+ MM_MODEM_BAND_EUTRAN_4,
+ MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7,
+ MM_MODEM_BAND_EUTRAN_12,
+ MM_MODEM_BAND_EUTRAN_13,
+ MM_MODEM_BAND_EUTRAN_25,
+ MM_MODEM_BAND_EUTRAN_29,
+ MM_MODEM_BAND_EUTRAN_30,
+ MM_MODEM_BAND_EUTRAN_41,
+ MM_MODEM_BAND_EUTRAN_66,
+ MM_MODEM_BAND_EUTRAN_71 }
+ }
+};
+
+static void
+test_parse_current_bands_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (current_band_mapping_tests); i++) {
+ GError *error = NULL;
+ GArray *bands = NULL;
+
+ bands = mm_telit_parse_bnd_query_response (current_band_mapping_tests[i].response,
+ &current_band_mapping_tests[i].config,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (bands);
+
+ mm_test_helpers_compare_bands (bands,
+ current_band_mapping_tests[i].mm_bands,
+ current_band_mapping_tests[i].mm_bands_len);
+ g_array_unref (bands);
+ }
+}
+
+/******************************************************************************/
+
+static void
+test_common_bnd_cmd (const gchar *expected_cmd,
+ gboolean modem_is_2g,
+ gboolean modem_is_3g,
+ gboolean modem_is_4g,
+ gboolean modem_alternate_3g_bands,
+ gboolean modem_ext_4g_bands,
+ GArray *bands_array)
+{
+ gchar *cmd;
+ GError *error = NULL;
+ MMTelitBNDParseConfig config = {
+ .modem_is_2g = modem_is_2g,
+ .modem_is_3g = modem_is_3g,
+ .modem_is_4g = modem_is_4g,
+ .modem_alternate_3g_bands = modem_alternate_3g_bands,
+ .modem_ext_4g_bands = modem_ext_4g_bands
+ };
+
+ cmd = mm_telit_build_bnd_request (bands_array, &config, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (cmd, ==, expected_cmd);
+ g_free (cmd);
+}
+
+#define test_common_bnd_cmd_2g(EXPECTED_CMD, BANDS_ARRAY) test_common_bnd_cmd (EXPECTED_CMD, TRUE, FALSE, FALSE, FALSE, FALSE, BANDS_ARRAY)
+#define test_common_bnd_cmd_3g(EXPECTED_CMD, ALTERNATE, BANDS_ARRAY) test_common_bnd_cmd (EXPECTED_CMD, FALSE, TRUE, FALSE, ALTERNATE, FALSE, BANDS_ARRAY)
+#define test_common_bnd_cmd_4g(EXPECTED_CMD, EXTENDED, BANDS_ARRAY) test_common_bnd_cmd (EXPECTED_CMD, FALSE, FALSE, TRUE, FALSE, EXTENDED, BANDS_ARRAY)
+
+static void
+test_common_bnd_cmd_error (gboolean modem_is_2g,
+ gboolean modem_is_3g,
+ gboolean modem_is_4g,
+ GArray *bands_array,
+ MMCoreError expected_error)
+{
+ gchar *cmd;
+ GError *error = NULL;
+ MMTelitBNDParseConfig config = {
+ .modem_is_2g = modem_is_2g,
+ .modem_is_3g = modem_is_3g,
+ .modem_is_4g = modem_is_4g,
+ .modem_alternate_3g_bands = FALSE,
+ .modem_ext_4g_bands = FALSE,
+ };
+ cmd = mm_telit_build_bnd_request (bands_array, &config, &error);
+ g_assert_error (error, MM_CORE_ERROR, (gint)expected_error);
+ g_assert (!cmd);
+}
+
+#define test_common_bnd_cmd_2g_invalid(BANDS_ARRAY) test_common_bnd_cmd_error (TRUE, FALSE, FALSE, BANDS_ARRAY, MM_CORE_ERROR_FAILED)
+#define test_common_bnd_cmd_3g_invalid(BANDS_ARRAY) test_common_bnd_cmd_error (FALSE, TRUE, FALSE, BANDS_ARRAY, MM_CORE_ERROR_FAILED)
+#define test_common_bnd_cmd_4g_invalid(BANDS_ARRAY) test_common_bnd_cmd_error (FALSE, FALSE, TRUE, BANDS_ARRAY, MM_CORE_ERROR_FAILED)
+#define test_common_bnd_cmd_2g_not_found(BANDS_ARRAY) test_common_bnd_cmd_error (TRUE, FALSE, FALSE, BANDS_ARRAY, MM_CORE_ERROR_NOT_FOUND)
+#define test_common_bnd_cmd_3g_not_found(BANDS_ARRAY) test_common_bnd_cmd_error (FALSE, TRUE, FALSE, BANDS_ARRAY, MM_CORE_ERROR_NOT_FOUND)
+#define test_common_bnd_cmd_4g_not_found(BANDS_ARRAY) test_common_bnd_cmd_error (FALSE, FALSE, TRUE, BANDS_ARRAY, MM_CORE_ERROR_NOT_FOUND)
+
+static void
+test_telit_get_2g_bnd_flag (void)
+{
+ GArray *bands_array;
+ MMModemBand egsm = MM_MODEM_BAND_EGSM;
+ MMModemBand dcs = MM_MODEM_BAND_DCS;
+ MMModemBand pcs = MM_MODEM_BAND_PCS;
+ MMModemBand g850 = MM_MODEM_BAND_G850;
+ MMModemBand u2100 = MM_MODEM_BAND_UTRAN_1;
+ MMModemBand eutran_i = MM_MODEM_BAND_EUTRAN_1;
+
+ /* Test Flag 0 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, egsm);
+ g_array_append_val (bands_array, dcs);
+ test_common_bnd_cmd_2g ("#BND=0", bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 1 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, egsm);
+ g_array_append_val (bands_array, pcs);
+ test_common_bnd_cmd_2g ("#BND=1", bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 2 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, g850);
+ g_array_append_val (bands_array, dcs);
+ test_common_bnd_cmd_2g ("#BND=2", bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 3 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, g850);
+ g_array_append_val (bands_array, pcs);
+ test_common_bnd_cmd_2g ("#BND=3", bands_array);
+ g_array_unref (bands_array);
+
+ /* Test invalid band array */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, g850);
+ g_array_append_val (bands_array, egsm);
+ test_common_bnd_cmd_2g_invalid (bands_array);
+ g_array_unref (bands_array);
+
+ /* Test unmatched band array */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, u2100);
+ g_array_append_val (bands_array, eutran_i);
+ test_common_bnd_cmd_2g_not_found (bands_array);
+ g_array_unref (bands_array);
+}
+
+static void
+test_telit_get_3g_bnd_flag (void)
+{
+ GArray *bands_array;
+ MMModemBand u2100 = MM_MODEM_BAND_UTRAN_1;
+ MMModemBand u1900 = MM_MODEM_BAND_UTRAN_2;
+ MMModemBand u1800 = MM_MODEM_BAND_UTRAN_3;
+ MMModemBand u850 = MM_MODEM_BAND_UTRAN_5;
+ MMModemBand u800 = MM_MODEM_BAND_UTRAN_6;
+ MMModemBand u900 = MM_MODEM_BAND_UTRAN_8;
+ MMModemBand u17iv = MM_MODEM_BAND_UTRAN_4;
+ MMModemBand u17ix = MM_MODEM_BAND_UTRAN_9;
+ MMModemBand egsm = MM_MODEM_BAND_EGSM;
+ MMModemBand eutran_i = MM_MODEM_BAND_EUTRAN_1;
+
+ /* Test flag 0 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (bands_array, u2100);
+ test_common_bnd_cmd_3g ("#BND=0,0", FALSE, bands_array);
+ test_common_bnd_cmd_3g ("#BND=0,0", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 1 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (bands_array, u1900);
+ test_common_bnd_cmd_3g ("#BND=0,1", FALSE, bands_array);
+ test_common_bnd_cmd_3g ("#BND=0,1", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 2 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (bands_array, u850);
+ test_common_bnd_cmd_3g ("#BND=0,2", FALSE, bands_array);
+ test_common_bnd_cmd_3g ("#BND=0,2", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 3 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 3);
+ g_array_append_val (bands_array, u2100);
+ g_array_append_val (bands_array, u1900);
+ g_array_append_val (bands_array, u850);
+ test_common_bnd_cmd_3g ("#BND=0,3", FALSE, bands_array);
+ test_common_bnd_cmd_3g ("#BND=0,3", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 4 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, u1900);
+ g_array_append_val (bands_array, u850);
+ test_common_bnd_cmd_3g ("#BND=0,4", FALSE, bands_array);
+ test_common_bnd_cmd_3g ("#BND=0,4", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 5 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (bands_array, u900);
+ test_common_bnd_cmd_3g ("#BND=0,5", FALSE, bands_array);
+ test_common_bnd_cmd_3g ("#BND=0,5", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 6 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, u2100);
+ g_array_append_val (bands_array, u900);
+ test_common_bnd_cmd_3g ("#BND=0,6", FALSE, bands_array);
+ test_common_bnd_cmd_3g ("#BND=0,6", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 7 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (bands_array, u17iv);
+ test_common_bnd_cmd_3g ("#BND=0,7", FALSE, bands_array);
+ test_common_bnd_cmd_3g ("#BND=0,7", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 12 in default */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (bands_array, u800);
+ test_common_bnd_cmd_3g ("#BND=0,12", FALSE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 12 in alternate */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 4);
+ g_array_append_val (bands_array, u2100);
+ g_array_append_val (bands_array, u1800);
+ g_array_append_val (bands_array, u850);
+ g_array_append_val (bands_array, u900);
+ test_common_bnd_cmd_3g ("#BND=0,12", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test invalid band array */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (bands_array, u17ix);
+ test_common_bnd_cmd_3g_invalid (bands_array);
+ g_array_unref (bands_array);
+
+ /* Test unmatched band array */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, egsm);
+ g_array_append_val (bands_array, eutran_i);
+ test_common_bnd_cmd_3g_not_found (bands_array);
+ g_array_unref (bands_array);
+}
+
+static void
+test_telit_get_4g_bnd_flag (void)
+{
+ GArray *bands_array;
+ MMModemBand eutran_i = MM_MODEM_BAND_EUTRAN_1;
+ MMModemBand eutran_ii = MM_MODEM_BAND_EUTRAN_2;
+ MMModemBand u2100 = MM_MODEM_BAND_UTRAN_1;
+ MMModemBand egsm = MM_MODEM_BAND_EGSM;
+
+ /* Test flag 1 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (bands_array, eutran_i);
+ test_common_bnd_cmd_4g ("#BND=0,0,1", FALSE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 3 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, eutran_i);
+ g_array_append_val (bands_array, eutran_ii);
+ test_common_bnd_cmd_4g ("#BND=0,0,3", FALSE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test unmatched band array */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, egsm);
+ g_array_append_val (bands_array, u2100);
+ test_common_bnd_cmd_4g_not_found (bands_array);
+ g_array_unref (bands_array);
+}
+
+/******************************************************************************/
+
+typedef struct {
+ const char* response;
+ MMTelitQssStatus expected_qss;
+ const char *error_message;
+} QssParseTest;
+
+static QssParseTest qss_parse_tests [] = {
+ {"#QSS: 0,0", QSS_STATUS_SIM_REMOVED, NULL},
+ {"#QSS: 1,0", QSS_STATUS_SIM_REMOVED, NULL},
+ {"#QSS: 0,1", QSS_STATUS_SIM_INSERTED, NULL},
+ {"#QSS: 0,2", QSS_STATUS_SIM_INSERTED_AND_UNLOCKED, NULL},
+ {"#QSS: 0,3", QSS_STATUS_SIM_INSERTED_AND_READY, NULL},
+ {"#QSS:0,3", QSS_STATUS_SIM_INSERTED_AND_READY, NULL},
+ {"#QSS: 0, 3", QSS_STATUS_SIM_INSERTED_AND_READY, NULL},
+ {"#QSS: 0", QSS_STATUS_UNKNOWN, "Could not parse \"#QSS?\" response: #QSS: 0"},
+ {"QSS:0,1", QSS_STATUS_UNKNOWN, "Could not parse \"#QSS?\" response: QSS:0,1"},
+ {"#QSS: 0,5", QSS_STATUS_UNKNOWN, "Unknown QSS status value given: 5"},
+};
+
+static void
+test_telit_parse_qss_query (void)
+{
+ MMTelitQssStatus actual_qss_status;
+ GError *error = NULL;
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (qss_parse_tests); i++) {
+ actual_qss_status = mm_telit_parse_qss_query (qss_parse_tests[i].response, &error);
+
+ g_assert_cmpint (actual_qss_status, ==, qss_parse_tests[i].expected_qss);
+ if (qss_parse_tests[i].error_message) {
+ g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED);
+ g_assert_cmpstr (error->message, ==, qss_parse_tests[i].error_message);
+ g_clear_error (&error);
+ }
+ }
+}
+
+static void
+test_telit_parse_swpkgv_response (void)
+{
+ static struct {
+ const gchar *response;
+ const gchar *expected;
+ } tt [] = {
+ {"\r\n12.34.567\r\nM0F.223004-B001\r\nP0F.224700\r\nA0F.223004-B001\r\n\r\nOK\r\n", "12.34.567"},
+ {"\r\n13.35.568-A123\r\nM0F.223004-B001\r\nP0F.224700\r\nA0F.223004-B001\r\n\r\nOK\r\n", "13.35.568-A123"},
+ {"\r\n14.36.569-B124\r\nM0F.223004-B001\r\nP0F.224700\r\nA0F.223004-B001\r\n\r\nOK\r\n", "14.36.569-B124"},
+ {"\r\n15.37.570-T125\r\nM0F.223004-B001\r\nP0F.224700\r\nA0F.223004-B001\r\n\r\nOK\r\n", "15.37.570-T125"},
+ {"\r\n16.38.571-P0F.224700\r\nM0F.223004-B001\r\nP0F.224700\r\nA0F.223004-B001\r\n\r\nOK\r\n", "16.38.571-P0F.224700"},
+ /* real example from LE910C1-EUX */
+ {"\r\n25.30.224-B001-P0F.224700\r\nM0F.223004-B001\r\nP0F.224700\r\nA0F.223004-B001\r\n\r\nOK\r\n", "25.30.224-B001-P0F.224700"},
+ };
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (tt); i++) {
+ gchar *actual = NULL;
+
+ actual = mm_telit_parse_swpkgv_response(tt[i].response);
+
+ g_assert_cmpstr (tt[i].expected, ==, actual);
+ g_free (actual);
+ }
+}
+
+static void
+test_telit_compare_software_revision_string (void)
+{
+ struct {
+ const char *revision_a;
+ const char *revision_b;
+ MMTelitSwRevCmp expected;
+ } tt [] = {
+ {"24.01.514", "24.01.514", MM_TELIT_SW_REV_CMP_EQUAL},
+ {"24.01.514", "24.01.513", MM_TELIT_SW_REV_CMP_NEWER},
+ {"24.01.513", "24.01.514", MM_TELIT_SW_REV_CMP_OLDER},
+ {"32.00.013", "24.01.514", MM_TELIT_SW_REV_CMP_INVALID},
+ {"32.00.014", "32.00.014", MM_TELIT_SW_REV_CMP_EQUAL},
+ {"32.00.014", "32.00.013", MM_TELIT_SW_REV_CMP_NEWER},
+ {"32.00.013", "32.00.014", MM_TELIT_SW_REV_CMP_OLDER},
+ {"38.00.000", "38.00.000", MM_TELIT_SW_REV_CMP_UNSUPPORTED},
+ /* LM9x0 Minor version (e.g. beta, test, alpha) value is currently
+ * ignored because not required by any implemented feature. */
+ {"24.01.516-B123", "24.01.516-B134", MM_TELIT_SW_REV_CMP_EQUAL},
+ };
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (tt); i++) {
+ g_assert_cmpint (tt[i].expected,
+ ==,
+ mm_telit_software_revision_cmp (tt[i].revision_a, tt[i].revision_b));
+ }
+}
+
+/******************************************************************************/
+
+int main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/MM/telit/bands/supported/parse_bands_response", test_parse_supported_bands_response);
+ g_test_add_func ("/MM/telit/bands/current/parse_bands_response", test_parse_current_bands_response);
+ g_test_add_func ("/MM/telit/bands/current/set_bands/2g", test_telit_get_2g_bnd_flag);
+ g_test_add_func ("/MM/telit/bands/current/set_bands/3g", test_telit_get_3g_bnd_flag);
+ g_test_add_func ("/MM/telit/bands/current/set_bands/4g", test_telit_get_4g_bnd_flag);
+ g_test_add_func ("/MM/telit/qss/query", test_telit_parse_qss_query);
+ g_test_add_func ("/MM/telit/swpkv/parse_response", test_telit_parse_swpkgv_response);
+ g_test_add_func ("/MM/telit/revision/compare", test_telit_compare_software_revision_string);
+ return g_test_run ();
+}