Merge remote-tracking branch 'cros/upstream' into 'cros/master'

Contains the following commits:
408e9f32 broadband-modem-qmi: fix assertion when disabling unsolicited events (Aleksander Morgado)
c6d213d6 broadband-modem-qmi,bearer-qmi: avoid depending on the client version (Aleksander Morgado)
62956b5c build: post-release version bump to 1.15.0 (Aleksander Morgado)
5f20662a release: bump version to 1.14.0 (Aleksander Morgado)
71d65bbe u-blox: always ignore M8 and M9 GPS modules (Aleksander Morgado)
fbf38753 blacklist: add chinese clone of Arduino nano (Aleksander Morgado)
5cbeeb1b build: require libqmi 1.26.0 (Aleksander Morgado)
57422ec0 NEWS: add info about MBIM-powered Cinterion devices (Aleksander Morgado)
ecf54efc cinterion: added port type hints for the mPLS62-w in MBIM mode (Aleksander Morgado)
c2ee7317 cinterion: allow MBIM-powered devices (Aleksander Morgado)
54de4192 build: require libmbim 1.24.0 (Aleksander Morgado)
9d31cbaa mm-kernel-device: give cmp a total order (Eric Caruso)
12ea7281 AUTHORS: update with latest commit stats (Aleksander Morgado)
cb8d8105 quectel: add port type hints for EG95 (Brendan Peter)
569990f3 broadband-modem-qmi: also build 5GS reg state from Serving System (Aleksander Morgado)
8340237f broadband-modem-qmi: avoid using stale EPS registration state (Aleksander Morgado)
3af93cbe broadband-modem-qmi: switch USSD state to idle when ussd session is terminated by network (Maxim Anisimov)
62c6f941 cli: make control chars in strings are escaped correctly for json output (Maxim Anisimov)
1e0b38d9 sms-part-3gpp:  fix unicode names in sms decode iconv() operations (Maxim Anisimov)
cbcc1411 build: bump version to 1.13.900 (1.14-rc1) (Aleksander Morgado)
cd501c4d NEWS: add details about the plugin configure options (Aleksander Morgado)
a5c060ef filter: setup automatic per-vid checks in the plugin whitelist (Aleksander Morgado)
bbeca601 ublox: fully ignore GPS devices with plugin-installed rules (Aleksander Morgado)
657d5e61 NEWS: update MBIM reset operation info to include Qualcomm devices (Aleksander Morgado)
1ef46cdf build: require libmbim 1.24-rc1 (Aleksander Morgado)
30428ed5 NEWS: update for 1.14.0 (Aleksander Morgado)
b8110197 docs,libmm-glib: add 1.14 index (Aleksander Morgado)
d36df682 wavecom: break loop matching 2G bands if exact combination found (Aleksander Morgado)
a6607a5e wavecom: port to use g_autoptr() setup (Aleksander Morgado)
08d40fe0 wavecom: fix GTask return handling when loading current caps (Aleksander Morgado)
d8b70602 huawei: ignore NDISDUP disconnection errors (Aleksander Morgado)
472fb96b huawei: don't delay reporting network initiated disconnects (Aleksander Morgado)
15543794 filter: 'strict' is the new default (Aleksander Morgado)
82d38f70 filter: rename 'default' to 'legacy' (Aleksander Morgado)
6eca2237 shared-qmi: require minimum set of NMEA types before starting GNSS engine (Aleksander Morgado)
fd28f947 ci: always build libqmi with basic collection (Aleksander Morgado)
10648f8d broadband-modem-qmi: plug memleak when changing current firmware (Aleksander Morgado)
d848b10d iface-modem-firmware: plug memleak when listing images (Aleksander Morgado)
3b35bbd4 huawei: fix memory leak when parsing HCSQ response (Louis-Alexis Eyraud)
df36f8d2 cinterion: fix syntax error for setting radio/bands/[23]g (Giacinto Cifelli)
3efe127a iface-modem-voice: fix assertions in call setup/teardown logic (Aleksander Morgado)
0cc5a6ce cinterion: skip sim ready check for modules that don't support it (Giacinto Cifelli)
da7bc39f blacklist: add USB CEC adapters (Aleksander Morgado)
84ec3d2f cinterion: simplify check (Giacinto Cifelli)
502c6365 zte: add MF667 port type hints (Aleksander Morgado)
a399c7e4 charsets: take_and_convert() methods should support GSM encoding (Aleksander Morgado)
c9945769 charsets,test: extend UCS2->UTF-8 testing for the reverse operation (Aleksander Morgado)
c4e6a0c4 charsets: don't warn in unlikely case of needing to convert to HEX from UTF-8 (Aleksander Morgado)
87b8c9b2 cinterion: minor coding style fixes in new band management (Aleksander Morgado)
793132d5 cinterion: radio/band handling for LTE modems (Giacinto Cifelli)
09260311 Revert "charsets: don't warn in unlikely case of needing to convert to HEX from UTF-8" (Aleksander Morgado)
6a7dd87f charsets: don't warn in unlikely case of needing to convert to HEX from UTF-8 (Giacinto Cifelli)
15a941fa broadband-modem-qmi: only use "Data Service Capabilities" array if content given (Aleksander Morgado)
29a68c94 huawei: ignore ^CCALLSTATE URCs for now (Aleksander Morgado)
1eeec40e iface-modem-voice: fix assert() when setting up call polling (Aleksander Morgado)
98fa83a6 cinterion,tests: avoid cast-align errors (Aleksander Morgado)
b71ae9ce wavecom: avoid cast-align errors (Aleksander Morgado)
8bf13294 libmm-glib,helpers: use locale-independent strtod() (Aleksander Morgado)
58ae8f4c cinterion,helpers: minor coding style fix (Aleksander Morgado)
9bfc8a78 novatel-lte: use GPtrArray to build a GStrv (Aleksander Morgado)
a2b0cee9 huawei,helpers: fix warnings with -Wcast-align (Aleksander Morgado)
bd2e6f5d huawei: avoid cast-align errors (Aleksander Morgado)
175bedf9 xmm: avoid cast-align errors (Aleksander Morgado)
ccf16e00 ublox,helpers: avoid cast-align errors (Aleksander Morgado)
76d06ce6 cinterion,helpers: avoid cast-align errors (Aleksander Morgado)
b0be4e89 telit,helpers: avoid cast-align errors (Aleksander Morgado)
301b6f0a plugins,tests: avoid cast-align errors (Aleksander Morgado)
c7dee320 test-qcdm-serial-port: fix warnings with -Wsign-compare (Aleksander Morgado)
71d8bb2e iface-modem-messaging: avoid cast-align errors (Aleksander Morgado)
ffbbec4a iface-modem-location: avoid warnings with -Wsign-compare (Aleksander Morgado)
7ccec0bd iface-modem: avoid cast-align errors (Aleksander Morgado)
3476e140 modem-helpers: use GPtrArray to build a GStrv (Aleksander Morgado)
8e033ba3 AT modem: charset definition in init (Giacinto Cifelli)
503581a3 port-qmi: plug memleak when explicitly releasing client (Aleksander Morgado)
fae614be udev rules for the PLS62 in 005b enumeration (Giacinto Cifelli)
6b36fe9f huawei: NDISDUP based devices may use plain TTYs for control (Aleksander Morgado)
f109b528 plugins/cinterion: added Signal interface (Giacinto Cifelli)
8b7bdea7 telit: flag GPS port in the LE910C1 (Aleksander Morgado)
9c77fb88 telit: add LE910C1 udev rule (David Khouya)
bbd39a45 configure.ac: check for xsltproc (Giacinto Cifelli)
d33a883b build: require libmbim 1.23.1 for autoptr support (Aleksander Morgado)
26b589cc broadmobi: new plugin (Aleksander Morgado)
0ddadbdc port: use correct enum to string conversion when logging (Aleksander Morgado)
83952c5a bearer-qmi: get correct bearer object pointer from task (Aleksander Morgado)

Cq-Depend: chromium:2274871,chromium:2274872
Change-Id: Iad3e97cca9fe6c33bd0ec063b719b99332c75da1
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 24b9009..8c3e7b1 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -39,7 +39,7 @@
     - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libqmi.git
     - pushd libqmi
     - NOCONFIGURE=1 ./autogen.sh
-    - ./configure --prefix=/usr --disable-mbim-qmux
+    - ./configure --prefix=/usr --disable-mbim-qmux --enable-collection=basic
     - make
     - make install
     - popd
@@ -78,14 +78,14 @@
     - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libqmi.git
     - pushd libqmi
     - NOCONFIGURE=1 ./autogen.sh
-    - ./configure --prefix=/usr --enable-mbim-qmux
+    - ./configure --prefix=/usr --enable-mbim-qmux --enable-collection=basic
     - make
     - make install
     - popd
     - NOCONFIGURE=1 ./autogen.sh
-    - for plugin in generic altair-lte anydata cinterion dell dlink
-                    fibocom foxconn haier huawei iridium linktop
-                    longcheer mbm motorola mtk nokia nokia-icera
+    - for plugin in generic altair-lte anydata broadmobi cinterion
+                    dell dlink fibocom foxconn haier huawei iridium
+                    linktop longcheer mbm motorola mtk nokia nokia-icera
                     novatel novatel-lte option option-hso pantech
                     quectel samsung sierra-legacy sierra simtech
                     telit thuraya tplink ublox via wavecom x22x zte; do
@@ -115,7 +115,7 @@
     - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libqmi.git
     - pushd libqmi
     - NOCONFIGURE=1 ./autogen.sh
-    - ./configure --prefix=/usr --enable-mbim-qmux
+    - ./configure --prefix=/usr --enable-mbim-qmux --enable-collection=basic
     - make
     - make install
     - popd
diff --git a/AUTHORS b/AUTHORS
index 06aeb98..a7b93cd 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -17,13 +17,16 @@
   Riccardo Vangelisti
   Alexander Sack
   Guido Günther
+  Eric Caruso
   Eric Shienbrood
   Elly Jones
-  Eric Caruso
+  Bob Ham
+  Giacinto Cifelli
   David McCullough
   Prathmesh Prabhu
   Thieu Le
   Thomas Tuttle
+  Maxim Anisimov
   Bjørn Mork
   Marius B. Kotsbak
   Michael Biebl
@@ -33,7 +36,6 @@
   Christian Persch
   Sven Schwermer
   Thomas Sailer
-  Bob Ham
   Colin Walters
   Franko Fang
   Jakub Sitnicki
@@ -49,6 +51,7 @@
   Martyn Russell
   Thomas Haller
   Tomas Jura
+  Amol Lad
   Colin Helliwell
   Graham Inggs
   Javier Viguera
@@ -56,15 +59,17 @@
   Maksim Salau
   Martin Pitt
   Matthew Stanger
-  Maxim Anisimov
+  Milo Casagrande
   Nick Stevens
   Norbert Frese
   Piotr Drąg
   Thomas Bechtold
   Yegor Yefremov
+  Yuri Chornoivan
   Adrian Bunk
   Alexander Couzens
   Amit Mendapara
+  Andika Triwidada
   Andrew Bird
   Anton Blanchard
   Arnd Hannemann
@@ -72,6 +77,7 @@
   Arun Raghavan
   Baruch Siach
   Beniamino Galvani
+  Brendan Peter
   Brian Norris
   Brian, Sam
   Bryan Duff
@@ -80,6 +86,7 @@
   David Castellanos
   David Herrmann
   David Härdeman
+  David Khouya
   Dmitry Ivanyushin
   Enrico Mioso
   Eugene Crosser
@@ -99,6 +106,7 @@
   Krzysztof Kotlenga
   Lech Perczak
   LiuQiFeng
+  Louis-Alexis Eyraud
   Luis A. Lozano
   M. I. Spozta
   Marc Murphy
@@ -107,18 +115,20 @@
   Mark-Jablonsky
   Michael Farrell
   Michał Sroczyński
-  Milo Casagrande
   Mohammed Sadiq
   Moo
+  Murithi Borona
   Nathan J. Williams
   Noel J. Bergman
   Oskar Enoksson
   Piotr Figiel
   Quentin.Li
+  Rafael Fontenelle
   Roshan Pius
   Sam Spilsbury
   Sławomir Bocheński
   Tabor Kelly
+  Teijo Kinnunen
   Thomas Grenman
   Thomas Voß
   Ting-Yuan Huang
@@ -131,10 +141,13 @@
   Ville Skyttä
   Vincent Untz
   Vitaly Gimly
-  Yuri Chornoivan
   Amol Lad
+  emintufan
   kuonirat
+  mozzwald
+  mstanger
   poma
   scootergrisen
   wi24rd
   yparitcher
+  Артемий Судаков
diff --git a/NEWS b/NEWS
index b13950f..a57854b 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,144 @@
 
+ModemManager 1.14.0
+-------------------------------------------
+This is a new stable release of ModemManager.
+
+The following notes are directed to package maintainers:
+
+ * This version requires:
+   ** GLib/GObject/GIO >= 2.48.0
+   ** libmbim >= 1.24.0 (for the optional MBIM support)
+   ** libqmi >= 1.26.0 (for the optional QMI support)
+
+ * Build updated with several improvements:
+   ** The build has been updated to use by default all warnings enabled by
+      AX_COMPILER_FLAGS(), and therefore when building the release from a git
+      checkout, autoconf-archive >= 2017.03.21 is now required. This new build
+      dependency isn't required when building from the release tarball.
+   ** Also when building from a git checkout, beware because by default
+      --enable-compile-warnings=error is enabled, which implies -Werror. If
+      you'd like to build from git and avoid -Werror, you should explicitly use
+      --enable-compile-warnings=yes (to keep the warnings but without being
+      errors), --enable-compile-warnings=no (to disable all the extra warnings
+      enabled by default) or --disable-Werror (to unconditionally make all
+      compiler warnings non-fatal).
+   ** Users can now preselect which plugins to build and install during
+      configure, with the --enable-plugin-[PLUGIN-NAME] options. The user can
+      also build and install all except for some, just by using the
+      --enable-all-plugins option followed by --disable-plugin-[PLUGIN-NAME].
+      By default all plugins are enabled, so all of them built and installed.
+      This new set of options are useful for users building custom systems
+      where they already know what kind of devices they're going to have, it
+      isn't recommended for standard distributions.
+
+The only updates in the public API are the following:
+
+ * Modem interface:
+   ** Added support for AT-based and/or QMI-based 5G devices, which will report
+      the new MM_MODEM_CAPABILITY_5GNR capability.
+   ** Deprecated the MM_MODEM_CAPABILITY_LTE_ADVANCED capability, as it was
+      never used in any implementation.
+
+ * Bearer interface:
+   ** Added additional 'attempts', 'failed-attempts', 'total-rx-bytes',
+      'total-tx-bytes' and 'total-duration' values in the 'Stats' property
+      exposed by the Bearer objects.
+
+The most important features and changes in this release are the following:
+
+ * The daemon switched to 'STRICT' filter mode by default. The old 'DEFAULT'
+   mode is renamed to 'LEGACY' and is considered now deprecated. Under the
+   'STRICT' filter mode, the TTY blacklist and greylist are ignored, and the
+   port probing mechanism uses its own heuristics to guess whether a given TTY
+   is owned by a modem or not.
+
+ * Added a new implicit whitelist rules applicable in 'STRICT' filter mode, so
+   that all devices with a USB vid matching one of the vids allowed by the
+   different installed plugins are implicitly allowed.
+
+ * Updated daemon logging so that we always prefix the messages with a string
+   identifying which modem the log refers to, making it easy to grep logs for
+   one specific device if the system has more than one.
+
+ * Updated the probing logic to make sure we don't attempt a re-probe when the
+   device is gone.
+
+ * Probing logic now allows new ports detected up to 1500ms since last port
+   added, useful on OpenWrt setups where ports are notified one by one via
+   hotplug events.
+
+ * AT:
+   ** Moved the charset definition logic to the initialization phase instead of
+      the enabling phase, because the feature support checks may already require
+      string processing based on the current charset.
+   ** Updated manual registration operation to attempt using current charset
+      (e.g. UCS2) if ASCII fails.
+
+ * QMI:
+   ** Devices using the LOC service for GNSS will now also setup the list of
+      required NMEA traces before starting the engine.
+   ** Implemented 3GPP USSD support using the Voice service.
+   ** Update carrier code if registration changes from one roaming operator to
+      another.
+   ** Explicitly disable autoconnect during modem enabling phase, because it
+      interferes with our connection management logic.
+   ** Fallback to raw-ip if WDA Get Data Format requests arguments, as in most
+      new 5G devices.
+   ** Updated to always use the asynchronous close() operation.
+   ** Handle disconnection indications during connection attempts.
+
+ * MBIM:
+   ** Update carrier code if registration changes from one roaming operator to
+      another.
+   ** Implement reset in Intel-based and Qualcomm-based devices.
+   ** Avoid LTE attach config/status if unsupported.
+   ** Updated to make sure all allocated QMI CIDs are released during shutdown.
+
+ * SIM interface:
+   ** Don't allow sending PIN/PUK if not required.
+
+ * 3GPP interface:
+   ** Fixed manual re-registration to the same operator.
+
+ * CDMA interface:
+   ** Don't allow multiple concurrent activation attempts.
+   ** Disallow empty carrier code in automatic activation.
+
+ * Bearer interface:
+   ** Updated to avoid connection checks or stats updates while disconnecting.
+
+ * libmm-glib:
+   ** New 'mm_location_gps_nmea_get_traces()' method to retrieve a NULL
+      terminated array of strings with all cached NMEA traces.
+   ** Deprecated the 'mm_location_gps_nmea_build_full()' method.
+
+ * mmcli:
+   ** Added a new 'any' lookup keyword for the --modem and --sim options, useful
+      when the system is only expected to have one single device.
+
+ * Plugins:
+   ** broadmobi: new plugin, right now just with port type hints for the BM818.
+   ** foxconn: new plugin to support the T77W968 (both with and without eSIM).
+   ** dell,dw5821e: added support for the DW5821e with eSIM variant.
+   ** huawei: don't delay reporting network initiated disconnects.
+   ** huawei: try to read port type hints from interface descriptions.
+   ** huawei: avoid using the QCDM port during a voice call.
+   ** cinterion: skip sim ready check for modules that don't support it.
+   ** cinterion: implemented radio/band handling for LTE modems.
+   ** cinterion: added Signal interface support bsaed on AT^SMONI.
+   ** cinterion: added support for MBIM based devices like the PLS62-W.
+   ** quectel: updated to detect SIM hot swap via +QUSIM URCs.
+   ** fibocom: added support for QMI based devices like the FM150.
+   ** ublox: ignore error when disconnecting last LTE bearer.
+   ** ublox: implement support to enable and detect +UUDTMF URCs.
+   ** ublox: added blacklist rules for GPS modules in the plugin itself.
+   ** sierra: implement manual and automatic CDMA activation.
+   ** novatel: implement manual and automatic CDMA activation.
+
+All the features and fixes which were backported to 1.12.x releases are also
+present in ModemManager 1.14.0.
+
+
 ModemManager 1.12.0
 -------------------------------------------
 This is a new stable release of ModemManager.
diff --git a/cli/mmcli-output.c b/cli/mmcli-output.c
index 6799ab6..1265856 100644
--- a/cli/mmcli-output.c
+++ b/cli/mmcli-output.c
@@ -1087,6 +1087,49 @@
 /******************************************************************************/
 /* JSON-friendly output */
 
+static gchar *
+json_strescape (const gchar *str)
+{
+    const gchar *p;
+    const gchar *end;
+    GString *output;
+    gsize len;
+
+    len = strlen (str);
+    end = str + len;
+    output = g_string_sized_new (len);
+
+    for (p = str; p < end; p++) {
+        if (*p == '\\' || *p == '"') {
+            g_string_append_c (output, '\\');
+            g_string_append_c (output, *p);
+        } else if ((*p > 0 && *p < 0x1f) || *p == 0x7f) {
+            switch (*p) {
+                case '\b':
+                    g_string_append (output, "\\b");
+                    break;
+                case '\f':
+                    g_string_append (output, "\\f");
+                    break;
+                case '\n':
+                    g_string_append (output, "\\n");
+                    break;
+                case '\r':
+                    g_string_append (output, "\\r");
+                    break;
+                case '\t':
+                    g_string_append (output, "\\t");
+                    break;
+                default:
+                    g_string_append_printf (output, "\\u00%02x", (guint)*p);
+                    break;
+            }
+        } else
+            g_string_append_c (output, *p);
+    }
+    return g_string_free (output, FALSE);
+}
+
 static gint
 list_sort_by_keys (const OutputItem *item_a,
                    const OutputItem *item_b)
@@ -1146,7 +1189,7 @@
             gchar            *escaped = NULL;
 
             if (single->value)
-                escaped = g_strescape (single->value, "\v");
+                escaped = json_strescape (single->value);
 
             g_print ("\"%s\":\"%s\"", current_path[cur_dlen], escaped ? escaped : "--");
             g_free (escaped);
@@ -1160,7 +1203,7 @@
             for (i = 0; i < n; i++) {
                 gchar *escaped;
 
-                escaped = g_strescape (multiple->values[i], "\v");
+                escaped = json_strescape (multiple->values[i]);
                 g_print("\"%s\"", escaped);
                 if (i < n - 1)
                     g_print(",");
diff --git a/configure.ac b/configure.ac
index e9883ad..3495cee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5,7 +5,7 @@
 dnl
 
 m4_define([mm_major_version], [1])
-m4_define([mm_minor_version], [13])
+m4_define([mm_minor_version], [15])
 m4_define([mm_micro_version], [0])
 m4_define([mm_version],
           [mm_major_version.mm_minor_version.mm_micro_version])
@@ -18,9 +18,9 @@
 dnl            with old code), increment a.
 dnl        If the interface has changed in an incompatible way (that is,
 dnl            functions have changed or been removed), then zero a.
-m4_define([mm_glib_lt_current],  [5])
+m4_define([mm_glib_lt_current],  [6])
 m4_define([mm_glib_lt_revision], [0])
-m4_define([mm_glib_lt_age],      [5])
+m4_define([mm_glib_lt_age],      [6])
 
 dnl-----------------------------------------------------------------------------
 dnl autoconf, automake, libtool initialization
@@ -46,6 +46,9 @@
 AC_PROG_INSTALL
 AC_PROG_MKDIR_P
 
+AC_CHECK_PROG(XSLTPROC_CHECK,xsltproc,yes)
+AS_IF([test x"$XSLTPROC_CHECK" != x"yes"], [AC_MSG_ERROR([Please install xsltproc before configuring.])])
+
 dnl Initialize libtool
 LT_PREREQ([2.2])
 LT_INIT([disable-static])
@@ -367,7 +370,7 @@
 dnl MBIM support (enabled by default)
 dnl
 
-LIBMBIM_VERSION=1.18.0
+LIBMBIM_VERSION=1.24.0
 
 AC_ARG_WITH(mbim, AS_HELP_STRING([--without-mbim], [Build without MBIM support]), [], [with_mbim=yes])
 AM_CONDITIONAL(WITH_MBIM, test "x$with_mbim" = "xyes")
@@ -391,7 +394,7 @@
 dnl QMI support (enabled by default)
 dnl
 
-LIBQMI_VERSION=1.25.5
+LIBQMI_VERSION=1.26.0
 
 AC_ARG_WITH(qmi, AS_HELP_STRING([--without-qmi], [Build without QMI support]), [], [with_qmi=yes])
 AM_CONDITIONAL(WITH_QMI, test "x$with_qmi" = "xyes")
@@ -448,6 +451,7 @@
 MM_ENABLE_PLUGIN([generic])
 MM_ENABLE_PLUGIN([altair-lte])
 MM_ENABLE_PLUGIN([anydata])
+MM_ENABLE_PLUGIN([broadmobi])
 MM_ENABLE_PLUGIN([cinterion])
 MM_ENABLE_PLUGIN([dell],
                  [with_shared_sierra,
@@ -592,6 +596,7 @@
       generic:                 ${enable_plugin_generic}
       altair lte:              ${enable_plugin_altair_lte}
       anydata:                 ${enable_plugin_anydata}
+      broadmobi:               ${enable_plugin_broadmobi}
       cinterion:               ${enable_plugin_cinterion}
       dell:                    ${enable_plugin_dell}
       dlink:                   ${enable_plugin_dlink}
diff --git a/docs/reference/libmm-glib/libmm-glib-docs.xml b/docs/reference/libmm-glib/libmm-glib-docs.xml
index 106651b..4fe7c6c 100644
--- a/docs/reference/libmm-glib/libmm-glib-docs.xml
+++ b/docs/reference/libmm-glib/libmm-glib-docs.xml
@@ -287,6 +287,10 @@
     <title>Index of new symbols in 1.12</title>
     <xi:include href="xml/api-index-1.12.xml"></xi:include>
   </chapter>
+  <chapter id="api-index-1-14" role="1.14">
+    <title>Index of new symbols in 1.14</title>
+    <xi:include href="xml/api-index-1.14.xml"></xi:include>
+  </chapter>
 
   <xi:include href="xml/annotation-glossary.xml"></xi:include>
 </book>
diff --git a/libmm-glib/mm-common-helpers.c b/libmm-glib/mm-common-helpers.c
index d2c54a1..da1cc1c 100644
--- a/libmm-glib/mm-common-helpers.c
+++ b/libmm-glib/mm-common-helpers.c
@@ -1565,7 +1565,7 @@
         return FALSE;
 
     errno = 0;
-    num = strtod (str, NULL);
+    num = g_ascii_strtod (str, NULL);
     if (!errno) {
         *out = num;
         return TRUE;
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 15ba237..6f83ad6 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -537,6 +537,29 @@
 endif
 
 ################################################################################
+# plugin: broadmobi
+################################################################################
+
+if ENABLE_PLUGIN_BROADMOBI
+
+pkglib_LTLIBRARIES += libmm-plugin-broadmobi.la
+libmm_plugin_broadmobi_la_SOURCES = \
+	broadmobi/mm-plugin-broadmobi.c \
+	broadmobi/mm-plugin-broadmobi.h \
+	$(NULL)
+libmm_plugin_broadmobi_la_CPPFLAGS = \
+	$(PLUGIN_COMMON_COMPILER_FLAGS) \
+	-DMM_MODULE_NAME=\"broadmobi\" \
+	$(NULL)
+libmm_plugin_broadmobi_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+dist_udevrules_DATA += broadmobi/77-mm-broadmobi-port-types.rules
+
+AM_CFLAGS += -DTESTUDEVRULESDIR_BROADMOBI=\"${srcdir}/broadmobi\"
+
+endif
+
+################################################################################
 # plugin: cinterion (previously siemens)
 ################################################################################
 
diff --git a/plugins/broadmobi/77-mm-broadmobi-port-types.rules b/plugins/broadmobi/77-mm-broadmobi-port-types.rules
new file mode 100644
index 0000000..863cb74
--- /dev/null
+++ b/plugins/broadmobi/77-mm-broadmobi-port-types.rules
@@ -0,0 +1,16 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change|move|bind", GOTO="mm_broadmobi_port_types_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="2020", GOTO="mm_broadmobi_port_types"
+GOTO="mm_broadmobi_port_types_end"
+
+LABEL="mm_broadmobi_port_types"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
+# BroadMobi BM818
+ATTRS{idVendor}=="2020", ATTRS{idProduct}=="2060", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="2020", ATTRS{idProduct}=="2060", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="2020", ATTRS{idProduct}=="2060", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="2020", ATTRS{idProduct}=="2060", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+LABEL="mm_broadmobi_port_types_end"
\ No newline at end of file
diff --git a/plugins/broadmobi/mm-plugin-broadmobi.c b/plugins/broadmobi/mm-plugin-broadmobi.c
new file mode 100644
index 0000000..c6a5782
--- /dev/null
+++ b/plugins/broadmobi/mm-plugin-broadmobi.c
@@ -0,0 +1,95 @@
+/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <string.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-port-enums-types.h"
+#include "mm-log-object.h"
+#include "mm-plugin-broadmobi.h"
+#include "mm-broadband-modem.h"
+
+#if defined WITH_QMI
+# include "mm-broadband-modem-qmi.h"
+#endif
+
+G_DEFINE_TYPE (MMPluginBroadmobi, mm_plugin_broadmobi, 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,
+              GList        *probes,
+              GError      **error)
+{
+#if defined WITH_QMI
+    if (mm_port_probe_list_has_qmi_port (probes)) {
+        mm_obj_dbg (self, "QMI-powered BroadMobi modem found...");
+        return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
+                                                          drivers,
+                                                          mm_plugin_get_name (self),
+                                                          vendor,
+                                                          product));
+    }
+#endif
+
+    return MM_BASE_MODEM (mm_broadband_modem_new (uid,
+                                                  drivers,
+                                                  mm_plugin_get_name (self),
+                                                  vendor,
+                                                  product));
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+    static const gchar *subsystems[] = { "tty", "net", "usb", NULL };
+    static const guint16 vendor_ids[] = { 0x2020, 0 };
+
+    return MM_PLUGIN (
+        g_object_new (MM_TYPE_PLUGIN_BROADMOBI,
+                      MM_PLUGIN_NAME,               MM_MODULE_NAME,
+                      MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+                      MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
+                      MM_PLUGIN_ALLOWED_AT,         TRUE,
+                      MM_PLUGIN_ALLOWED_QCDM,       TRUE,
+                      MM_PLUGIN_ALLOWED_QMI,        TRUE,
+                      NULL));
+}
+
+static void
+mm_plugin_broadmobi_init (MMPluginBroadmobi *self)
+{
+}
+
+static void
+mm_plugin_broadmobi_class_init (MMPluginBroadmobiClass *klass)
+{
+    MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+    plugin_class->create_modem = create_modem;
+}
diff --git a/plugins/broadmobi/mm-plugin-broadmobi.h b/plugins/broadmobi/mm-plugin-broadmobi.h
new file mode 100644
index 0000000..1f46cfc
--- /dev/null
+++ b/plugins/broadmobi/mm-plugin-broadmobi.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) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_PLUGIN_BROADMOBI_H
+#define MM_PLUGIN_BROADMOBI_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_BROADMOBI            (mm_plugin_broadmobi_get_type ())
+#define MM_PLUGIN_BROADMOBI(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_BROADMOBI, MMPluginBroadmobi))
+#define MM_PLUGIN_BROADMOBI_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  MM_TYPE_PLUGIN_BROADMOBI, MMPluginBroadmobiClass))
+#define MM_IS_PLUGIN_BROADMOBI(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_BROADMOBI))
+#define MM_IS_PLUGIN_BROADMOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_PLUGIN_BROADMOBI))
+#define MM_PLUGIN_BROADMOBI_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_PLUGIN_BROADMOBI, MMPluginBroadmobiClass))
+
+typedef struct {
+    MMPlugin parent;
+} MMPluginBroadmobi;
+
+typedef struct {
+    MMPluginClass parent;
+} MMPluginBroadmobiClass;
+
+GType mm_plugin_broadmobi_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_BROADMOBI_H */
diff --git a/plugins/cinterion/77-mm-cinterion-port-types.rules b/plugins/cinterion/77-mm-cinterion-port-types.rules
index e21a1b1..f5ceee3 100644
--- a/plugins/cinterion/77-mm-cinterion-port-types.rules
+++ b/plugins/cinterion/77-mm-cinterion-port-types.rules
@@ -20,4 +20,28 @@
 ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
 ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="08", ENV{ID_MM_PORT_IGNORE}="1"
 
+# PLS62 family non-mbim enumeration uses alternate settings for 2G band management
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{ID_MM_CINTERION_MODEM_FAMILY}="imt"
+# PLS62 family non-mbim enumeration
+#  ttyACM0 (if #0): AT port
+#  ttyACM1 (if #2): AT port
+#  ttyACM2 (if #4): unknown
+#  ttyACM3 (if #6): unknown
+#  ttyACM4 (if #8): unknown
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="08", ENV{ID_MM_PORT_IGNORE}="1"
+
+# PLS62 family mbim enumeration
+#  ttyACM0 (if #0): AT port
+#  ttyACM1 (if #2): AT port
+#  ttyACM2 (if #4): AT port
+#  ttyACM3 (if #6): unknown
+#  ttyACM4 (if #8): unknown
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="08", ENV{ID_MM_PORT_IGNORE}="1"
+
 LABEL="mm_cinterion_port_types_end"
diff --git a/plugins/cinterion/mm-broadband-modem-cinterion.c b/plugins/cinterion/mm-broadband-modem-cinterion.c
index 09b4e13..9987521 100644
--- a/plugins/cinterion/mm-broadband-modem-cinterion.c
+++ b/plugins/cinterion/mm-broadband-modem-cinterion.c
@@ -40,6 +40,7 @@
 #include "mm-modem-helpers-cinterion.h"
 #include "mm-shared-cinterion.h"
 #include "mm-broadband-bearer-cinterion.h"
+#include "mm-iface-modem-signal.h"
 
 static void iface_modem_init           (MMIfaceModem          *iface);
 static void iface_modem_3gpp_init      (MMIfaceModem3gpp      *iface);
@@ -47,6 +48,7 @@
 static void iface_modem_location_init  (MMIfaceModemLocation  *iface);
 static void iface_modem_voice_init     (MMIfaceModemVoice     *iface);
 static void iface_modem_time_init      (MMIfaceModemTime      *iface);
+static void iface_modem_signal_init    (MMIfaceModemSignal    *iface);
 static void shared_cinterion_init      (MMSharedCinterion     *iface);
 
 static MMIfaceModem         *iface_modem_parent;
@@ -54,6 +56,7 @@
 static MMIfaceModemLocation *iface_modem_location_parent;
 static MMIfaceModemVoice    *iface_modem_voice_parent;
 static MMIfaceModemTime     *iface_modem_time_parent;
+static MMIfaceModemSignal   *iface_modem_signal_parent;
 
 G_DEFINE_TYPE_EXTENDED (MMBroadbandModemCinterion, mm_broadband_modem_cinterion, MM_TYPE_BROADBAND_MODEM, 0,
                         G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
@@ -62,6 +65,7 @@
                         G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
                         G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init)
                         G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)
+                        G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIGNAL, iface_modem_signal_init)
                         G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_CINTERION, shared_cinterion_init))
 
 typedef enum {
@@ -75,7 +79,7 @@
     gchar *sleep_mode_cmd;
 
     /* Cached supported bands in Cinterion format */
-    guint supported_bands;
+    guint supported_bands[MM_CINTERION_RB_BLOCK_N];
 
     /* Cached supported modes for SMS setup */
     GArray *cnmi_supported_mode;
@@ -90,6 +94,15 @@
     /* Flags for feature support checks */
     FeatureSupport swwan_support;
     FeatureSupport sind_psinfo_support;
+    FeatureSupport smoni_support;
+    FeatureSupport sind_simstatus_support;
+
+    /* Flags for model-based behaviors */
+    MMCinterionModemFamily modem_family;
+    MMCinterionRadioBandFormat rb_format;
+
+    /* Command sequence */
+    MMBaseModemAtCommandAlloc *cmds;
 };
 
 /*****************************************************************************/
@@ -762,15 +775,14 @@
 
     self = MM_BROADBAND_MODEM_CINTERION (_self);
     if (!(response = mm_base_modem_at_command_finish (_self, res, &error))) {
+        /* something went wrong, disable indicator */
         self->priv->sind_psinfo_support = FEATURE_NOT_SUPPORTED;
         mm_obj_warn (self, "couldn't enable ^SIND psinfo notifications: %s", error->message);
     } else if (!mm_cinterion_parse_sind_response (response, NULL, &mode, &val, &error)) {
+        /* problem with parsing, disable indicator */
         self->priv->sind_psinfo_support = FEATURE_NOT_SUPPORTED;
         mm_obj_warn (self, "couldn't parse ^SIND psinfo response: %s", error->message);
     } else {
-        /* Flag ^SIND psinfo supported so that we don't poll */
-        self->priv->sind_psinfo_support = FEATURE_SUPPORTED;
-
         /* Report initial access technology gathered right away */
         mm_obj_dbg (self, "reporting initial access technologies...");
         mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self),
@@ -795,7 +807,7 @@
     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);
 
-    if (self->priv->sind_psinfo_support != FEATURE_NOT_SUPPORTED) {
+    if (self->priv->sind_psinfo_support == FEATURE_SUPPORTED) {
         /* Enable access technology update reporting */
         mm_base_modem_at_command (MM_BASE_MODEM (self),
                                   "AT^SIND=\"psinfo\",1",
@@ -1145,27 +1157,61 @@
     response = mm_base_modem_at_command_finish (_self, res, &error);
     if (!response ||
         !mm_cinterion_parse_scfg_test (response,
+                                       self->priv->modem_family,
                                        mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
                                        &bands,
+                                       &self->priv->rb_format,
                                        &error))
         g_task_return_error (task, error);
     else {
-        mm_cinterion_build_band (bands, 0, FALSE, &self->priv->supported_bands, NULL);
-        g_assert (self->priv->supported_bands != 0);
-        g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
+        if (!mm_cinterion_build_band (bands,
+                                      NULL,
+                                      FALSE,
+                                      self->priv->rb_format,
+                                      self->priv->modem_family,
+                                      self->priv->supported_bands,
+                                      &error))
+            g_task_return_error (task, error);
+        else
+            g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
     }
     g_object_unref (task);
 }
 
 static void
-load_supported_bands (MMIfaceModem        *self,
+load_supported_bands (MMIfaceModem        *_self,
                       GAsyncReadyCallback  callback,
                       gpointer             user_data)
 {
-    GTask *task;
+    MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+    GTask          *task;
+    MMPort         *primary;
+    MMKernelDevice *port;
+    const gchar    *family = NULL;
 
-    task = g_task_new (self, NULL, callback, user_data);
-    mm_base_modem_at_command (MM_BASE_MODEM (self),
+    /* Lookup for the tag specifying which modem family the current device belongs */
+    primary = MM_PORT (mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)));
+    port = mm_port_peek_kernel_device (primary);
+    family = mm_kernel_device_get_global_property (port, "ID_MM_CINTERION_MODEM_FAMILY");
+
+    /* if the property is not set, default family */
+    self->priv->modem_family = MM_CINTERION_MODEM_FAMILY_DEFAULT;
+
+    /* set used family also in the string for mm_obj_dbg */
+    if (!family)
+        family = "default";
+
+    if (g_ascii_strcasecmp (family, "imt") == 0)
+        self->priv->modem_family = MM_CINTERION_MODEM_FAMILY_IMT;
+    else if (g_ascii_strcasecmp (family, "default") != 0) {
+        mm_obj_dbg (self, "cinterion modem family '%s' unknown", family);
+        family = "default";
+    }
+
+    mm_obj_dbg (self, "Using cinterion %s modem family", family);
+
+    task = g_task_new (_self, NULL, callback, user_data);
+    mm_base_modem_at_command (MM_BASE_MODEM (_self),
                               "AT^SCFG=?",
                               3,
                               FALSE,
@@ -1185,19 +1231,22 @@
 }
 
 static void
-get_band_ready (MMBaseModem  *self,
+get_band_ready (MMBaseModem  *_self,
                 GAsyncResult *res,
                 GTask        *task)
 {
+    MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
     const gchar *response;
     GError      *error = NULL;
     GArray      *bands = NULL;
 
-    response = mm_base_modem_at_command_finish (self, res, &error);
+    response = mm_base_modem_at_command_finish (_self, res, &error);
     if (!response ||
         !mm_cinterion_parse_scfg_response (response,
+                                           self->priv->modem_family,
                                            mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
                                            &bands,
+                                           self->priv->rb_format,
                                            &error))
         g_task_return_error (task, error);
     else
@@ -1215,7 +1264,7 @@
     task = g_task_new (self, NULL, callback, user_data);
 
     mm_base_modem_at_command (MM_BASE_MODEM (self),
-                              "AT^SCFG=\"Radio/Band\"",
+                              "AT^SCFG?",
                               3,
                               FALSE,
                               (GAsyncReadyCallback)get_band_ready,
@@ -1248,43 +1297,100 @@
 }
 
 static void
+scfg_set_ready_sequence (MMBaseModem  *_self,
+                         GAsyncResult *res,
+                         GTask        *task)
+{
+    GError *error = NULL;
+    gpointer ctx = NULL;
+    guint i;
+    MMBroadbandModemCinterion *self;
+
+    self = g_task_get_source_object (task);
+    for (i = 0; self->priv->cmds[i].command; i++)
+        mm_base_modem_at_command_alloc_clear (&self->priv->cmds[i]);
+    g_free(self->priv->cmds);
+    self->priv->cmds = NULL;
+
+    mm_base_modem_at_sequence_finish (_self, res, &ctx, &error);
+    g_task_return_boolean (task, TRUE);
+    g_object_unref (task);
+}
+
+static void
 set_bands_3g (GTask  *task,
               GArray *bands_array)
 {
     MMBroadbandModemCinterion *self;
     GError                    *error = NULL;
-    guint                      band = 0;
-    gchar                     *cmd;
+    guint                      band[MM_CINTERION_RB_BLOCK_N] = { 0 };
 
     self = g_task_get_source_object (task);
 
     if (!mm_cinterion_build_band (bands_array,
                                   self->priv->supported_bands,
                                   FALSE, /* 2G and 3G */
-                                  &band,
+                                  self->priv->rb_format,
+                                  self->priv->modem_family,
+                                  band,
                                   &error)) {
         g_task_return_error (task, error);
         g_object_unref (task);
         return;
     }
 
-    /* Following the setup:
-     *  AT^SCFG="Radion/Band",<rba>
-     * We will set the preferred band equal to the allowed band, so that we force
-     * the modem to connect at that specific frequency only. Note that we will be
-     * passing a number here!
-     *
-     * The optional <rbe> field is set to 1, so that changes take effect
-     * immediately.
-     */
-    cmd = g_strdup_printf ("^SCFG=\"Radio/Band\",%u,1", band);
-    mm_base_modem_at_command (MM_BASE_MODEM (self),
-                              cmd,
-                              15,
-                              FALSE,
-                              (GAsyncReadyCallback)scfg_set_ready,
-                              task);
-    g_free (cmd);
+    if (self->priv->rb_format == MM_CINTERION_RADIO_BAND_FORMAT_SINGLE) {
+        g_autofree gchar *cmd = NULL;
+
+        /* Following the setup:
+         *  AT^SCFG="Radion/Band",<rba>
+         * We will set the preferred band equal to the allowed band, so that we force
+         * the modem to connect at that specific frequency only. Note that we will be
+         * passing a number here!
+         *
+         * The optional <rbe> field is set to 1, so that changes take effect
+         * immediately.
+         */
+        cmd = g_strdup_printf ("^SCFG=\"Radio/Band\",%u,1", band[MM_CINTERION_RB_BLOCK_LEGACY]);
+        mm_base_modem_at_command (MM_BASE_MODEM (self),
+                                  cmd,
+                                  15,
+                                  FALSE,
+                                  (GAsyncReadyCallback)scfg_set_ready,
+                                  task);
+    } else { /* self->priv->rb_format == MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE */
+        if (self->priv->modem_family == MM_CINTERION_MODEM_FAMILY_IMT) {
+            g_autofree gchar *bandstr2G = NULL;
+            g_autofree gchar *bandstr3G = NULL;
+            g_autofree gchar *bandstr4G = NULL;
+
+            bandstr2G = g_strdup_printf ("0x%08X", band[MM_CINTERION_RB_BLOCK_GSM]);
+            bandstr3G = g_strdup_printf ("0x%08X", band[MM_CINTERION_RB_BLOCK_UMTS]);
+            bandstr4G = g_strdup_printf ("0x%08X", band[MM_CINTERION_RB_BLOCK_LTE_LOW]);
+            bandstr2G = mm_broadband_modem_take_and_convert_to_current_charset (MM_BROADBAND_MODEM (self), bandstr2G);
+            bandstr3G = mm_broadband_modem_take_and_convert_to_current_charset (MM_BROADBAND_MODEM (self), bandstr3G);
+            bandstr4G = mm_broadband_modem_take_and_convert_to_current_charset (MM_BROADBAND_MODEM (self), bandstr4G);
+            self->priv->cmds = g_new0 (MMBaseModemAtCommandAlloc, 3 + 1);
+            self->priv->cmds[0].command = g_strdup_printf ("^SCFG=\"Radio/Band/2G\",\"%s\"", bandstr2G);
+            self->priv->cmds[1].command = g_strdup_printf ("^SCFG=\"Radio/Band/3G\",\"%s\"", bandstr3G);
+            self->priv->cmds[2].command = g_strdup_printf ("^SCFG=\"Radio/Band/4G\",\"%s\"", bandstr4G);
+            self->priv->cmds[0].timeout = self->priv->cmds[1].timeout = self->priv->cmds[2].timeout = 60;
+        } else {
+            self->priv->cmds = g_new0 (MMBaseModemAtCommandAlloc, 3 + 1);
+            self->priv->cmds[0].command = g_strdup_printf ("^SCFG=\"Radio/Band/2G\",\"%08x\",,1", band[MM_CINTERION_RB_BLOCK_GSM]);
+            self->priv->cmds[1].command = g_strdup_printf ("^SCFG=\"Radio/Band/3G\",\"%08x\",,1", band[MM_CINTERION_RB_BLOCK_UMTS]);
+            self->priv->cmds[2].command = g_strdup_printf ("^SCFG=\"Radio/Band/4G\",\"%08x\",\"%08x\",1", band[MM_CINTERION_RB_BLOCK_LTE_LOW], band[MM_CINTERION_RB_BLOCK_LTE_HIGH]);
+            self->priv->cmds[0].timeout = self->priv->cmds[1].timeout = self->priv->cmds[2].timeout = 15;
+        }
+
+        mm_base_modem_at_sequence (MM_BASE_MODEM (self),
+                                   (const MMBaseModemAtCommand *)self->priv->cmds,
+                                   NULL,
+                                   NULL,
+                                   (GAsyncReadyCallback)scfg_set_ready_sequence,
+                                   task);
+    }
+
 }
 
 static void
@@ -1293,7 +1399,7 @@
 {
     MMBroadbandModemCinterion *self;
     GError                    *error = NULL;
-    guint                      band = 0;
+    guint                      band[MM_CINTERION_RB_BLOCK_N] = { 0 };
     gchar                     *cmd;
     gchar                     *bandstr;
 
@@ -1302,7 +1408,9 @@
     if (!mm_cinterion_build_band (bands_array,
                                   self->priv->supported_bands,
                                   TRUE, /* 2G only */
-                                  &band,
+                                  MM_CINTERION_RADIO_BAND_FORMAT_SINGLE,
+                                  0,
+                                  band,
                                   &error)) {
         g_task_return_error (task, error);
         g_object_unref (task);
@@ -1310,7 +1418,7 @@
     }
 
     /* Build string with the value, in the proper charset */
-    bandstr = g_strdup_printf ("%u", band);
+    bandstr = g_strdup_printf ("%u", band[MM_CINTERION_RB_BLOCK_LEGACY]);
     bandstr = mm_broadband_modem_take_and_convert_to_current_charset (MM_BROADBAND_MODEM (self), bandstr);
     if (!bandstr) {
         g_task_return_new_error (task,
@@ -1600,8 +1708,8 @@
     self = g_task_get_source_object (task);
     ctx = g_task_get_task_data (task);
 
-    if (ctx->retries == 0) {
-        /* Too much wait, go on anyway */
+    /* if not supported or too much wait, skip */
+    if (self->priv->sind_simstatus_support != FEATURE_SUPPORTED || ctx->retries == 0) {
         g_task_return_boolean (task, TRUE);
         g_object_unref (task);
         return;
@@ -1618,6 +1726,40 @@
 }
 
 static void
+sind_indicators_ready (MMBaseModem  *_self,
+                       GAsyncResult *res,
+                       GTask        *task)
+{
+    MMBroadbandModemCinterion *self;
+    g_autoptr(GError)          error = NULL;
+    const gchar               *response;
+
+    self = MM_BROADBAND_MODEM_CINTERION (_self);
+    if (!(response = mm_base_modem_at_command_finish (_self, res, &error))) {
+        self->priv->sind_psinfo_support = FEATURE_NOT_SUPPORTED;
+        mm_obj_dbg (self, "psinfo support? no");
+
+        self->priv->sind_simstatus_support = FEATURE_NOT_SUPPORTED;
+        mm_obj_dbg (self, "simstatus support? no");
+
+        g_task_return_boolean (task, TRUE);
+        g_object_unref (task);
+
+        return;
+    }
+
+    if (g_regex_match_simple ("\\(\\s*psinfo\\s*,", response, 0, 0))
+        self->priv->sind_psinfo_support = FEATURE_SUPPORTED;
+    mm_obj_dbg (self, "psinfo support? %s", self->priv->sind_psinfo_support == FEATURE_SUPPORTED ? "yes":"no");
+
+    if (g_regex_match_simple ("\\(\\s*simstatus\\s*,", response, 0, 0))
+        self->priv->sind_simstatus_support = FEATURE_SUPPORTED;
+    mm_obj_dbg (self, "simstatus support? %s", self->priv->sind_simstatus_support == FEATURE_SUPPORTED ? "yes":"no");
+
+    after_sim_unlock_context_step (task);
+}
+
+static void
 after_sim_unlock (MMIfaceModem        *self,
                   GAsyncReadyCallback  callback,
                   gpointer             user_data)
@@ -1630,7 +1772,13 @@
     ctx->retries = MAX_AFTER_SIM_UNLOCK_RETRIES;
     g_task_set_task_data (task, ctx, g_free);
 
-    after_sim_unlock_context_step (task);
+    /* check which indicators are available */
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "AT^SIND=?",
+                              3,
+                              FALSE,
+                              (GAsyncReadyCallback)sind_indicators_ready,
+                              task);
 }
 
 /*****************************************************************************/
@@ -1791,8 +1939,10 @@
                                               MMBroadbandModemCinterionPrivate);
 
     /* Initialize private variables */
-    self->priv->sind_psinfo_support = FEATURE_SUPPORT_UNKNOWN;
-    self->priv->swwan_support       = FEATURE_SUPPORT_UNKNOWN;
+    self->priv->sind_psinfo_support    = FEATURE_SUPPORT_UNKNOWN;
+    self->priv->swwan_support          = FEATURE_SUPPORT_UNKNOWN;
+    self->priv->smoni_support          = FEATURE_SUPPORT_UNKNOWN;
+    self->priv->sind_simstatus_support = FEATURE_SUPPORT_UNKNOWN;
 
     self->priv->ciev_regex = g_regex_new ("\\r\\n\\+CIEV:\\s*([a-z]+),(\\d+)\\r\\n",
                                           G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
@@ -1958,3 +2108,133 @@
     /* Virtual methods */
     object_class->finalize = finalize;
 }
+
+/*****************************************************************************/
+/* Check support (Signal interface) */
+
+static gboolean
+signal_check_support_finish  (MMIfaceModemSignal  *self,
+                              GAsyncResult        *res,
+                              GError             **error)
+{
+    return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_signal_check_support_ready (MMIfaceModemSignal *self,
+                                   GAsyncResult       *res,
+                                   GTask              *task)
+{
+    gboolean parent_supported;
+
+    parent_supported = iface_modem_signal_parent->check_support_finish (self, res, NULL);
+    g_task_return_boolean (task, parent_supported);
+    g_object_unref (task);
+}
+
+static void
+check_smoni_support (MMBaseModem  *_self,
+                     GAsyncResult *res,
+                     GTask        *task)
+{
+    MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+
+    /* Fetch the result to the SMONI test. If no response given (error triggered), assume unsupported */
+    if (mm_base_modem_at_command_finish (_self, res, NULL)) {
+        mm_obj_dbg (self, "SMONI supported");
+        self->priv->smoni_support = FEATURE_SUPPORTED;
+        g_task_return_boolean (task, TRUE); // otherwise the whole interface is not available
+        g_object_unref (task);
+        return;
+    }
+
+    mm_obj_dbg (self, "SMONI unsupported");
+    self->priv->smoni_support = FEATURE_NOT_SUPPORTED;
+
+    /* Otherwise, check if the parent CESQ-based implementation works */
+    g_assert (iface_modem_signal_parent->check_support && iface_modem_signal_parent->check_support_finish);
+    iface_modem_signal_parent->check_support (g_task_get_task_data (task),
+                                              (GAsyncReadyCallback)parent_signal_check_support_ready,
+                                              task);
+}
+
+static void
+signal_check_support (MMIfaceModemSignal  *_self,
+                      GAsyncReadyCallback  callback,
+                      gpointer             user_data)
+{
+    MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+
+    GTask *task = g_task_new (self, NULL, callback, user_data);
+    g_task_set_task_data (task, _self, NULL);
+
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "^SMONI=?",
+                              3,
+                              FALSE,
+                              (GAsyncReadyCallback) check_smoni_support,
+                              task);
+}
+
+/*****************************************************************************/
+/* Load extended signal information (Signal interface) */
+
+static gboolean
+signal_load_values_finish (MMIfaceModemSignal  *_self,
+                           GAsyncResult        *res,
+                           MMSignal           **cdma,
+                           MMSignal           **evdo,
+                           MMSignal           **gsm,
+                           MMSignal           **umts,
+                           MMSignal           **lte,
+                           GError             **error)
+{
+    MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+    const gchar *response;
+
+    if (self->priv->smoni_support == FEATURE_NOT_SUPPORTED)
+        return iface_modem_signal_parent->load_values_finish (_self, res, cdma, evdo, gsm, umts, lte, error);
+
+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (_self), res, error);
+    if (!response || !mm_cinterion_smoni_response_to_signal_info (response, gsm, umts, lte, error))
+        return FALSE;
+
+    if (cdma)
+        *cdma = NULL;
+    if (evdo)
+        *evdo = NULL;
+
+    return TRUE;
+}
+
+static void
+signal_load_values (MMIfaceModemSignal  *_self,
+                    GCancellable        *cancellable,
+                    GAsyncReadyCallback  callback,
+                    gpointer             user_data)
+{
+    MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+    if (self->priv->smoni_support == FEATURE_SUPPORTED) {
+        mm_base_modem_at_command (MM_BASE_MODEM (_self),
+                                  "^SMONI",
+                                  3,
+                                  FALSE,
+                                  callback,
+                                  user_data);
+        return;
+    }
+
+    /* ^SMONI not supported, fallback to the parent */
+    iface_modem_signal_parent->load_values (_self, cancellable, callback, user_data);
+}
+
+static void
+iface_modem_signal_init (MMIfaceModemSignal *iface)
+{
+    iface_modem_signal_parent   = g_type_interface_peek_parent (iface);
+
+    iface->check_support        = signal_check_support;
+    iface->check_support_finish = signal_check_support_finish;
+    iface->load_values          = signal_load_values;
+    iface->load_values_finish   = signal_load_values_finish;
+}
diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.c b/plugins/cinterion/mm-modem-helpers-cinterion.c
index da2dd64..9041960 100644
--- a/plugins/cinterion/mm-modem-helpers-cinterion.c
+++ b/plugins/cinterion/mm-modem-helpers-cinterion.c
@@ -33,10 +33,16 @@
 /* Setup relationship between the 3G band bitmask in the modem and the bitmask
  * in ModemManager. */
 typedef struct {
-    guint32 cinterion_band_flag;
+    guint32     cinterion_band_flag;
     MMModemBand mm_band;
 } CinterionBand;
 
+typedef struct {
+    MMCinterionRbBlock cinterion_band_block;
+    guint32            cinterion_band_flag;
+    MMModemBand        mm_band;
+} CinterionBandEx;
+
 /* Table checked in PLS8-X/E/J/V/US, HC25 & PHS8 references. The table includes 2/3/4G
  * frequencies. Depending on which one is configured, one access technology or
  * the other will be used. This may conflict with the allowed mode configuration
@@ -68,6 +74,68 @@
     { (1 << 24), MM_MODEM_BAND_EUTRAN_19 }
 };
 
+static const CinterionBandEx cinterion_bands_ex[] = {
+    { MM_CINTERION_RB_BLOCK_GSM,      0x00000001, MM_MODEM_BAND_EGSM      },
+    { MM_CINTERION_RB_BLOCK_GSM,      0x00000002, MM_MODEM_BAND_DCS       },
+    { MM_CINTERION_RB_BLOCK_GSM,      0x00000004, MM_MODEM_BAND_G850      },
+    { MM_CINTERION_RB_BLOCK_GSM,      0x00000008, MM_MODEM_BAND_PCS       },
+    { MM_CINTERION_RB_BLOCK_UMTS,     0x00000001, MM_MODEM_BAND_UTRAN_1   },
+    { MM_CINTERION_RB_BLOCK_UMTS,     0x00000002, MM_MODEM_BAND_UTRAN_2   },
+    { MM_CINTERION_RB_BLOCK_UMTS,     0x00000004, MM_MODEM_BAND_UTRAN_3   },
+    { MM_CINTERION_RB_BLOCK_UMTS,     0x00000008, MM_MODEM_BAND_UTRAN_4   },
+    { MM_CINTERION_RB_BLOCK_UMTS,     0x00000010, MM_MODEM_BAND_UTRAN_5   },
+    { MM_CINTERION_RB_BLOCK_UMTS,     0x00000020, MM_MODEM_BAND_UTRAN_6   },
+    { MM_CINTERION_RB_BLOCK_UMTS,     0x00000080, MM_MODEM_BAND_UTRAN_8   },
+    { MM_CINTERION_RB_BLOCK_UMTS,     0x00000100, MM_MODEM_BAND_UTRAN_9   },
+    { MM_CINTERION_RB_BLOCK_UMTS,     0x00040000, MM_MODEM_BAND_UTRAN_19  },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00000001, MM_MODEM_BAND_EUTRAN_1  },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00000002, MM_MODEM_BAND_EUTRAN_2  },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00000004, MM_MODEM_BAND_EUTRAN_3  },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00000008, MM_MODEM_BAND_EUTRAN_4  },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00000010, MM_MODEM_BAND_EUTRAN_5  },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00000040, MM_MODEM_BAND_EUTRAN_7  },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00000080, MM_MODEM_BAND_EUTRAN_8  },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00000800, MM_MODEM_BAND_EUTRAN_12 },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00001000, MM_MODEM_BAND_EUTRAN_13 },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00010000, MM_MODEM_BAND_EUTRAN_17 },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00020000, MM_MODEM_BAND_EUTRAN_18 },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00040000, MM_MODEM_BAND_EUTRAN_19 },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00080000, MM_MODEM_BAND_EUTRAN_20 },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x02000000, MM_MODEM_BAND_EUTRAN_26 },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x08000000, MM_MODEM_BAND_EUTRAN_28 },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x10000000, MM_MODEM_BAND_EUTRAN_29 },
+    { MM_CINTERION_RB_BLOCK_LTE_HIGH, 0x00000020, MM_MODEM_BAND_EUTRAN_38 },
+    { MM_CINTERION_RB_BLOCK_LTE_HIGH, 0x00000040, MM_MODEM_BAND_EUTRAN_39 },
+    { MM_CINTERION_RB_BLOCK_LTE_HIGH, 0x00000080, MM_MODEM_BAND_EUTRAN_40 },
+    { MM_CINTERION_RB_BLOCK_LTE_HIGH, 0x00000100, MM_MODEM_BAND_EUTRAN_41 }
+};
+
+static const CinterionBandEx cinterion_bands_imt[] = {
+    { MM_CINTERION_RB_BLOCK_GSM,      0x00000004, MM_MODEM_BAND_EGSM      },
+    { MM_CINTERION_RB_BLOCK_GSM,      0x00000010, MM_MODEM_BAND_DCS       },
+    { MM_CINTERION_RB_BLOCK_GSM,      0x00000020, MM_MODEM_BAND_PCS       },
+    { MM_CINTERION_RB_BLOCK_GSM,      0x00000040, MM_MODEM_BAND_G850      },
+    { MM_CINTERION_RB_BLOCK_UMTS,     0x00000001, MM_MODEM_BAND_UTRAN_1   },
+    { MM_CINTERION_RB_BLOCK_UMTS,     0x00000002, MM_MODEM_BAND_UTRAN_2   },
+    { MM_CINTERION_RB_BLOCK_UMTS,     0x00000008, MM_MODEM_BAND_UTRAN_4   },
+    { MM_CINTERION_RB_BLOCK_UMTS,     0x00000010, MM_MODEM_BAND_UTRAN_5   },
+    { MM_CINTERION_RB_BLOCK_UMTS,     0x00000080, MM_MODEM_BAND_UTRAN_8   },
+    { MM_CINTERION_RB_BLOCK_UMTS,     0x00000100, MM_MODEM_BAND_UTRAN_9   },
+    { MM_CINTERION_RB_BLOCK_UMTS,     0x00040000, MM_MODEM_BAND_UTRAN_19  },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00000001, MM_MODEM_BAND_EUTRAN_1  },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00000002, MM_MODEM_BAND_EUTRAN_2  },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00000004, MM_MODEM_BAND_EUTRAN_3  },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00000008, MM_MODEM_BAND_EUTRAN_4  },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00000010, MM_MODEM_BAND_EUTRAN_5  },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00000040, MM_MODEM_BAND_EUTRAN_7  },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00000080, MM_MODEM_BAND_EUTRAN_8  },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00000800, MM_MODEM_BAND_EUTRAN_12 },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00020000, MM_MODEM_BAND_EUTRAN_18 },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00040000, MM_MODEM_BAND_EUTRAN_19 },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x00080000, MM_MODEM_BAND_EUTRAN_20 },
+    { MM_CINTERION_RB_BLOCK_LTE_LOW,  0x08000000, MM_MODEM_BAND_EUTRAN_28 }
+};
+
 /* Check valid combinations in 2G-only devices */
 #define VALIDATE_2G_BAND(cinterion_mask) \
     (cinterion_mask == 1  ||             \
@@ -81,46 +149,127 @@
      cinterion_mask == 15)
 
 /*****************************************************************************/
-/* ^SCFG (3G) test parser
+/* ^SCFG (3G+LTE) test parser
  *
- * Example:
+ * Example 3G:
  *   AT^SCFG=?
  *     ...
  *     ^SCFG: "MEShutdown/OnIgnition",("on","off")
  *     ^SCFG: "Radio/Band",("1-511","0-1")
  *     ^SCFG: "Radio/NWSM",("0","1","2")
  *     ...
- *
  *     ^SCFG: "Radio/Band\",("1"-"147")
+ *
+ * Example LTE1 (GSM charset):
+ *   AT^SCFG=?
+ *     ...
+ *     ^SCFG: "Radio/Band/2G",("0x00000004"-"0x00000074")
+ *     ^SCFG: "Radio/Band/3G",("0x00000001"-"0x0004019B")
+ *     ^SCFG: "Radio/Band/4G",("0x00000001"-"0x080E08DF")
+ *     ...
+ *
+ * Example LTE1 (UCS2 charset):
+ *   AT^SCFG=?
+ *     ...
+ *     ^SCFG: "Radio/Band/2G",("0030007800300030003000300030003000300034"-"0030007800300030003000300030003000370034")
+ *     ^SCFG: "Radio/Band/3G",("0030007800300030003000300030003000300031"-"0030007800300030003000340030003100390042")
+ *     ^SCFG: "Radio/Band/4G",("0030007800300030003000300030003000300031"-"0030007800300038003000450030003800440046")
+ *     ...
+ *
+ * Example LTE2 (all charsets):
+ *   AT^SCFG=?
+ *     ...
+ *   ^SCFG: "Radio/Band/2G",("00000001-0000000f"),,("0","1")
+ *   ^SCFG: "Radio/Band/3G",("00000001-000400b5"),,("0","1")
+ *   ^SCFG: "Radio/Band/4G",("00000001-8a0e00d5"),("00000002-000001e2"),("0","1")
+ *     ...
  */
 
-gboolean
-mm_cinterion_parse_scfg_test (const gchar *response,
-                              MMModemCharset charset,
-                              GArray **supported_bands,
-                              GError **error)
+static void
+parse_bands (guint                      bandlist,
+             GArray                   **bands,
+             MMCinterionRbBlock         block,
+             MMCinterionModemFamily     modem_family)
 {
-    GRegex *r;
-    GMatchInfo *match_info;
-    GError *inner_error = NULL;
-    GArray *bands = NULL;
+    guint                  i;
+    const CinterionBandEx *ref_bands;
+    guint                  nb_ref_bands;
+
+    if (!bandlist)
+        return;
+
+    if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT) {
+        ref_bands = cinterion_bands_imt;
+        nb_ref_bands = G_N_ELEMENTS (cinterion_bands_imt);
+    } else {
+        ref_bands = cinterion_bands_ex;
+        nb_ref_bands = G_N_ELEMENTS (cinterion_bands_ex);
+    }
+
+    for (i = 0; i < nb_ref_bands; i++) {
+        if (block == ref_bands[i].cinterion_band_block && (bandlist & ref_bands[i].cinterion_band_flag)) {
+            if (G_UNLIKELY (!*bands))
+                *bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 23);
+            g_array_append_val (*bands, ref_bands[i].mm_band);
+        }
+    }
+}
+
+static guint
+take_and_convert_from_matched_string (gchar                  *str,
+                                      MMModemCharset          charset,
+                                      MMCinterionModemFamily  modem_family)
+{
+    guint val = 0;
+
+    if (!str)
+        return 0;
+
+    if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT)
+        str = mm_charset_take_and_convert_to_utf8 (str, charset);
+
+    mm_get_uint_from_hex_str (str, &val);
+    g_free (str);
+
+    return val;
+}
+
+gboolean
+mm_cinterion_parse_scfg_test (const gchar                 *response,
+                              MMCinterionModemFamily       modem_family,
+                              MMModemCharset               charset,
+                              GArray                     **supported_bands,
+                              MMCinterionRadioBandFormat  *format,
+                              GError                     **error)
+{
+    g_autoptr(GRegex)      r1 = NULL;
+    g_autoptr(GMatchInfo)  match_info1 = NULL;
+    g_autoptr(GRegex)      r2 = NULL;
+    g_autoptr(GMatchInfo)  match_info2 = NULL;
+    GError                *inner_error = NULL;
+    GArray                *bands = NULL;
+
+    g_assert (format);
 
     if (!response) {
         g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response");
         return FALSE;
     }
 
-    r = g_regex_new ("\\^SCFG:\\s*\"Radio/Band\",\\((?:\")?([0-9]*)(?:\")?-(?:\")?([0-9]*)(?:\")?.*\\)",
-                     G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
-                     0, NULL);
-    g_assert (r != NULL);
+    r1 = g_regex_new ("\\^SCFG:\\s*\"Radio/Band\",\\((?:\")?([0-9]*)(?:\")?-(?:\")?([0-9]*)(?:\")?.*\\)",
+                     G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL);
+    g_assert (r1 != NULL);
 
-    g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
-    if (!inner_error && g_match_info_matches (match_info)) {
-        gchar *maxbandstr;
-        guint maxband = 0;
+    g_regex_match_full (r1, response, strlen (response), 0, 0, &match_info1, &inner_error);
+    if (inner_error)
+        goto finish;
+    if (g_match_info_matches (match_info1)) {
+        g_autofree gchar *maxbandstr = NULL;
+        guint             maxband = 0;
 
-        maxbandstr = mm_get_string_unquoted_from_match_info (match_info, 2);
+        *format = MM_CINTERION_RADIO_BAND_FORMAT_SINGLE;
+
+        maxbandstr = mm_get_string_unquoted_from_match_info (match_info1, 2);
         if (maxbandstr) {
             /* Handle charset conversion if the number is given in UCS2 */
             if (charset != MM_MODEM_CHARSET_UNKNOWN)
@@ -144,14 +293,48 @@
                 }
             }
         }
-
-        g_free (maxbandstr);
+        goto finish;
     }
 
-    g_match_info_free (match_info);
-    g_regex_unref (r);
+    r2 = g_regex_new ("\\^SCFG:\\s*\"Radio/Band/([234]G)\",\\(\"?([0-9A-Fa-fx]*)\"?-\"?([0-9A-Fa-fx]*)\"?\\)(,*\\(\"?([0-9A-Fa-fx]*)\"?-\"?([0-9A-Fa-fx]*)\"?\\))?",
+                     0, 0, NULL);
+    g_assert (r2 != NULL);
+    g_regex_match_full (r2, response, strlen (response), 0, 0, &match_info2, &inner_error);
+    if (inner_error)
+        goto finish;
+    while (g_match_info_matches (match_info2)) {
+        g_autofree gchar *techstr = NULL;
+        guint             maxband;
 
-    if (!bands)
+        *format = MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE;
+
+        techstr = mm_get_string_unquoted_from_match_info (match_info2, 1);
+        if (g_strcmp0 (techstr, "2G") == 0) {
+            maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 3), charset, modem_family);
+            parse_bands (maxband, &bands, MM_CINTERION_RB_BLOCK_GSM, modem_family);
+        } else if (g_strcmp0 (techstr, "3G") == 0) {
+            maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 3), charset, modem_family);
+            parse_bands (maxband, &bands, MM_CINTERION_RB_BLOCK_UMTS, modem_family);
+        } else if (g_strcmp0 (techstr, "4G") == 0) {
+            maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 3), charset, modem_family);
+            parse_bands (maxband, &bands, MM_CINTERION_RB_BLOCK_LTE_LOW, modem_family);
+            if (modem_family == MM_CINTERION_MODEM_FAMILY_DEFAULT) {
+                maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 6), charset, modem_family);
+                parse_bands (maxband, &bands, MM_CINTERION_RB_BLOCK_LTE_HIGH, modem_family);
+            }
+        } else {
+            inner_error = g_error_new (MM_CORE_ERROR,
+                                       MM_CORE_ERROR_FAILED,
+                                       "Couldn't parse ^SCFG=? response");
+            break;
+        }
+
+        g_match_info_next (match_info2, NULL);
+    }
+
+finish:
+    /* set error only if not already given */
+    if (!bands && !inner_error)
         inner_error = g_error_new (MM_CORE_ERROR,
                                    MM_CORE_ERROR_FAILED,
                                    "No valid bands found in ^SCFG=? response");
@@ -168,7 +351,7 @@
 }
 
 /*****************************************************************************/
-/* ^SCFG response parser
+/* ^SCFG response parser (2 types: 2G/3G and LTE)
  *
  * Example (3G):
  *   AT^SCFG="Radio/Band"
@@ -181,63 +364,125 @@
  * Example (2G):
  *   AT+SCFG="Radio/Band"
  *     ^SCFG: "Radio/Band","3","3"
+ *
+ * Example LTE1 (GSM charset):
+ *   AT^SCFG=?
+ *     ...
+ *     ^SCFG: "Radio/Band/2G","0x00000074"
+ *     ^SCFG: "Radio/Band/3G","0x0004019B"
+ *     ^SCFG: "Radio/Band/4G","0x080E08DF"
+ *     ...
+ *   AT^SCFG=?
+ *     ...
+ * Example LTE1 (UCS2 charset):
+ *   AT^SCFG=?
+ *     ...
+ *     ^SCFG: "Radio/Band/2G","0030007800300030003000300030003000370034"
+ *     ^SCFG: "Radio/Band/3G","0030007800300030003000340030003100390042"
+ *     ^SCFG: "Radio/Band/4G","0030007800300038003000450030003800440046"
+ *     ...
+ * Example LTE2 (all charsets):
+ *   AT^SCFG=?
+ *     ...
+ *     ^SCFG: "Radio/Band/2G","0000000f"
+ *     ^SCFG: "Radio/Band/3G","000400b5"
+ *     ^SCFG: "Radio/Band/4G","8a0e00d5","000000e2"
+ *     ...
  */
 
 gboolean
-mm_cinterion_parse_scfg_response (const gchar *response,
-                                  MMModemCharset charset,
-                                  GArray **current_bands,
-                                  GError **error)
+mm_cinterion_parse_scfg_response (const gchar                  *response,
+                                  MMCinterionModemFamily        modem_family,
+                                  MMModemCharset                charset,
+                                  GArray                      **current_bands,
+                                  MMCinterionRadioBandFormat    format,
+                                  GError                      **error)
 {
-    GRegex *r;
-    GMatchInfo *match_info;
-    GError *inner_error = NULL;
-    GArray *bands = NULL;
+    g_autoptr(GRegex)      r = NULL;
+    g_autoptr(GMatchInfo)  match_info = NULL;
+    GError                *inner_error = NULL;
+    GArray                *bands = NULL;
 
     if (!response) {
         g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response");
         return FALSE;
     }
 
-    r = g_regex_new ("\\^SCFG:\\s*\"Radio/Band\",\\s*\"?([0-9a-fA-F]*)\"?", 0, 0, NULL);
-    g_assert (r != NULL);
+    if (format == MM_CINTERION_RADIO_BAND_FORMAT_SINGLE) {
+        r = g_regex_new ("\\^SCFG:\\s*\"Radio/Band\",\\s*\"?([0-9a-fA-F]*)\"?", 0, 0, NULL);
+        g_assert (r != NULL);
+        g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+        if (inner_error)
+            goto finish;
+        if (g_match_info_matches (match_info)) {
+            g_autofree gchar *currentstr = NULL;
+            guint current = 0;
 
-    if (g_regex_match (r, response, 0, &match_info)) {
-        gchar *currentstr;
-        guint current = 0;
+            currentstr = mm_get_string_unquoted_from_match_info (match_info, 1);
+            if (currentstr) {
+                /* Handle charset conversion if the number is given in UCS2 */
+                if (charset != MM_MODEM_CHARSET_UNKNOWN)
+                    currentstr = mm_charset_take_and_convert_to_utf8 (currentstr, charset);
+                mm_get_uint_from_str (currentstr, &current);
+            }
 
-        currentstr = mm_get_string_unquoted_from_match_info (match_info, 1);
-        if (currentstr) {
-            /* Handle charset conversion if the number is given in UCS2 */
-            if (charset != MM_MODEM_CHARSET_UNKNOWN)
-                currentstr = mm_charset_take_and_convert_to_utf8 (currentstr, charset);
+            if (current == 0) {
+                inner_error = g_error_new (MM_CORE_ERROR,
+                                           MM_CORE_ERROR_FAILED,
+                                           "Couldn't parse ^SCFG? response");
+            } else {
+                guint i;
 
-            mm_get_uint_from_str (currentstr, &current);
-        }
-
-        if (current == 0) {
-            inner_error = g_error_new (MM_CORE_ERROR,
-                                       MM_CORE_ERROR_FAILED,
-                                       "Couldn't parse ^SCFG response");
-        } else {
-            guint i;
-
-            for (i = 0; i < G_N_ELEMENTS (cinterion_bands); i++) {
-                if (current & cinterion_bands[i].cinterion_band_flag) {
-                    if (G_UNLIKELY (!bands))
-                        bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9);
-                    g_array_append_val (bands, cinterion_bands[i].mm_band);
+                for (i = 0; i < G_N_ELEMENTS (cinterion_bands); i++) {
+                    if (current & cinterion_bands[i].cinterion_band_flag) {
+                        if (G_UNLIKELY (!bands))
+                            bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9);
+                        g_array_append_val (bands, cinterion_bands[i].mm_band);
+                    }
                 }
             }
         }
+    } else if (format == MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE) {
+        r = g_regex_new ("\\^SCFG:\\s*\"Radio/Band/([234]G)\",\"?([0-9A-Fa-fx]*)\"?,?\"?([0-9A-Fa-fx]*)?\"?",
+                         0, 0, NULL);
+        g_assert (r != NULL);
+        g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+        if (inner_error)
+            goto finish;
+        while (g_match_info_matches (match_info)) {
+            g_autofree gchar *techstr = NULL;
+            guint current;
 
-        g_free (currentstr);
-    }
+            techstr = mm_get_string_unquoted_from_match_info (match_info, 1);
+            if (g_strcmp0(techstr, "2G") == 0) {
+                current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 2), charset, modem_family);
+                parse_bands (current, &bands, MM_CINTERION_RB_BLOCK_GSM, modem_family);
 
-    g_match_info_free (match_info);
-    g_regex_unref (r);
+            } else if (g_strcmp0(techstr, "3G") == 0) {
+                current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 2), charset, modem_family);
+                parse_bands (current, &bands, MM_CINTERION_RB_BLOCK_UMTS, modem_family);
+            } else if (g_strcmp0(techstr, "4G") == 0) {
+                current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 2), charset, modem_family);
+                parse_bands (current, &bands, MM_CINTERION_RB_BLOCK_LTE_LOW, modem_family);
+                if (modem_family == MM_CINTERION_MODEM_FAMILY_DEFAULT) {
+                    current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 3), charset, modem_family);
+                    parse_bands (current, &bands, MM_CINTERION_RB_BLOCK_LTE_HIGH, modem_family);
+                }
+            } else {
+                inner_error = g_error_new (MM_CORE_ERROR,
+                                           MM_CORE_ERROR_FAILED,
+                                           "Couldn't parse ^SCFG? response");
+                break;
+            }
 
-    if (!bands)
+            g_match_info_next (match_info, NULL);
+        }
+    } else
+        g_assert_not_reached ();
+
+finish:
+    /* set error only if not already given */
+    if (!bands && !inner_error)
         inner_error = g_error_new (MM_CORE_ERROR,
                                    MM_CORE_ERROR_FAILED,
                                    "No valid bands found in ^SCFG response");
@@ -371,52 +616,101 @@
 /* Build Cinterion-specific band value */
 
 gboolean
-mm_cinterion_build_band (GArray *bands,
-                         guint supported,
-                         gboolean only_2g,
-                         guint *out_band,
-                         GError **error)
+mm_cinterion_build_band (GArray                        *bands,
+                         guint                         *supported,
+                         gboolean                       only_2g,
+                         MMCinterionRadioBandFormat     format,
+                         MMCinterionModemFamily         modem_family,
+                         guint                         *out_band,
+                         GError                       **error)
 {
-    guint band = 0;
+    guint band[MM_CINTERION_RB_BLOCK_N] = { 0 };
 
-    /* The special case of ANY should be treated separately. */
-    if (bands->len == 1 && g_array_index (bands, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
-        band = supported;
-    } else {
-        guint i;
+    if (format == MM_CINTERION_RADIO_BAND_FORMAT_SINGLE) {
+        /* The special case of ANY should be treated separately. */
+        if (bands->len == 1 && g_array_index (bands, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
+            if (supported)
+                band[MM_CINTERION_RB_BLOCK_LEGACY] = supported[MM_CINTERION_RB_BLOCK_LEGACY];
+        } else {
+            guint i;
 
-        for (i = 0; i < G_N_ELEMENTS (cinterion_bands); i++) {
-            guint j;
+            for (i = 0; i < G_N_ELEMENTS (cinterion_bands); i++) {
+                guint j;
 
-            for (j = 0; j < bands->len; j++) {
-                if (g_array_index (bands, MMModemBand, j) == cinterion_bands[i].mm_band) {
-                    band |= cinterion_bands[i].cinterion_band_flag;
-                    break;
+                for (j = 0; j < bands->len; j++) {
+                    if (g_array_index (bands, MMModemBand, j) == cinterion_bands[i].mm_band) {
+                        band[MM_CINTERION_RB_BLOCK_LEGACY] |= cinterion_bands[i].cinterion_band_flag;
+                        break;
+                    }
+                }
+            }
+
+            /* 2G-only modems only support a subset of the possible band
+             * combinations. Detect it early and error out.
+             */
+            if (only_2g && !VALIDATE_2G_BAND (band[MM_CINTERION_RB_BLOCK_LEGACY]))
+                band[MM_CINTERION_RB_BLOCK_LEGACY] = 0;
+        }
+
+        if (band[MM_CINTERION_RB_BLOCK_LEGACY] == 0) {
+            g_autofree gchar *bands_string = NULL;
+
+            bands_string = mm_common_build_bands_string ((MMModemBand *)(gpointer)bands->data, bands->len);
+            g_set_error (error,
+                         MM_CORE_ERROR,
+                         MM_CORE_ERROR_FAILED,
+                         "The given band combination is not supported: '%s'",
+                         bands_string);
+            return FALSE;
+        }
+
+    } else { /* format == MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE */
+        if (bands->len == 1 && g_array_index (bands, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
+            if (supported)
+                memcpy (band, supported, sizeof (guint) * MM_CINTERION_RB_BLOCK_N);
+        } else {
+            guint i;
+            const CinterionBandEx *ref_bands;
+            guint nb_ref_bands;
+
+            if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT) {
+                ref_bands = cinterion_bands_imt;
+                nb_ref_bands = G_N_ELEMENTS (cinterion_bands_imt);
+            } else {
+                ref_bands = cinterion_bands_ex;
+                nb_ref_bands = G_N_ELEMENTS (cinterion_bands_ex);
+            }
+
+            for (i = 0; i < nb_ref_bands; i++) {
+                guint j;
+
+                for (j = 0; j < bands->len; j++) {
+                    if (g_array_index (bands, MMModemBand, j) == ref_bands[i].mm_band) {
+                        band[ref_bands[i].cinterion_band_block] |= ref_bands[i].cinterion_band_flag;
+                        break;
+                    }
                 }
             }
         }
 
-        /* 2G-only modems only support a subset of the possible band
-         * combinations. Detect it early and error out.
-         */
-        if (only_2g && !VALIDATE_2G_BAND (band))
-            band = 0;
+        /* this modem family does not allow disabling all bands in a given technology through this command */
+        if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT &&
+            (!band[MM_CINTERION_RB_BLOCK_GSM] ||
+             !band[MM_CINTERION_RB_BLOCK_UMTS] ||
+             !band[MM_CINTERION_RB_BLOCK_LTE_LOW])) {
+            g_autofree gchar *bands_string = NULL;
+
+            bands_string = mm_common_build_bands_string ((MMModemBand *)(gpointer)bands->data, bands->len);
+            g_set_error (error,
+                         MM_CORE_ERROR,
+                         MM_CORE_ERROR_FAILED,
+                         "The given band combination is not supported: '%s'",
+                         bands_string);
+            return FALSE;
+        }
     }
 
-    if (band == 0) {
-        gchar *bands_string;
-
-        bands_string = mm_common_build_bands_string ((MMModemBand *)bands->data, bands->len);
-        g_set_error (error,
-                     MM_CORE_ERROR,
-                     MM_CORE_ERROR_FAILED,
-                     "The given band combination is not supported: '%s'",
-                     bands_string);
-        g_free (bands_string);
-        return FALSE;
-    }
-
-    *out_band = band;
+    memcpy (out_band, band, sizeof (guint) * MM_CINTERION_RB_BLOCK_N);
     return TRUE;
 }
 
@@ -869,3 +1163,247 @@
 
     return TRUE;
 }
+
+/*****************************************************************************/
+/* ^SMONI response parser */
+
+gboolean
+mm_cinterion_parse_smoni_query_response (const gchar           *response,
+                                         MMCinterionRadioGen   *out_tech,
+                                         gdouble               *out_rssi,
+                                         gdouble               *out_ecn0,
+                                         gdouble               *out_rscp,
+                                         gdouble               *out_rsrp,
+                                         gdouble               *out_rsrq,
+                                         GError               **error)
+{
+    g_autoptr(GRegex)        r = NULL;
+    g_autoptr(GRegex)        pre = NULL;
+    g_autoptr(GMatchInfo)    match_info = NULL;
+    g_autoptr(GMatchInfo)    match_info_pre = NULL;
+    GError                  *inner_error = NULL;
+    MMCinterionRadioGen      tech = MM_CINTERION_RADIO_GEN_NONE;
+    gdouble                  rssi = -G_MAXDOUBLE;
+    gdouble                  ecn0 = -G_MAXDOUBLE;
+    gdouble                  rscp = -G_MAXDOUBLE;
+    gdouble                  rsrq = -G_MAXDOUBLE;
+    gdouble                  rsrp = -G_MAXDOUBLE;
+    gboolean                 success = FALSE;
+
+    g_assert (out_tech);
+    g_assert (out_rssi);
+    g_assert (out_ecn0);
+    g_assert (out_rscp);
+    g_assert (out_rsrp);
+    g_assert (out_rsrq);
+    g_assert (out_rssi);
+
+    /* Possible Responses:
+     * 2G
+     * ^SMONI: 2G,ARFCN,BCCH,MCC,MNC,LAC,cell,C1,C2,NCC,BCC,GPRS,Conn_state                                     // registered
+     * ^SMONI: 2G,ARFCN,BCCH,MCC,MNC,LAC,cell,C1,C2,NCC,BCC,GPRS,ARFCN,TS,timAdv,dBm,Q,ChMod                    // searching
+     * ^SMONI: 2G,ARFCN,BCCH,MCC,MNC,LAC,cell,C1,C2,NCC,BCC,GPRS,PWR,RXLev,ARFCN,TS,timAdv,dBm,Q,ChMod          // limsrv
+     * ^SMONI: 2G,ARFCN,BCCH,MCC,MNC,LAC,cell,C1,C2,NCC,BCC,GPRS,ARFCN,TS,timAdv,dBm,Q,ChMod                    // dedicated channel
+     *
+     * ^SMONI: 2G,71,-61,262,02,0143,83BA,33,33,3,6,G,NOCONN
+     *               ^^^
+     * ^SMONI: 2G,SEARCH,SEARCH
+     * ^SMONI: 2G,673,-89,262,07,4EED,A500,16,16,7,4,G,5,-107,LIMSRV
+     *                ^^^                                ^^^^ RXLev dBm
+     * ^SMONI: 2G,673,-80,262,07,4EED,A500,35,35,7,4,G,643,4,0,-80,0,S_FR
+     *                ^^^                                      ^^^ dBm: Receiving level of the traffic channel carrier in dBm
+     *  BCCH: Receiving level of the BCCH carrier in dBm (level is limited from -110dBm to -47dBm)
+     *   -> rssi for 2G, directly without mm_3gpp_rxlev_to_rssi
+     *
+     *
+     * 3G
+     * ^SMONI: 3G,UARFCN,PSC,EC/n0,RSCP,MCC,MNC,LAC,cell,SQual,SRxLev,,Conn_state",
+     * ^SMONI: 3G,UARFCN,PSC,EC/n0,RSCP,MCC,MNC,LAC,cell,SQual,SRxLev,PhysCh, SF,Slot,EC/n0,RSCP,ComMod,HSUPA,HSDPA",
+     * ^SMONI: 3G,UARFCN,PSC,EC/n0,RSCP,MCC,MNC,LAC,cell,SQual,SRxLev,PhysCh, SF,Slot,EC/n0,RSCP,ComMod,HSUPA,HSDPA",
+     * ^SMONI: 3G,UARFCN,PSC,EC/n0,RSCP,MCC,MNC,LAC,cell,SQual,SRxLev,PhysCh, SF,Slot,EC/n0,RSCP,ComMod,HSUPA,HSDPA",
+     *
+     * ^SMONI: 3G,10564,296,-7.5,-79,262,02,0143,00228FF,-92,-78,NOCONN
+     *                      ^^^^ ^^^
+     * ^SMONI: 3G,SEARCH,SEARCH
+     * ^SMONI: 3G,10564,96,-7.5,-79,262,02,0143,00228FF,-92,-78,LIMSRV
+     *                     ^^^^ ^^^
+     * ^SMONI: 3G,10737,131,-5,-93,260,01,7D3D,C80BC9A,--,--,----,---,-,-5,-93,0,01,06
+     *                      ^^ ^^^
+     *   RSCP: Received Signal Code Power in dBm -> no need for mm_3gpp_rscp_level_to_rscp
+     *   EC/n0: EC/n0   Carrier to noise ratio in dB = measured Ec/Io value in dB. Please refer to 3GPP 25.133, section 9.1.2.3, Table 9.9 for details on the mapping from EC/n0 to EC/Io.
+     *     -> direct value, without need for mm_3gpp_ecn0_level_to_ecio
+     *
+     *
+     * 4G
+     * ^SMONI: 4G,EARFCN,Band,DL bandwidth,UL bandwidth,Mode,MCC,MNC,TAC,Global Cell ID,Physical Cell ID,Srxlev,RSRP,RSRQ,Conn_state
+     * ^SMONI: 4G,EARFCN,Band,DL bandwidth,UL bandwidth,Mode,MCC,MNC,TAC,Global Cell ID,Physical Cell ID,Srxlev,RSRP,RSRQ,Conn_state
+     * ^SMONI: 4G,EARFCN,Band,DL bandwidth,UL bandwidth,Mode,MCC,MNC,TAC,Global Cell ID,Physical Cell ID,Srxlev,RSRP,RSRQ,Conn_state
+     * ^SMONI: 4G,EARFCN,Band,DL bandwidth,UL bandwidth,Mode,MCC,MNC,TAC,Global Cell ID,Physical Cell ID,TX_power,RSRP,RSRQ,Conn_state
+     *
+     * ^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,33,-94,-7,NOCONN
+     *                                                         ^^^ ^^
+     * ^SMONI: 4G,SEARCH
+     * ^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,33,-94,-7,LIMSRV
+     *                                                         ^^^ ^^
+     * ^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,90,-94,-7,CONN
+     *                                                         ^^^ ^^
+     *  RSRP    Reference Signal Received Power (see 3GPP 36.214 Section 5.1.1.) -> directly the value without mm_3gpp_rsrq_level_to_rsrp
+     *  RSRQ    Reference Signal Received Quality (see 3GPP 36.214 Section 5.1.2.) -> directly the value without mm_3gpp_rsrq_level_to_rsrq
+     */
+    if (g_regex_match_simple ("\\^SMONI:\\s*[234]G,SEARCH", response, 0, 0)) {
+        success = TRUE;
+        goto out;
+    }
+    pre = g_regex_new ("\\^SMONI:\\s*([234])", 0, 0, NULL);
+    g_assert (pre != NULL);
+    g_regex_match_full (pre, response, strlen (response), 0, 0, &match_info_pre, &inner_error);
+    if (!inner_error && g_match_info_matches (match_info_pre)) {
+        if (!mm_get_uint_from_match_info (match_info_pre, 1, &tech)) {
+            inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read tech");
+            goto out;
+        }
+        #define FLOAT "([-+]?[0-9]+\\.?[0-9]*)"
+        switch (tech) {
+        case MM_CINTERION_RADIO_GEN_2G:
+            r = g_regex_new ("\\^SMONI:\\s*2G,(\\d+),"FLOAT, 0, 0, NULL);
+            g_assert (r != NULL);
+            g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+            if (!inner_error && g_match_info_matches (match_info)) {
+                /* skip ARFCN */
+                if (!mm_get_double_from_match_info (match_info, 2, &rssi)) {
+                    inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read BCCH=rssi");
+                    goto out;
+                }
+            }
+            break;
+        case MM_CINTERION_RADIO_GEN_3G:
+            r = g_regex_new ("\\^SMONI:\\s*3G,(\\d+),(\\d+),"FLOAT","FLOAT, 0, 0, NULL);
+            g_assert (r != NULL);
+            g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+            if (!inner_error && g_match_info_matches (match_info)) {
+                /* skip UARFCN */
+                /* skip PSC (Primary scrambling code) */
+                if (!mm_get_double_from_match_info (match_info, 3, &ecn0)) {
+                    inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read EcN0");
+                    goto out;
+                }
+                if (!mm_get_double_from_match_info (match_info, 4, &rscp)) {
+                    inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSCP");
+                    goto out;
+                }
+            }
+            break;
+        case MM_CINTERION_RADIO_GEN_4G:
+            r = g_regex_new ("\\^SMONI:\\s*4G,(\\d+),(\\d+),(\\d+),(\\d+),(\\w+),(\\d+),(\\d+),(\\w+),(\\w+),(\\d+),([^,]*),"FLOAT","FLOAT, 0, 0, NULL);
+            g_assert (r != NULL);
+            g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+            if (!inner_error && g_match_info_matches (match_info)) {
+                /* skip EARFCN */
+                /* skip Band */
+                /* skip DL bandwidth */
+                /* skip UL bandwidth */
+                /* skip Mode */
+                /* skip MCC */
+                /* skip MNC */
+                /* skip TAC */
+                /* skip Global Cell ID */
+                /* skip Physical Cell ID */
+                /* skip Srxlev/TX_power */
+                if (!mm_get_double_from_match_info (match_info, 12, &rsrp)) {
+                    inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSRQ");
+                    goto out;
+                }
+                if (!mm_get_double_from_match_info (match_info, 13, &rsrq)) {
+                    inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSRP");
+                    goto out;
+                }
+            }
+            break;
+        case MM_CINTERION_RADIO_GEN_NONE:
+        default:
+            goto out;
+        }
+        #undef FLOAT
+        success = TRUE;
+    }
+
+out:
+    if (inner_error) {
+        g_propagate_error (error, inner_error);
+        return FALSE;
+    }
+
+    if (!success) {
+        g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+                     "Couldn't parse ^SMONI response: %s", response);
+        return FALSE;
+    }
+
+    *out_tech = tech;
+    *out_rssi = rssi;
+    *out_rscp = rscp;
+    *out_ecn0 = ecn0;
+    *out_rsrq = rsrq;
+    *out_rsrp = rsrp;
+    return TRUE;
+}
+
+/*****************************************************************************/
+/* Get extended signal information */
+
+gboolean
+mm_cinterion_smoni_response_to_signal_info (const gchar  *response,
+                                            MMSignal    **out_gsm,
+                                            MMSignal    **out_umts,
+                                            MMSignal    **out_lte,
+                                            GError      **error)
+{
+    MMCinterionRadioGen     tech    = MM_CINTERION_RADIO_GEN_NONE;
+    gdouble                 rssi    = MM_SIGNAL_UNKNOWN;
+    gdouble                 ecn0    = MM_SIGNAL_UNKNOWN;
+    gdouble                 rscp    = MM_SIGNAL_UNKNOWN;
+    gdouble                 rsrq    = MM_SIGNAL_UNKNOWN;
+    gdouble                 rsrp    = MM_SIGNAL_UNKNOWN;
+    MMSignal               *gsm     = NULL;
+    MMSignal               *umts    = NULL;
+    MMSignal               *lte     = NULL;
+
+    if (!mm_cinterion_parse_smoni_query_response (response,
+                                                  &tech, &rssi,
+                                                  &ecn0, &rscp,
+                                                  &rsrp, &rsrq,
+                                                  error))
+        return FALSE;
+
+    switch (tech) {
+    case MM_CINTERION_RADIO_GEN_2G:
+        gsm = mm_signal_new ();
+        mm_signal_set_rssi (gsm, rssi);
+        break;
+    case MM_CINTERION_RADIO_GEN_3G:
+        umts = mm_signal_new ();
+        mm_signal_set_rscp (umts, rscp);
+        mm_signal_set_ecio (umts, ecn0); /* UMTS EcIo (assumed EcN0) */
+        break;
+    case MM_CINTERION_RADIO_GEN_4G:
+        lte = mm_signal_new ();
+        mm_signal_set_rsrp (lte, rsrp);
+        mm_signal_set_rsrq (lte, rsrq);
+        break;
+    case MM_CINTERION_RADIO_GEN_NONE: /* not registered, searching */
+        break; /* no error case */
+    default: /* should not happen, so if it does, error */
+        g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+                     "Couldn't build detailed signal info");
+        return FALSE;
+    }
+
+    if (out_gsm)
+        *out_gsm = gsm;
+    if (out_umts)
+        *out_umts = umts;
+    if (out_lte)
+        *out_lte = lte;
+
+    return TRUE;
+}
diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.h b/plugins/cinterion/mm-modem-helpers-cinterion.h
index ad6a5fa..c35c5b3 100644
--- a/plugins/cinterion/mm-modem-helpers-cinterion.h
+++ b/plugins/cinterion/mm-modem-helpers-cinterion.h
@@ -26,21 +26,51 @@
 #define _LIBMM_INSIDE_MM
 #include <libmm-glib.h>
 
+typedef enum {
+    MM_CINTERION_MODEM_FAMILY_DEFAULT  = 0,
+    MM_CINTERION_MODEM_FAMILY_IMT      = 1,
+} MMCinterionModemFamily;
+
+typedef enum {
+    MM_CINTERION_RADIO_BAND_FORMAT_SINGLE = 0,
+    MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE  = 1,
+} MMCinterionRadioBandFormat;
+
+typedef enum {
+    MM_CINTERION_RB_BLOCK_LEGACY   = 0,
+    MM_CINTERION_RB_BLOCK_GSM      = 0,
+    MM_CINTERION_RB_BLOCK_UMTS     = 1,
+    MM_CINTERION_RB_BLOCK_LTE_LOW  = 2,
+    MM_CINTERION_RB_BLOCK_LTE_HIGH = 3,
+    MM_CINTERION_RB_BLOCK_N        = 4
+} MMCinterionRbBlock;
+
+typedef enum {
+    MM_CINTERION_RADIO_GEN_NONE    = 0,
+    MM_CINTERION_RADIO_GEN_2G      = 2,
+    MM_CINTERION_RADIO_GEN_3G      = 3,
+    MM_CINTERION_RADIO_GEN_4G      = 4,
+} MMCinterionRadioGen;
+
 /*****************************************************************************/
 /* ^SCFG test parser */
 
-gboolean mm_cinterion_parse_scfg_test (const gchar *response,
-                                       MMModemCharset charset,
-                                       GArray **supported_bands,
-                                       GError **error);
+gboolean mm_cinterion_parse_scfg_test (const gchar                   *response,
+                                       MMCinterionModemFamily         modem_family,
+                                       MMModemCharset                 charset,
+                                       GArray                       **supported_bands,
+                                       MMCinterionRadioBandFormat    *format,
+                                       GError                       **error);
 
 /*****************************************************************************/
 /* ^SCFG response parser */
 
-gboolean mm_cinterion_parse_scfg_response (const gchar *response,
-                                           MMModemCharset charset,
-                                           GArray **bands,
-                                           GError **error);
+gboolean mm_cinterion_parse_scfg_response (const gchar                   *response,
+                                           MMCinterionModemFamily         modem_family,
+                                           MMModemCharset                 charset,
+                                           GArray                       **bands,
+                                           MMCinterionRadioBandFormat     format,
+                                           GError                       **error);
 
 /*****************************************************************************/
 /* +CNMI test parser */
@@ -56,11 +86,13 @@
 /*****************************************************************************/
 /* Build Cinterion-specific band value */
 
-gboolean mm_cinterion_build_band (GArray *bands,
-                                  guint supported,
-                                  gboolean only_2g,
-                                  guint *out_band,
-                                  GError **error);
+gboolean mm_cinterion_build_band (GArray                       *bands,
+                                  guint                        *supported,
+                                  gboolean                      only_2g,
+                                  MMCinterionRadioBandFormat    format,
+                                  MMCinterionModemFamily        modem_family,
+                                  guint                        *out_band,
+                                  GError                      **error);
 
 /*****************************************************************************/
 /* Single ^SIND response parser */
@@ -113,4 +145,22 @@
                                        MMNetworkTimezone **tzp,
                                        GError            **error);
 
+/*****************************************************************************/
+/* ^SMONI helper */
+
+gboolean mm_cinterion_parse_smoni_query_response (const gchar           *response,
+                                                  MMCinterionRadioGen   *out_tech,
+                                                  gdouble               *out_rssi,
+                                                  gdouble               *out_ecn0,
+                                                  gdouble               *out_rscp,
+                                                  gdouble               *out_rsrp,
+                                                  gdouble               *out_rsrq,
+                                                  GError               **error);
+
+gboolean mm_cinterion_smoni_response_to_signal_info (const gchar  *response,
+                                                     MMSignal    **out_gsm,
+                                                     MMSignal    **out_umts,
+                                                     MMSignal    **out_lte,
+                                                     GError      **error);
+
 #endif  /* MM_MODEM_HELPERS_CINTERION_H */
diff --git a/plugins/cinterion/mm-plugin-cinterion.c b/plugins/cinterion/mm-plugin-cinterion.c
index 5fdd48b..f2eb5d7 100644
--- a/plugins/cinterion/mm-plugin-cinterion.c
+++ b/plugins/cinterion/mm-plugin-cinterion.c
@@ -35,6 +35,10 @@
 #include "mm-broadband-modem-qmi-cinterion.h"
 #endif
 
+#if defined WITH_MBIM
+#include "mm-broadband-modem-mbim.h"
+#endif
+
 G_DEFINE_TYPE (MMPluginCinterion, mm_plugin_cinterion, MM_TYPE_PLUGIN)
 
 MM_PLUGIN_DEFINE_MAJOR_VERSION
@@ -124,6 +128,17 @@
     }
 #endif
 
+#if defined WITH_MBIM
+    if (mm_port_probe_list_has_mbim_port (probes)) {
+        mm_obj_dbg (self, "MBIM-powered Cinterion modem found...");
+        return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid,
+                                                           drivers,
+                                                           mm_plugin_get_name (self),
+                                                           vendor,
+                                                           product));
+    }
+#endif
+
     return MM_BASE_MODEM (mm_broadband_modem_cinterion_new (uid,
                                                             drivers,
                                                             mm_plugin_get_name (self),
@@ -182,6 +197,7 @@
                       MM_PLUGIN_ALLOWED_VENDOR_IDS,     vendor_ids,
                       MM_PLUGIN_ALLOWED_AT,             TRUE,
                       MM_PLUGIN_ALLOWED_QMI,            TRUE,
+                      MM_PLUGIN_ALLOWED_MBIM,           TRUE,
                       MM_PLUGIN_CUSTOM_INIT,            &custom_init,
                       NULL));
 }
diff --git a/plugins/cinterion/tests/test-modem-helpers-cinterion.c b/plugins/cinterion/tests/test-modem-helpers-cinterion.c
index 0baeec3..b23a356 100644
--- a/plugins/cinterion/tests/test-modem-helpers-cinterion.c
+++ b/plugins/cinterion/tests/test-modem-helpers-cinterion.c
@@ -20,27 +20,36 @@
 #include <ModemManager.h>
 #define _LIBMM_INSIDE_MM
 #include <libmm-glib.h>
+#include <math.h>
 
 #include "mm-log-test.h"
 #include "mm-modem-helpers.h"
 #include "mm-modem-helpers-cinterion.h"
 
+#define g_assert_cmpfloat_tolerance(val1, val2, tolerance)  \
+    g_assert_cmpfloat (fabs (val1 - val2), <, tolerance)
+
 /*****************************************************************************/
 /* Test ^SCFG test responses */
 
 static void
 common_test_scfg (const gchar *response,
-                  GArray *expected_bands)
+                  GArray *expected_bands,
+                  MMModemCharset charset,
+                  MMCinterionModemFamily modem_family)
 {
     GArray *bands = NULL;
     gchar *expected_bands_str;
     gchar *bands_str;
     GError *error = NULL;
     gboolean res;
+    MMCinterionRadioBandFormat format;
 
     res = mm_cinterion_parse_scfg_test (response,
-                                        MM_MODEM_CHARSET_UNKNOWN,
+                                        modem_family,
+                                        charset,
                                         &bands,
+                                        &format,
                                         &error);
     g_assert_no_error (error);
     g_assert (res == TRUE);
@@ -49,9 +58,9 @@
     mm_common_bands_garray_sort (bands);
     mm_common_bands_garray_sort (expected_bands);
 
-    expected_bands_str = mm_common_build_bands_string ((const MMModemBand *)expected_bands->data,
+    expected_bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)expected_bands->data,
                                                        expected_bands->len);
-    bands_str = mm_common_build_bands_string ((const MMModemBand *)bands->data,
+    bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)bands->data,
                                               bands->len);
 
     /* Instead of comparing the array one by one, compare the strings built from the mask
@@ -107,7 +116,7 @@
     single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single);
     single = MM_MODEM_BAND_UTRAN_6, g_array_append_val (expected_bands, single);
 
-    common_test_scfg (response, expected_bands);
+    common_test_scfg (response, expected_bands, MM_MODEM_CHARSET_UNKNOWN, MM_CINTERION_MODEM_FAMILY_DEFAULT);
 
     g_array_unref (expected_bands);
 }
@@ -175,7 +184,271 @@
     single = MM_MODEM_BAND_UTRAN_1, g_array_append_val (expected_bands, single);
     single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single);
 
-    common_test_scfg (response, expected_bands);
+    common_test_scfg (response, expected_bands, MM_MODEM_CHARSET_UNKNOWN, MM_CINTERION_MODEM_FAMILY_DEFAULT);
+
+    g_array_unref (expected_bands);
+}
+
+static void
+test_scfg_pls62_gsm (void)
+{
+    GArray *expected_bands;
+    MMModemBand single;
+   const gchar *response =
+        "^SCFG: \"MEopMode/Prov/AutoSelect\",(\"off\",\"on\")\r\n"
+        "^SCFG: \"MEopMode/Prov/Cfg\",(\"fallback\",\"attus\")\r\n"
+        "^SCFG: \"Serial/Ifc\",(\"Current\",\"ASC0\",\"USB0\",\"USB1\",\"USB2\",\"MUX1\",\"MUX2\",\"MUX3\",\"0\"),(\"0\",\"3\"),(\"1200\",\"2400\",\"4800\",\"9600\",\"19200\",\"38400\",\"57600\",\"115200\",\"230400\",\"460800\",\"500000\",\"750000\",\"921600\"),(\"0)\r\n"
+        "^SCFG: \"RemoteWakeUp/Ports\",(\"current\",\"powerup\"),(\"asc0\",\"acm1\",\"acm2\",\"acm3\",\"rmnet0\",\"rmnet1\")\r\n"
+        "^SCFG: \"Gpio/mode/ASC1\",(\"std\",\"gpio\",\"rsv\")\r\n"
+        "^SCFG: \"Gpio/mode/DCD0\",(\"std\",\"gpio\",\"rsv\")\r\n"
+        "^SCFG: \"Gpio/mode/DSR0\",(\"std\",\"gpio\",\"rsv\")\r\n"
+        "^SCFG: \"Gpio/mode/DTR0\",(\"std\",\"gpio\",\"rsv\")\r\n"
+        "^SCFG: \"Gpio/mode/FSR\",(\"std\",\"gpio\",\"rsv\")\r\n"
+        "^SCFG: \"Gpio/mode/PULSE\",(\"std\",\"gpio\",\"rsv\")\r\n"
+        "^SCFG: \"Gpio/mode/PWM\",(\"std\",\"gpio\",\"rsv\")\r\n"
+        "^SCFG: \"Gpio/mode/HWAKEUP\",(\"std\",\"gpio\",\"rsv\")\r\n"
+        "^SCFG: \"Gpio/mode/RING0\",(\"std\",\"gpio\",\"rsv\")\r\n"
+        "^SCFG: \"Gpio/mode/SPI\",(\"std\",\"gpio\",\"rsv\")\r\n"
+        "^SCFG: \"Gpio/mode/SYNC\",(\"std\",\"gpio\",\"rsv\")\r\n"
+        "^SCFG: \"GPRS/AutoAttach\",(\"disabled\",\"enabled\")\r\n"
+        "^SCFG: \"Ident/Manufacturer\",(25)\r\n"
+        "^SCFG: \"Ident/Product\",(25)\r\n"
+        "^SCFG: \"MEopMode/SoR\",(\"off\",\"on\")\r\n"
+        "^SCFG: \"MEopMode/CregRoam\",(\"0\",\"1\")\r\n"
+        "^SCFG: \"MeOpMode/SRPOM\",(\"0\",\"1\")\r\n"
+        "^SCFG: \"MEopMode/RingOnData\",(\"off\",\"on\")\r\n"
+        "^SCFG: \"MEShutdown/Fso\",(\"0\",\"1\")\r\n"
+        "^SCFG: \"MEShutdown/sVsup/threshold\",(\"-4\",\"-3\",\"-2\",\"-1\",\"0\",\"1\",\"2\",\"3\",\"4\"),(\"0\")\r\n"
+        "^SCFG: \"Radio/Band/2G\",(\"0x00000004\"-\"0x00000074\")\r\n"
+        "^SCFG: \"Radio/Band/3G\",(\"0x00000001\"-\"0x0004019B\")\r\n"
+        "^SCFG: \"Radio/Band/4G\",(\"0x00000001\"-\"0x080E08DF\")\r\n"
+        "^SCFG: \"Radio/Mtpl/2G\",(\"0\"-\"3\"),(\"1\"-\"8\"),(\"0x00000004\",\"0x00000010\",\"0x00000020\",\"0x00000040\"),,(\"18\"-\"33\"),(\"18\"-\"27\")\r\n"
+        "^SCFG: \"Radio/Mtpl/3G\",(\"0\"-\"3\"),(\"1\"-\"8\"),(\"0x00000001\",\"0x00000002\",\"0x00000008\",\"0x00000010\",\"0x00000080\",\"0x00000100\",\"0x00040000\"),,(\"18\"-\"24\")\r\n"
+        "^SCFG: \"Radio/Mtpl/4G\",(\"0\"-\"3\"),(\"1\"-\"8\"),(\"0x00000001\",\"0x00000002\",\"0x00000004\",\"0x00000008\",\"0x00000010\",\"0x00000040\",\"0x00000080\",\"0x00000800\",\"0x00020000\",\"0x00040000\",\"0x00080000\",\"0x08000000\"),,(\"18)\r\n"
+        "^SCFG: \"Radio/OutputPowerReduction\",(\"0\",\"1\",\"2\",\"3\",\"4\")\r\n"
+        "^SCFG: \"Serial/Interface/Allocation\",(\"0\",\"1\"),(\"0\",\"1\")\r\n"
+        "^SCFG: \"Serial/USB/DDD\",(\"0\",\"1\"),(\"0\"),(4),(4),(4),(63),(63),(4)\r\n"
+        "^SCFG: \"Tcp/IRT\",(\"1\"-\"60\")\r\n"
+        "^SCFG: \"Tcp/MR\",(\"2\"-\"30\")\r\n"
+        "^SCFG: \"Tcp/OT\",(\"1\"-\"6000\")\r\n"
+        "^SCFG: \"Tcp/WithURCs\",(\"on\",\"off\")\r\n"
+        "^SCFG: \"Trace/Syslog/OTAP\",(\"0\",\"1\"),(\"null\",\"asc0\",\"asc1\",\"usb\",\"usb1\",\"usb2\",\"file\",\"system\"),(\"1\"-\"65535\"),(125),(\"buffered\",\"secure\"),(\"off\",\"on\")\r\n"
+        "^SCFG: \"Urc/Ringline\",(\"off\",\"local\",\"asc0\",\"wakeup\")\r\n"
+        "^SCFG: \"Urc/Ringline/ActiveTime\",(\"0\",\"1\",\"2\")\r\n"
+        "^SCFG: \"Userware/Autostart\",(\"0\",\"1\")\r\n"
+        "^SCFG: \"Userware/Autostart/Delay\",(\"0\"-\"10000\")\r\n"
+        "^SCFG: \"Userware/DebugInterface\",(\"0\"-\"255\")|(\"FE80::\"-\"FE80::FFFFFFFFFFFFFFFF\"),(\"0\"-\"255\")|(\"FE80::\"-\"FE80::FFFFFFFFFFFFFFFF\"),(\"0\",\"1\")\r\n"
+        "^SCFG: \"Userware/DebugMode\",(\"off\",\"on\")\r\n"
+        "^SCFG: \"Userware/Passwd\",(\"0\"-\"8\")\r\n"
+        "^SCFG: \"Userware/Stdout\",(\"null\",\"asc0\",\"asc1\",\"usb\",\"usb1\",\"usb2\",\"file\",\"system\"),(\"1\"-\"65535\"),(\"0\"-\"125\"),(\"buffered\",\"secure\"),(\"off\",\"on\")\r\n"
+        "^SCFG: \"Userware/Watchdog\",(\"0\",\"1\",\"2\")\r\n"
+        "^SCFG: \"MEopMode/ExpectDTR\",(\"current\",\"powerup\"),(\"asc0\",\"acm1\",\"acm2\",\"acm3\")\r\n";
+
+    expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 23);
+    single = MM_MODEM_BAND_EGSM,        g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_DCS,         g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_PCS,         g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_G850,        g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_1,     g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_2,     g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_4,     g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_5,     g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_8,     g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_9,     g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_19,    g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_1,    g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_2,    g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_3,    g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_4,    g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_5,    g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_7,    g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_8,    g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_12,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_18,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_19,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_20,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_28,   g_array_append_val (expected_bands, single);
+
+    common_test_scfg (response, expected_bands, MM_MODEM_CHARSET_GSM, MM_CINTERION_MODEM_FAMILY_IMT);
+
+    g_array_unref (expected_bands);
+}
+
+static void
+test_scfg_pls62_ucs2 (void)
+{
+    GArray *expected_bands;
+    MMModemBand single;
+    const gchar *response =
+        "^SCFG: \"MEopMode/Prov/AutoSelect\",(\"006F00660066\",\"006F006E\")\r\n"
+        "^SCFG: \"MEopMode/Prov/Cfg\",(\"fallback\",\"attus\")\r\n"
+        "^SCFG: \"Serial/Ifc\",(\"00430075007200720065006E0074\",\"0041005300430030\",\"0055005300420030\",\"0055005300420031\",\"0055005300420032\",\"004D005500580031\",\"004D005500580032\",\"004D005500580033\",\"0030\"),(\"0030\",\"0033)\r\n"
+        "^SCFG: \"RemoteWakeUp/Ports\",(\"00630075007200720065006E0074\",\"0070006F00770065007200750070\"),(\"0061007300630030\",\"00610063006D0031\",\"00610063006D0032\",\"00610063006D0033\",\"0072006D006E006500740030\",\"0072006D0)\r\n"
+        "^SCFG: \"Gpio/mode/ASC1\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+        "^SCFG: \"Gpio/mode/DCD0\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+        "^SCFG: \"Gpio/mode/DSR0\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+        "^SCFG: \"Gpio/mode/DTR0\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+        "^SCFG: \"Gpio/mode/FSR\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+        "^SCFG: \"Gpio/mode/PULSE\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+        "^SCFG: \"Gpio/mode/PWM\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+        "^SCFG: \"Gpio/mode/HWAKEUP\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+        "^SCFG: \"Gpio/mode/RING0\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+        "^SCFG: \"Gpio/mode/SPI\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+        "^SCFG: \"Gpio/mode/SYNC\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+        "^SCFG: \"GPRS/AutoAttach\",(\"00640069007300610062006C00650064\",\"0065006E00610062006C00650064\")\r\n"
+        "^SCFG: \"Ident/Manufacturer\",(25)\r\n"
+        "^SCFG: \"Ident/Product\",(25)\r\n"
+        "^SCFG: \"MEopMode/SoR\",(\"006F00660066\",\"006F006E\")\r\n"
+        "^SCFG: \"MEopMode/CregRoam\",(\"0030\",\"0031\")\r\n"
+        "^SCFG: \"MeOpMode/SRPOM\",(\"0030\",\"0031\")\r\n"
+        "^SCFG: \"MEopMode/RingOnData\",(\"006F00660066\",\"006F006E\")\r\n"
+        "^SCFG: \"MEShutdown/Fso\",(\"0030\",\"0031\")\r\n"
+        "^SCFG: \"MEShutdown/sVsup/threshold\",(\"002D0034\",\"002D0033\",\"002D0032\",\"002D0031\",\"0030\",\"0031\",\"0032\",\"0033\",\"0034\"),(\"0030\")\r\n"
+        "^SCFG: \"Radio/Band/2G\",(\"0030007800300030003000300030003000300034\"-\"0030007800300030003000300030003000370034\")\r\n"
+        "^SCFG: \"Radio/Band/3G\",(\"0030007800300030003000300030003000300031\"-\"0030007800300030003000340030003100390042\")\r\n"
+        "^SCFG: \"Radio/Band/4G\",(\"0030007800300030003000300030003000300031\"-\"0030007800300038003000450030003800440046\")\r\n"
+        "^SCFG: \"Radio/Mtpl/2G\",(\"00300022002D00220033\"),(\"00310022002D00220038\"),(\"00300078003000300030003000300030003000340022002C002200300078003000300030003000300030003100300022002C0022003000780030003000300030003)\r\n"
+        "^SCFG: \"Radio/Mtpl/3G\",(\"00300022002D00220033\"),(\"00310022002D00220038\"),(\"00300078003000300030003000300030003000310022002C002200300078003000300030003000300030003000320022002C0022003000780030003000300030003)\r\n"
+        "^SCFG: \"Radio/Mtpl/4G\",(\"00300022002D00220033\"),(\"00310022002D00220038\"),(\"00310022002D00220038\"),,(\"003100380022002D002200320033\")\r\n"
+        "^SCFG: \"Radio/OutputPowerReduction\",(\"0030\",\"0031\",\"0032\",\"0033\",\"0034\")\r\n"
+        "^SCFG: \"Serial/Interface/Allocation\",(\"0030\",\"0031\"),(\"0030\",\"0031\")\r\n"
+        "^SCFG: \"Serial/USB/DDD\",(\"0030\",\"0031\"),(\"0030\"),(4),(4),(4),(63),(63),(4)\r\n"
+        "^SCFG: \"Tcp/IRT\",(\"0031\"-\"00360030\")\r\n"
+        "^SCFG: \"Tcp/MR\",(\"0032\"-\"00330030\")\r\n"
+        "^SCFG: \"Tcp/OT\",(\"0031\"-\"0036003000300030\")\r\n"
+        "^SCFG: \"Tcp/WithURCs\",(\"006F006E\",\"006F00660066\")\r\n"
+        "^SCFG: \"Trace/Syslog/OTAP\",(\"0030\",\"0031\"),(\"006E0075006C006C\",\"0061007300630030\",\"0061007300630031\",\"007500730062\",\"0075007300620031\",\"0075007300620032\",\"00660069006C0065\",\"00730079007300740065006D\"),(\"003)\r\n"
+        "^SCFG: \"Urc/Ringline\",(\"006F00660066\",\"006C006F00630061006C\",\"0061007300630030\",\"00770061006B006500750070\")\r\n"
+        "^SCFG: \"Urc/Ringline/ActiveTime\",(\"0030\",\"0031\",\"0032\")\r\n"
+        "^SCFG: \"Userware/Autostart\",(\"0030\",\"0031\")\r\n"
+        "^SCFG: \"Userware/Autostart/Delay\",(\"00300022002D002200310030003000300030\")\r\n"
+        "^SCFG: \"Userware/DebugInterface\",(\"0030\"-\"003200350035\")|(\"0046004500380030003A003A\"-\"0046004500380030003A003A0046004600460046004600460046004600460046004600460046004600460046\"),(\"0030\"-\"003200350035\")|(\"004)\r\n"
+        "^SCFG: \"Userware/DebugMode\",(\"006F00660066\",\"006F006E\")\r\n"
+        "^SCFG: \"Userware/Passwd\",(\"0030\"-\"0038\")\r\n"
+        "^SCFG: \"Userware/Stdout\",(\"006E0075006C006C\",\"0061007300630030\",\"0061007300630031\",\"007500730062\",\"0075007300620031\",\"0075007300620032\",\"00660069006C0065\",\"00730079007300740065006D\"),(\"0031\"-\"00360035003500)\r\n"
+        "^SCFG: \"Userware/Watchdog\",(\"0030\",\"0031\",\"0032\")\r\n"
+        "^SCFG: \"MEopMode/ExpectDTR\",(\"00630075007200720065006E0074\",\"0070006F00770065007200750070\"),(\"0061007300630030\",\"00610063006D0031\",\"00610063006D0032\",\"00610063006D0033\")\r\n";
+
+    expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 23);
+    single = MM_MODEM_BAND_EGSM,        g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_DCS,         g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_PCS,         g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_G850,        g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_1,     g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_2,     g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_4,     g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_5,     g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_8,     g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_9,     g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_19,    g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_1,    g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_2,    g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_3,    g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_4,    g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_5,    g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_7,    g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_8,    g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_12,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_18,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_19,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_20,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_28,   g_array_append_val (expected_bands, single);
+
+    common_test_scfg (response, expected_bands, MM_MODEM_CHARSET_UCS2, MM_CINTERION_MODEM_FAMILY_IMT);
+
+    g_array_unref (expected_bands);
+}
+
+static void
+test_scfg_alas5 (void)
+{
+    GArray *expected_bands;
+    MMModemBand single;
+   const gchar *response =
+        "^SCFG: \"Audio/Loop\",(\"0\",\"1\")\r\n"
+        "^SCFG: \"Audio/SvTone\",(\"0-2047\")\r\n"
+        "^SCFG: \"Call/Ecall/AckTimeout\",(\"0-60000\")\r\n"
+        "^SCFG: \"Call/Ecall/BlockSMSPP\",(\"0\",\"1\")\r\n"
+        "^SCFG: \"Call/Ecall/Callback\",(\"0\",\"1\")\r\n"
+        "^SCFG: \"Call/Ecall/CallbackTimeout\",(\"0-86400000\")\r\n"
+        "^SCFG: \"Call/Ecall/Force\",(\"0\",\"1\",\"2\")\r\n"
+        "^SCFG: \"Call/Ecall/Msd\",(280)\r\n"
+        "^SCFG: \"Call/Ecall/Pullmode\",(\"0\",\"1\")\r\n"
+        "^SCFG: \"Call/Ecall/SessionTimeout\",(\"0-300000\")\r\n"
+        "^SCFG: \"Call/Ecall/StartTimeout\",(\"0-600000\")\r\n"
+        "^SCFG: \"Call/ECC\",(\"0\"-\"255\")\r\n"
+        "^SCFG: \"Call/Speech/Codec\",(\"0\",\"2\")\r\n"
+        "^SCFG: \"GPRS/Auth\",(\"0\",\"1\",\"2\")\r\n"
+        "^SCFG: \"GPRS/AutoAttach\",(\"disabled\",\"enabled\")\r\n"
+        "^SCFG: \"GPRS/MTU/Mode\",(\"0-1\")\r\n"
+        "^SCFG: \"GPRS/MTU/Size\",(\"1280-4096\")\r\n"
+        "^SCFG: \"MEopMode/CFUN\",(\"0\",\"1\")\r\n"
+        "^SCFG: \"MEopMode/CregRoam\",(\"0\",\"1\")\r\n"
+        "^SCFG: \"MEopMode/Dormancy\",(\"0\",\"1\",\"9\")\r\n"
+        "^SCFG: \"MEopMode/DTM/Mode\",(\"0\",\"1\",\"2\")\r\n"
+        "^SCFG: \"MEopMode/ExpectDTR\",(\"current\",\"powerup\"),(\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\")\r\n"
+        "^SCFG: \"MEopMode/FGI/Split\",(\"0\",\"1\")\r\n"
+        "^SCFG: \"MEopMode/IMS\",(\"0\",\"1\")\r\n"
+        "^SCFG: \"MEopMode/NonBlock/Cops\",(\"0\",\"1\")\r\n"
+        "^SCFG: \"MEopMode/PowerMgmt/LCI\",(\"disabled\",\"enabled\"),(\"GPIO1\",\"GPIO3\",\"GPIO4\",\"GPIO5\",\"GPIO6\",\"GPIO7\",\"GPIO8\",\"GPIO11\",\"GPIO12\",\"GPIO13\",\"GPIO14\",\"GPIO15\",\"GPIO16\",\"GPIO17\",\"GPIO22\")\r\n"
+        "^SCFG: \"MEopMode/Prov/AutoFallback\",(\"on\",\"off\")\r\n"
+        "^SCFG: \"MEopMode/Prov/AutoSelect\",(\"on\",\"off\")\r\n"
+        "^SCFG: \"MEopMode/Prov/Cfg\",(\"vdfde\",\"tmode\",\"clarobr\",\"telenorno\",\"telenorse\",\"vdfpt\",\"fallb3gpp*\",\"vdfww\",\"vdfes\",\"swisscomch\",\"eeuk\",\"orangero\",\"orangees\",\"tefde\",\"telenordk\",\"timit\",\"tn1de\",\"tefes\",\"tels)\r\n"
+        "^SCFG: \"MEopMode/PwrSave\",(\"disabled\",\"enabled\"),(\"0-36000\"),(\"0-36000\"),(\"CPU-A\",\"CPU-M\"),(\"powerup\",\"current\")\r\n"
+        "^SCFG: \"MEopMode/SRPOM\",(\"0\",\"1\")\r\n"
+        "^SCFG: \"MEopMode/USB/KeepData\",(\"current\",\"powerup\"),(\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\")\r\n"
+        "^SCFG: \"MEShutdown/OnIgnition\",(\"on\",\"off\")\r\n"
+        "^SCFG: \"MEShutdown/Timer\",(\"off\",\"0\"-\"525600\")\r\n"
+        "^SCFG: \"Misc/CId\",(290)\r\n"
+        "^SCFG: \"Radio/Band/2G\",(\"00000001-0000000f\"),,(\"0\",\"1\")\r\n"
+        "^SCFG: \"Radio/Band/3G\",(\"00000001-000400b5\"),,(\"0\",\"1\")\r\n"
+        "^SCFG: \"Radio/Band/4G\",(\"00000001-8a0e00d5\"),(\"00000002-000001e2\"),(\"0\",\"1\")\r\n"
+        "^SCFG: \"Radio/CNS\",(\"0\",\"1\")\r\n"
+        "^SCFG: \"Radio/Mtpl\",(\"0-1\"),(\"1-8\")\r\n"
+        "^SCFG: \"Radio/Mtpl/2G\",(\"2-3\"),(\"1-8\"),(\"00000001-0000000f\"),,(\"18-33\"),(\"18-27\")\r\n"
+        "^SCFG: \"Radio/Mtpl/3G\",(\"2-3\"),(\"1-8\"),(\"00000001-000000b5\"),,(\"18-24\")\r\n"
+        "^SCFG: \"Radio/Mtpl/4G\",(\"2-3\"),(\"1-8\"),(\"00000001-8a0e00d5\"),(\"00000002-000000e2\"),(\"18-24\")\r\n"
+        "^SCFG: \"Radio/OutputPowerReduction\",(\"4\"-\"8\")\r\n"
+        "^SCFG: \"RemoteWakeUp/Event/ASC\",(\"none\",\"GPIO1\",\"GPIO3\",\"GPIO4\",\"GPIO5\",\"GPIO6\",\"GPIO7\",\"GPIO8\",\"GPIO11\",\"GPIO12\",\"GPIO13\",\"GPIO14\",\"GPIO15\",\"GPIO16\",\"GPIO17\",\"GPIO22\")\r\n"
+        "^SCFG: \"RemoteWakeUp/Event/URC\",(\"none\",\"GPIO1\",\"GPIO3\",\"GPIO4\",\"GPIO5\",\"GPIO6\",\"GPIO7\",\"GPIO8\",\"GPIO11\",\"GPIO12\",\"GPIO13\",\"GPIO14\",\"GPIO15\",\"GPIO16\",\"GPIO17\",\"GPIO22\")\r\n"
+        "^SCFG: \"RemoteWakeUp/Event/USB\",(\"none\",\"GPIO1\",\"GPIO3\",\"GPIO4\",\"GPIO5\",\"GPIO6\",\"GPIO7\",\"GPIO8\",\"GPIO11\",\"GPIO12\",\"GPIO13\",\"GPIO14\",\"GPIO15\",\"GPIO16\",\"GPIO17\",\"GPIO22\")\r\n"
+        "^SCFG: \"RemoteWakeUp/Ports\",(\"current\",\"powerup\"),(\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\")\r\n"
+        "^SCFG: \"RemoteWakeUp/Pulse\",(\"1\"-\"100\")\r\n"
+        "^SCFG: \"Serial/USB/DDD\",(\"0-1\"),(\"0\"),(\"0001-ffff\"),(\"0000-ffff\"),(\"0000-ffff\"),(63),(63),(4)\r\n"
+        "^SCFG: \"SIM/CS\",(\"NOSIM\",\"SIM1\",\"SIM2\")\r\n"
+        "^SCFG: \"SMS/4GPREF\",(\"IMS\",\"CSPS\")\r\n"
+        "^SCFG: \"SMS/AutoAck\",(\"0\",\"1\")\r\n"
+        "^SCFG: \"SMS/RETRM\",(\"1-45\")\r\n"
+        "^SCFG: \"URC/Ringline\",(\"off\",\"local\",\"asc0\")\r\n"
+        "^SCFG: \"URC/Ringline/ActiveTime\",(\"2\",\"on\",\"off\")\r\n";
+
+    expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 23);
+    single = MM_MODEM_BAND_EGSM,      g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_DCS,       g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_PCS,       g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_G850,      g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_1,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_3,   g_array_append_val (expected_bands, single); //
+    single = MM_MODEM_BAND_UTRAN_5,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_6,   g_array_append_val (expected_bands, single); //
+    single = MM_MODEM_BAND_UTRAN_8,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_19,  g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_1,  g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_3,  g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_5,  g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_7,  g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_8,  g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_18, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_19, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_20, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_26, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_28, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_38, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_39, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_40, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_41, g_array_append_val (expected_bands, single);
+
+    common_test_scfg (response, expected_bands, MM_MODEM_CHARSET_GSM, MM_CINTERION_MODEM_FAMILY_DEFAULT);
 
     g_array_unref (expected_bands);
 }
@@ -186,7 +459,9 @@
 static void
 common_test_scfg_response (const gchar *response,
                            MMModemCharset charset,
-                           GArray *expected_bands)
+                           GArray *expected_bands,
+                           MMCinterionModemFamily modem_family,
+                           MMCinterionRadioBandFormat rbf)
 {
     GArray *bands = NULL;
     gchar *expected_bands_str;
@@ -194,7 +469,7 @@
     GError *error = NULL;
     gboolean res;
 
-    res = mm_cinterion_parse_scfg_response (response, charset, &bands, &error);
+    res = mm_cinterion_parse_scfg_response (response, modem_family, charset, &bands, rbf, &error);
     g_assert_no_error (error);
     g_assert (res == TRUE);
     g_assert (bands != NULL);
@@ -202,9 +477,9 @@
     mm_common_bands_garray_sort (bands);
     mm_common_bands_garray_sort (expected_bands);
 
-    expected_bands_str = mm_common_build_bands_string ((const MMModemBand *)expected_bands->data,
+    expected_bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)expected_bands->data,
                                                        expected_bands->len);
-    bands_str = mm_common_build_bands_string ((const MMModemBand *)bands->data,
+    bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)bands->data,
                                               bands->len);
 
     /* Instead of comparing the array one by one, compare the strings built from the mask
@@ -229,7 +504,7 @@
     single = MM_MODEM_BAND_EGSM,  g_array_append_val (expected_bands, single);
     single = MM_MODEM_BAND_DCS,   g_array_append_val (expected_bands, single);
 
-    common_test_scfg_response (response, MM_MODEM_CHARSET_UNKNOWN, expected_bands);
+    common_test_scfg_response (response, MM_MODEM_CHARSET_UNKNOWN, expected_bands, MM_CINTERION_MODEM_FAMILY_DEFAULT, MM_CINTERION_RADIO_BAND_FORMAT_SINGLE);
 
     g_array_unref (expected_bands);
 }
@@ -246,7 +521,7 @@
     expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9);
     single = MM_MODEM_BAND_EGSM,  g_array_append_val (expected_bands, single);
 
-    common_test_scfg_response (response, MM_MODEM_CHARSET_UCS2, expected_bands);
+    common_test_scfg_response (response, MM_MODEM_CHARSET_UCS2, expected_bands, MM_CINTERION_MODEM_FAMILY_DEFAULT, MM_CINTERION_RADIO_BAND_FORMAT_SINGLE);
 
     g_array_unref (expected_bands);
 }
@@ -269,7 +544,252 @@
     single = MM_MODEM_BAND_UTRAN_2, g_array_append_val (expected_bands, single);
     single = MM_MODEM_BAND_UTRAN_5, g_array_append_val (expected_bands, single);
 
-    common_test_scfg_response (response, MM_MODEM_CHARSET_UNKNOWN, expected_bands);
+    common_test_scfg_response (response, MM_MODEM_CHARSET_UNKNOWN, expected_bands, MM_CINTERION_MODEM_FAMILY_DEFAULT, MM_CINTERION_RADIO_BAND_FORMAT_SINGLE);
+
+    g_array_unref (expected_bands);
+}
+
+static void
+test_scfg_response_pls62_gsm (void)
+{
+    GArray *expected_bands;
+    MMModemBand single;
+     const gchar *response =
+        "^SCFG: \"MEopMode/Prov/AutoSelect\",\"off\"\r\n"
+        "^SCFG: \"MEopMode/Prov/Cfg\",\"attus\"\r\n"
+        "^SCFG: \"Serial/Ifc\",\"0\"\r\n"
+        "^SCFG: \"RemoteWakeUp/Ports\",\"current\"\r\n"
+        "^SCFG: \"RemoteWakeUp/Ports\",\"powerup\"\r\n"
+        "^SCFG: \"Gpio/mode/ASC1\",\"gpio\"\r\n"
+        "^SCFG: \"Gpio/mode/DCD0\",\"gpio\"\r\n"
+        "^SCFG: \"Gpio/mode/DSR0\",\"gpio\"\r\n"
+        "^SCFG: \"Gpio/mode/DTR0\",\"gpio\"\r\n"
+        "^SCFG: \"Gpio/mode/FSR\",\"gpio\"\r\n"
+        "^SCFG: \"Gpio/mode/PULSE\",\"gpio\"\r\n"
+        "^SCFG: \"Gpio/mode/PWM\",\"gpio\"\r\n"
+        "^SCFG: \"Gpio/mode/HWAKEUP\",\"gpio\"\r\n"
+        "^SCFG: \"Gpio/mode/RING0\",\"gpio\"\r\n"
+        "^SCFG: \"Gpio/mode/SPI\",\"gpio\"\r\n"
+        "^SCFG: \"Gpio/mode/SYNC\",\"gpio\"\r\n"
+        "^SCFG: \"GPRS/AutoAttach\",\"enabled\"\r\n"
+        "^SCFG: \"Ident/Manufacturer\",\"Cinterion\"\r\n"
+        "^SCFG: \"Ident/Product\",\"PLS62-W\"\r\n"
+        "^SCFG: \"MEopMode/SoR\",\"off\"\r\n"
+        "^SCFG: \"MEopMode/CregRoam\",\"0\"\r\n"
+        "^SCFG: \"MeOpMode/SRPOM\",\"0\"\r\n"
+        "^SCFG: \"MEopMode/RingOnData\",\"off\"\r\n"
+        "^SCFG: \"MEShutdown/Fso\",\"0\"\r\n"
+        "^SCFG: \"MEShutdown/sVsup/threshold\",\"0\",\"0\"\r\n"
+        "^SCFG: \"Radio/Band/2G\",\"0x00000014\"\r\n"
+        "^SCFG: \"Radio/Band/3G\",\"0x00000182\"\r\n"
+        "^SCFG: \"Radio/Band/4G\",\"0x080E0000\"\r\n"
+        "^SCFG: \"Radio/Mtpl/2G\",\"0\"\r\n"
+        "^SCFG: \"Radio/Mtpl/3G\",\"0\"\r\n"
+        "^SCFG: \"Radio/Mtpl/4G\",\"0\"\r\n"
+        "^SCFG: \"Radio/OutputPowerReduction\",\"4\"\r\n"
+        "^SCFG: \"Serial/Interface/Allocation\",\"0\",\"0\"\r\n"
+        "^SCFG: \"Serial/USB/DDD\",\"0\",\"0\",\"0409\",\"1E2D\",\"005B\",\"Cinterion Wireless Modules\",\"PLSx\",\"\"\r\n"
+        "^SCFG: \"Tcp/IRT\",\"3\"\r\n"
+        "^SCFG: \"Tcp/MR\",\"10\"\r\n"
+        "^SCFG: \"Tcp/OT\",\"6000\"\r\n"
+        "^SCFG: \"Tcp/WithURCs\",\"on\"\r\n"
+        "^SCFG: \"Trace/Syslog/OTAP\",\"0\"\r\n"
+        "^SCFG: \"Urc/Ringline\",\"local\"\r\n"
+        "^SCFG: \"Urc/Ringline/ActiveTime\",\"2\"\r\n"
+        "^SCFG: \"Userware/Autostart\",\"0\"\r\n"
+        "^SCFG: \"Userware/Autostart/Delay\",\"0\"\r\n"
+        "^SCFG: \"Userware/DebugInterface\",\"0.0.0.0\",\"0.0.0.0\",\"0\"\r\n"
+        "^SCFG: \"Userware/DebugMode\",\"off\"\r\n"
+        "^SCFG: \"Userware/Passwd\",\r\n"
+        "^SCFG: \"Userware/Stdout\",\"null\",,,,\"off\"\r\n"
+        "^SCFG: \"Userware/Watchdog\",\"0\"\r\n"
+        "^SCFG: \"MEopMode/ExpectDTR\",\"current\"\r\n"
+        "^SCFG: \"MEopMode/ExpectDTR\",\"powerup\"\r\n";
+
+    expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9);
+    single = MM_MODEM_BAND_EGSM,      g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_DCS,       g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_2,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_8,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_9,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_18,  g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_19,  g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_20, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_28,  g_array_append_val (expected_bands, single);
+
+    common_test_scfg_response (response, MM_MODEM_CHARSET_GSM, expected_bands, MM_CINTERION_MODEM_FAMILY_IMT, MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE);
+
+    g_array_unref (expected_bands);
+}
+
+static void
+test_scfg_response_pls62_ucs2 (void)
+{
+    GArray *expected_bands;
+    MMModemBand single;
+     const gchar *response =
+        "^SCFG: \"MEopMode/Prov/AutoSelect\",\"006F00660066\"\r\n"
+        "^SCFG: \"MEopMode/Prov/Cfg\",\"00610074007400750073\"\r\n"
+        "^SCFG: \"Serial/Ifc\",\"0\"\r\n"
+        "^SCFG: \"RemoteWakeUp/Ports\",\"00630075007200720065006E0074\"\r\n"
+        "^SCFG: \"RemoteWakeUp/Ports\",\"0070006F00770065007200750070\"\r\n"
+        "^SCFG: \"Gpio/mode/ASC1\",\"006700700069006F\"\r\n"
+        "^SCFG: \"Gpio/mode/DCD0\",\"006700700069006F\"\r\n"
+        "^SCFG: \"Gpio/mode/DSR0\",\"006700700069006F\"\r\n"
+        "^SCFG: \"Gpio/mode/DTR0\",\"006700700069006F\"\r\n"
+        "^SCFG: \"Gpio/mode/FSR\",\"006700700069006F\"\r\n"
+        "^SCFG: \"Gpio/mode/PULSE\",\"006700700069006F\"\r\n"
+        "^SCFG: \"Gpio/mode/PWM\",\"006700700069006F\"\r\n"
+        "^SCFG: \"Gpio/mode/HWAKEUP\",\"006700700069006F\"\r\n"
+        "^SCFG: \"Gpio/mode/RING0\",\"006700700069006F\"\r\n"
+        "^SCFG: \"Gpio/mode/SPI\",\"006700700069006F\"\r\n"
+        "^SCFG: \"Gpio/mode/SYNC\",\"006700700069006F\"\r\n"
+        "^SCFG: \"GPRS/AutoAttach\",\"0065006E00610062006C00650064\"\r\n"
+        "^SCFG: \"Ident/Manufacturer\",\"Cinterion\"\r\n"
+        "^SCFG: \"Ident/Product\",\"PLS62-W\"\r\n"
+        "^SCFG: \"MEopMode/SoR\",\"006F00660066\"\r\n"
+        "^SCFG: \"MEopMode/CregRoam\",\"0030\"\r\n"
+        "^SCFG: \"MeOpMode/SRPOM\",\"0030\"\r\n"
+        "^SCFG: \"MEopMode/RingOnData\",\"006F00660066\"\r\n"
+        "^SCFG: \"MEShutdown/Fso\",\"0030\"\r\n"
+        "^SCFG: \"MEShutdown/sVsup/threshold\",\"0030\",\"0030\"\r\n"
+        "^SCFG: \"Radio/Band/2G\",\"0030007800300030003000300030003000310034\"\r\n"
+        "^SCFG: \"Radio/Band/3G\",\"0030007800300030003000300030003100380032\"\r\n"
+        "^SCFG: \"Radio/Band/4G\",\"0030007800300038003000450030003000300030\"\r\n"
+        "^SCFG: \"Radio/Mtpl/2G\",\"0030\"\r\n"
+        "^SCFG: \"Radio/Mtpl/3G\",\"0030\"\r\n"
+        "^SCFG: \"Radio/Mtpl/4G\",\"0030\"\r\n"
+        "^SCFG: \"Radio/OutputPowerReduction\",\"0034\"\r\n"
+        "^SCFG: \"Serial/Interface/Allocation\",\"0030\",\"0030\"\r\n"
+        "^SCFG: \"Serial/USB/DDD\",\"0030\",\"0030\",\"0030003400300039\",\"0031004500320044\",\"0030003000350042\",\"00430069006E0074006500720069006F006E00200057006900720065006C0065007300730020004D006F00640075006C00650073\",\"005\"\r\n"
+        "^SCFG: \"Tcp/IRT\",\"0033\"\r\n"
+        "^SCFG: \"Tcp/MR\",\"00310030\"\r\n"
+        "^SCFG: \"Tcp/OT\",\"0036003000300030\"\r\n"
+        "^SCFG: \"Tcp/WithURCs\",\"006F006E\"\r\n"
+        "^SCFG: \"Trace/Syslog/OTAP\",\"0030\"\r\n"
+        "^SCFG: \"Urc/Ringline\",\"006C006F00630061006C\"\r\n"
+        "^SCFG: \"Urc/Ringline/ActiveTime\",\"0032\"\r\n"
+        "^SCFG: \"Userware/Autostart\",\"0030\"\r\n"
+        "^SCFG: \"Userware/Autostart/Delay\",\"0030\"\r\n"
+        "^SCFG: \"Userware/DebugInterface\",\"0030002E0030002E0030002E0030\",\"0030002E0030002E0030002E0030\",\"0030\"\r\n"
+        "^SCFG: \"Userware/DebugMode\",\"006F00660066\"\r\n"
+        "^SCFG: \"Userware/Passwd\",\r\n"
+        "^SCFG: \"Userware/Stdout\",\"006E0075006C006C\",,,,\"006F00660066\"\r\n"
+        "^SCFG: \"Userware/Watchdog\",\"0030\"\r\n"
+        "^SCFG: \"MEopMode/ExpectDTR\",\"00630075007200720065006E0074\"\r\n"
+        "^SCFG: \"MEopMode/ExpectDTR\",\"0070006F00770065007200750070\"\r\n";
+
+    expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9);
+    single = MM_MODEM_BAND_EGSM,      g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_DCS,       g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_2,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_8,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_9,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_18,  g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_19,  g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_20, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_28,  g_array_append_val (expected_bands, single);
+
+    common_test_scfg_response (response, MM_MODEM_CHARSET_UCS2, expected_bands, MM_CINTERION_MODEM_FAMILY_IMT, MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE);
+
+    g_array_unref (expected_bands);
+}
+
+static void
+test_scfg_response_alas5 (void)
+{
+    GArray *expected_bands;
+    MMModemBand single;
+     const gchar *response =
+        "^SCFG: \"Audio/Loop\",\"0\"\r\n"
+        "^SCFG: \"Audio/SvTone\",\"0\"\r\n"
+        "^SCFG: \"Call/Ecall/AckTimeout\",\"5000\"\r\n"
+        "^SCFG: \"Call/Ecall/BlockSMSPP\",\"0\"\r\n"
+        "^SCFG: \"Call/Ecall/Callback\",\"0\"\r\n"
+        "^SCFG: \"Call/Ecall/CallbackTimeout\",\"43200000\"\r\n"
+        "^SCFG: \"Call/Ecall/Force\",\"1\"\r\n"
+        "^SCFG: \"Call/Ecall/Msd\",\"\"\r\n"
+        "^SCFG: \"Call/Ecall/Pullmode\",\"0\"\r\n"
+        "^SCFG: \"Call/Ecall/SessionTimeout\",\"20000\"\r\n"
+        "^SCFG: \"Call/Ecall/StartTimeout\",\"5000\"\r\n"
+        "^SCFG: \"Call/ECC\",\"0\"\r\n"
+        "^SCFG: \"Call/Speech/Codec\",\"0\"\r\n"
+        "^SCFG: \"GPRS/Auth\",\"2\"\r\n"
+        "^SCFG: \"GPRS/AutoAttach\",\"enabled\"\r\n"
+        "^SCFG: \"GPRS/MTU/Mode\",\"0\"\r\n"
+        "^SCFG: \"GPRS/MTU/Size\",1500\r\n"
+        "^SCFG: \"MEopMode/CFUN\",\"1\",\"1\"\r\n"
+        "^SCFG: \"MEopMode/CregRoam\",\"0\"\r\n"
+        "^SCFG: \"MEopMode/Dormancy\",\"0\",\"0\"\r\n"
+        "^SCFG: \"MEopMode/DTM/Mode\",\"2\"\r\n"
+        "^SCFG: \"MEopMode/ExpectDTR\",\"current\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"mbim\",\"asc0\"\r\n"
+        "^SCFG: \"MEopMode/ExpectDTR\",\"powerup\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"mbim\",\"asc0\"\r\n"
+        "^SCFG: \"MEopMode/FGI/Split\",\"1\"\r\n"
+        "^SCFG: \"MEopMode/IMS\",\"1\"\r\n"
+        "^SCFG: \"MEopMode/NonBlock/Cops\",\"0\"\r\n"
+        "^SCFG: \"MEopMode/PowerMgmt/LCI\",\"disabled\"\r\n"
+        "^SCFG: \"MEopMode/Prov/AutoFallback\",\"off\"\r\n"
+        "^SCFG: \"MEopMode/Prov/AutoSelect\",\"on\"\r\n"
+        "^SCFG: \"MEopMode/Prov/Cfg\",\"vdfde\"\r\n"
+        "^SCFG: \"MEopMode/PwrSave\",\"enabled\",\"52\",\"50\",\"CPU-A\",\"powerup\"\r\n"
+        "^SCFG: \"MEopMode/PwrSave\",\"enabled\",\"52\",\"50\",\"CPU-A\",\"current\"\r\n"
+        "^SCFG: \"MEopMode/PwrSave\",\"enabled\",\"0\",\"0\",\"CPU-M\",\"powerup\"\r\n"
+        "^SCFG: \"MEopMode/PwrSave\",\"enabled\",\"0\",\"0\",\"CPU-M\",\"current\"\r\n"
+        "^SCFG: \"MEopMode/SRPOM\",\"0\"\r\n"
+        "^SCFG: \"MEopMode/USB/KeepData\",\"current\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\"\r\n"
+        "^SCFG: \"MEopMode/USB/KeepData\",\"powerup\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\"\r\n"
+        "^SCFG: \"MEShutdown/OnIgnition\",\"off\"\r\n"
+        "^SCFG: \"MEShutdown/Timer\",\"off\"\r\n"
+        "^SCFG: \"Misc/CId\",\"\"\r\n"
+        "^SCFG: \"Radio/Band/2G\",\"0000000f\"\r\n"
+        "^SCFG: \"Radio/Band/3G\",\"000400b5\"\r\n"
+        "^SCFG: \"Radio/Band/4G\",\"8a0e00d5\",\"000000e2\"\r\n"
+        "^SCFG: \"Radio/CNS\",\"0\"\r\n"
+        "^SCFG: \"Radio/Mtpl\",\"0\"\r\n"
+        "^SCFG: \"Radio/Mtpl/2G\",\"0\"\r\n"
+        "^SCFG: \"Radio/Mtpl/3G\",\"0\"\r\n"
+        "^SCFG: \"Radio/Mtpl/4G\",\"0\"\r\n"
+        "^SCFG: \"Radio/OutputPowerReduction\",\"4\"\r\n"
+        "^SCFG: \"RemoteWakeUp/Event/ASC\",\"none\"\r\n"
+        "^SCFG: \"RemoteWakeUp/Event/URC\",\"none\"\r\n"
+        "^SCFG: \"RemoteWakeUp/Event/USB\",\"GPIO4\"\r\n"
+        "^SCFG: \"RemoteWakeUp/Ports\",\"current\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\"\r\n"
+        "^SCFG: \"RemoteWakeUp/Ports\",\"powerup\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\"\r\n"
+        "^SCFG: \"RemoteWakeUp/Pulse\",\"10\"\r\n"
+        "^SCFG: \"Serial/USB/DDD\",\"0\",\"0\",\"0409\",\"1e2d\",\"0065\",\"Cinterion\",\"LTE Modem\",\"8d8f\"\r\n"
+        "^SCFG: \"SIM/CS\",\"SIM1\"\r\n"
+        "^SCFG: \"SMS/4GPREF\",\"IMS\"\r\n"
+        "^SCFG: \"SMS/AutoAck\",\"0\"\r\n"
+        "^SCFG: \"SMS/RETRM\",\"30\"\r\n"
+        "^SCFG: \"URC/Ringline\",\"local\"\r\n"
+        "^SCFG: \"URC/Ringline/ActiveTime\",\"2\"\r\n";
+
+    expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 25);
+    single = MM_MODEM_BAND_EGSM,      g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_DCS,       g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_PCS,       g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_G850,      g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_1,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_3,   g_array_append_val (expected_bands, single); //
+    single = MM_MODEM_BAND_UTRAN_5,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_6,   g_array_append_val (expected_bands, single); //
+    single = MM_MODEM_BAND_UTRAN_8,   g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_UTRAN_19,  g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_1,  g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_3,  g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_5,  g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_7,  g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_8,  g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_18, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_19, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_20, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_26, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_28, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_38, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_39, g_array_append_val (expected_bands, single);
+    single = MM_MODEM_BAND_EUTRAN_40, g_array_append_val (expected_bands, single);
+
+    common_test_scfg_response (response, MM_MODEM_CHARSET_GSM, expected_bands, MM_CINTERION_MODEM_FAMILY_DEFAULT, MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE);
 
     g_array_unref (expected_bands);
 }
@@ -857,6 +1377,244 @@
 }
 
 /*****************************************************************************/
+/* Test ^SMONI responses */
+
+typedef struct {
+    const gchar            *str;
+    MMCinterionRadioGen     tech;
+    gdouble                 rssi;
+    gdouble                 ecn0;
+    gdouble                 rscp;
+    gdouble                 rsrp;
+    gdouble                 rsrq;
+} SMoniResponseTest;
+
+static const SMoniResponseTest smoni_response_tests[] = {
+    {
+        .str       = "^SMONI: 2G,71,-61,262,02,0143,83BA,33,33,3,6,G,NOCONN",
+        .tech      = MM_CINTERION_RADIO_GEN_2G,
+        .rssi      = -61.0,
+        .ecn0      = 0.0,
+        .rscp      = 0.0,
+        .rsrp      = 0.0,
+        .rsrq      = 0.0
+    },
+    {
+        .str       = "^SMONI: 2G,SEARCH,SEARCH",
+        .tech      = MM_CINTERION_RADIO_GEN_NONE,
+        .rssi      = 0.0,
+        .ecn0      = 0.0,
+        .rscp      = 0.0,
+        .rsrp      = 0.0,
+        .rsrq      = 0.0
+    },
+    {
+        .str       = "^SMONI: 2G,673,-89,262,07,4EED,A500,16,16,7,4,G,5,-107,LIMSRV",
+        .tech      = MM_CINTERION_RADIO_GEN_2G,
+        .rssi      =  -89.0,
+        .ecn0      = 0.0,
+        .rscp      = 0.0,
+        .rsrp      = 0.0,
+        .rsrq      = 0.0
+    },
+    {
+        .str       = "^SMONI: 2G,673,-80,262,07,4EED,A500,35,35,7,4,G,643,4,0,-80,0,S_FR",
+        .tech      = MM_CINTERION_RADIO_GEN_2G,
+        .rssi      = -80.0,
+        .ecn0      = 0.0,
+        .rscp      = 0.0,
+        .rsrp      = 0.0,
+        .rsrq      = 0.0
+    },
+    {
+        .str       = "^SMONI: 3G,10564,296,-7.5,-79,262,02,0143,00228FF,-92,-78,NOCONN",
+        .tech      = MM_CINTERION_RADIO_GEN_3G,
+        .rssi      = 0.0,
+        .ecn0      = -7.5,
+        .rscp      = -79.0,
+        .rsrp      = 0.0,
+        .rsrq      = 0.0
+    },
+    {
+        .str       = "^SMONI: 3G,SEARCH,SEARCH",
+        .tech      = MM_CINTERION_RADIO_GEN_NONE,
+        .rssi      = 0.0,
+        .ecn0      = 0,
+        .rscp      = 0,
+        .rsrp      = 0.0,
+        .rsrq      = 0.0
+    },
+    {
+        .str       = "^SMONI: 3G,10564,96,-6.5,-77,262,02,0143,00228FF,-92,-78,LIMSRV",
+        .tech      = MM_CINTERION_RADIO_GEN_3G,
+        .rssi      =  0.0,
+        .ecn0      = -6.5,
+        .rscp      = -77.0,
+        .rsrp      = 0.0,
+        .rsrq      = 0.0
+    },
+    {
+        .str       = "^SMONI: 3G,10737,131,-5,-93,260,01,7D3D,C80BC9A,--,--,----,---,-,-5,-93,0,01,06",
+        .tech      = MM_CINTERION_RADIO_GEN_3G,
+        .rssi      = 0.0,
+        .ecn0      = -5.0,
+        .rscp      = -93.0,
+        .rsrp      = 0.0,
+        .rsrq      = 0.0
+    },
+    {
+        .str       = "^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,33,-94,-7,NOCONN",
+        .tech      = MM_CINTERION_RADIO_GEN_4G,
+        .rssi      = 0.0,
+        .ecn0      = 0.0,
+        .rscp      = 0.0,
+        .rsrp      = -94.0,
+        .rsrq      = -7.0
+    },
+    {
+        .str       = "^SMONI: 4G,SEARCH",
+        .tech      = MM_CINTERION_RADIO_GEN_NONE,
+        .rssi      = 0.0,
+        .ecn0      = 0.0,
+        .rscp      = 0.0,
+        .rsrp      = 0.0,
+        .rsrq      = 0.0
+    },
+    {
+        .str       = "^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,33,-90,-6,LIMSRV",
+        .tech      = MM_CINTERION_RADIO_GEN_4G,
+        .rssi      = 0.0,
+        .ecn0      = 0.0,
+        .rscp      = 0.0,
+        .rsrp      = -90.0,
+        .rsrq      = -6.0
+    },
+    {
+        .str       = "^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,90,-101,-7,CONN",
+        .tech      = MM_CINTERION_RADIO_GEN_4G,
+        .rssi      = 0.0,
+        .ecn0      = 0.0,
+        .rscp      = 0.0,
+        .rsrp      = -101.0,
+        .rsrq      = -7.0
+    },
+    {
+        .str       = "^SMONI: 4G,2850,7,20,20,FDD,262,02,C096,027430F,275,11,-114,-9,NOCONN",
+        .tech      = MM_CINTERION_RADIO_GEN_4G,
+        .rssi      = 0.0,
+        .ecn0      = 0.0,
+        .rscp      = 0.0,
+        .rsrp      = -114.0,
+        .rsrq      = -9.0
+    },
+    {
+        .str       = "^SMONI: 4G,2850,7,20,20,FDD,262,02,C096,027430F,275,-,-113,-8,CONN",
+        .tech      = MM_CINTERION_RADIO_GEN_4G,
+        .rssi      = 0.0,
+        .ecn0      = 0.0,
+        .rscp      = 0.0,
+        .rsrp      = -113.0,
+        .rsrq      = -8.0
+    }
+};
+
+static void
+test_smoni_response (void)
+{
+    guint i;
+
+    for (i = 0; i < G_N_ELEMENTS (smoni_response_tests); i++) {
+        GError                 *error = NULL;
+        gboolean                success;
+        MMCinterionRadioGen     tech = MM_CINTERION_RADIO_GEN_NONE;
+        gdouble                 rssi = MM_SIGNAL_UNKNOWN;
+        gdouble                 ecn0 = MM_SIGNAL_UNKNOWN;
+        gdouble                 rscp = MM_SIGNAL_UNKNOWN;
+        gdouble                 rsrp = MM_SIGNAL_UNKNOWN;
+        gdouble                 rsrq = MM_SIGNAL_UNKNOWN;
+
+        success = mm_cinterion_parse_smoni_query_response (smoni_response_tests[i].str,
+                                                            &tech, &rssi,
+                                                            &ecn0, &rscp,
+                                                            &rsrp, &rsrq,
+                                                            &error);
+        g_assert_no_error (error);
+        g_assert (success);
+
+        g_assert_cmpuint (smoni_response_tests[i].tech,      ==, tech);
+        switch (smoni_response_tests[i].tech) {
+        case MM_CINTERION_RADIO_GEN_2G:
+            g_assert_cmpfloat_tolerance (rssi, smoni_response_tests[i].rssi, 0.1);
+            break;
+        case MM_CINTERION_RADIO_GEN_3G:
+            g_assert_cmpfloat_tolerance (ecn0, smoni_response_tests[i].ecn0, 0.1);
+            g_assert_cmpfloat_tolerance (rscp, smoni_response_tests[i].rscp, 0.1);
+            break;
+        case MM_CINTERION_RADIO_GEN_4G:
+            g_assert_cmpfloat_tolerance (rsrp, smoni_response_tests[i].rsrp, 0.1);
+            g_assert_cmpfloat_tolerance (rsrq, smoni_response_tests[i].rsrq, 0.1);
+            break;
+        case MM_CINTERION_RADIO_GEN_NONE:
+        default:
+            break;
+        }
+    }
+}
+
+static void
+test_smoni_response_to_signal (void)
+{
+    guint i;
+
+    for (i = 0; i < G_N_ELEMENTS (smoni_response_tests); i++) {
+        GError   *error = NULL;
+        gboolean  success;
+        MMSignal *gsm  = NULL;
+        MMSignal *umts = NULL;
+        MMSignal *lte  = NULL;
+
+        success = mm_cinterion_smoni_response_to_signal_info (smoni_response_tests[i].str,
+                                                              &gsm, &umts, &lte,
+                                                              &error);
+        g_assert_no_error (error);
+        g_assert (success);
+
+        switch (smoni_response_tests[i].tech) {
+        case MM_CINTERION_RADIO_GEN_2G:
+            g_assert (gsm);
+            g_assert_cmpfloat_tolerance (mm_signal_get_rssi (gsm), smoni_response_tests[i].rssi, 0.1);
+            g_object_unref (gsm);
+            g_assert (!umts);
+            g_assert (!lte);
+            break;
+        case MM_CINTERION_RADIO_GEN_3G:
+            g_assert (umts);
+            g_assert_cmpfloat_tolerance (mm_signal_get_rscp (umts), smoni_response_tests[i].rscp, 0.1);
+            g_assert_cmpfloat_tolerance (mm_signal_get_ecio (umts), smoni_response_tests[i].ecn0, 0.1);
+            g_object_unref (umts);
+            g_assert (!gsm);
+            g_assert (!lte);
+            break;
+        case MM_CINTERION_RADIO_GEN_4G:
+            g_assert (lte);
+            g_assert_cmpfloat_tolerance (mm_signal_get_rsrp (lte), smoni_response_tests[i].rsrp, 0.1);
+            g_assert_cmpfloat_tolerance (mm_signal_get_rsrq (lte), smoni_response_tests[i].rsrq, 0.1);
+            g_object_unref (lte);
+            g_assert (!gsm);
+            g_assert (!umts);
+            break;
+        case MM_CINTERION_RADIO_GEN_NONE:
+        default:
+            g_assert (!gsm);
+            g_assert (!umts);
+            g_assert (!lte);
+            break;
+        }
+    }
+}
+
+
+/*****************************************************************************/
 
 int main (int argc, char **argv)
 {
@@ -866,9 +1624,15 @@
 
     g_test_add_func ("/MM/cinterion/scfg",                    test_scfg);
     g_test_add_func ("/MM/cinterion/scfg/ehs5",               test_scfg_ehs5);
+    g_test_add_func ("/MM/cinterion/scfg/pls62/gsm",          test_scfg_pls62_gsm);
+    g_test_add_func ("/MM/cinterion/scfg/pls62/ucs2",         test_scfg_pls62_ucs2);
+    g_test_add_func ("/MM/cinterion/scfg/alas5",              test_scfg_alas5);
     g_test_add_func ("/MM/cinterion/scfg/response/3g",        test_scfg_response_3g);
     g_test_add_func ("/MM/cinterion/scfg/response/2g",        test_scfg_response_2g);
     g_test_add_func ("/MM/cinterion/scfg/response/2g/ucs2",   test_scfg_response_2g_ucs2);
+    g_test_add_func ("/MM/cinterion/scfg/response/pls62/gsm", test_scfg_response_pls62_gsm);
+    g_test_add_func ("/MM/cinterion/scfg/response/pls62/ucs2",test_scfg_response_pls62_ucs2);
+    g_test_add_func ("/MM/cinterion/scfg/response/alas5",     test_scfg_response_alas5);
     g_test_add_func ("/MM/cinterion/cnmi/phs8",               test_cnmi_phs8);
     g_test_add_func ("/MM/cinterion/cnmi/other",              test_cnmi_other);
     g_test_add_func ("/MM/cinterion/swwan/pls8",              test_swwan_pls8);
@@ -881,6 +1645,8 @@
     g_test_add_func ("/MM/cinterion/slcc/urc/complex",        test_slcc_urc_complex);
     g_test_add_func ("/MM/cinterion/ctzu/urc/simple",         test_ctzu_urc_simple);
     g_test_add_func ("/MM/cinterion/ctzu/urc/full",           test_ctzu_urc_full);
+    g_test_add_func ("/MM/cinterion/smoni/query_response",    test_smoni_response);
+    g_test_add_func ("/MM/cinterion/smoni/query_response_to_signal", test_smoni_response_to_signal);
 
     return g_test_run ();
 }
diff --git a/plugins/huawei/mm-broadband-bearer-huawei.c b/plugins/huawei/mm-broadband-bearer-huawei.c
index 81a2e5a..ca20c65 100644
--- a/plugins/huawei/mm-broadband-bearer-huawei.c
+++ b/plugins/huawei/mm-broadband-bearer-huawei.c
@@ -37,8 +37,6 @@
 struct _MMBroadbandBearerHuaweiPrivate {
     gpointer connect_pending;
     gpointer disconnect_pending;
-    /* Tag for the post task for network-initiated disconnect */
-    guint network_disconnect_pending_id;
 };
 
 /*****************************************************************************/
@@ -342,11 +340,6 @@
         return;
     }
 
-    /* Network-initiated disconnect should not be outstanding at this point,
-     * because it interferes with the connect attempt.
-     */
-    g_assert (self->priv->network_disconnect_pending_id == 0);
-
     switch (ctx->step) {
     case CONNECT_3GPP_CONTEXT_STEP_FIRST: {
         MMBearerIpFamily ip_family;
@@ -658,7 +651,6 @@
 {
     GTask *task;
     Disconnect3gppContext *ctx;
-    GError *error = NULL;
 
     task = self->priv->disconnect_pending;
     g_assert (task != NULL);
@@ -668,13 +660,11 @@
     /* Balance refcount */
     g_object_unref (self);
 
-    if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
-        /* Clear task */
-        self->priv->disconnect_pending = NULL;
-        g_task_return_error (task, error);
-        g_object_unref (task);
-        return;
-    }
+    /* Running NDISDUP=1,0 on an already disconnected bearer/context will
+     * return ERROR! Ignore errors in the NDISDUP disconnection command,
+     * because we're anyway going to check the bearer/context status
+     * afterwards.  */
+    mm_base_modem_at_command_full_finish (modem, res, NULL);
 
     /* Go to next step */
     ctx->step++;
@@ -694,15 +684,6 @@
     case DISCONNECT_3GPP_CONTEXT_STEP_FIRST:
         /* Store the task */
         self->priv->disconnect_pending = task;
-
-        /* We ignore any pending network-initiated disconnection in order to prevent it
-         * from interfering with the client-initiated disconnection, as we would like to
-         * proceed with the latter anyway. */
-        if (self->priv->network_disconnect_pending_id != 0) {
-            g_source_remove (self->priv->network_disconnect_pending_id);
-            self->priv->network_disconnect_pending_id = 0;
-        }
-
         ctx->step++;
         /* fall through */
 
@@ -804,17 +785,6 @@
 
 /*****************************************************************************/
 
-static gboolean
-network_disconnect_3gpp_delayed (MMBroadbandBearerHuawei *self)
-{
-    mm_obj_dbg (self, "disconnect bearer on network request");
-
-    self->priv->network_disconnect_pending_id = 0;
-    mm_base_bearer_report_connection_status (MM_BASE_BEARER (self),
-                                             MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
-    return G_SOURCE_REMOVE;
-}
-
 static void
 report_connection_status (MMBaseBearer *bearer,
                           MMBearerConnectionStatus status)
@@ -837,23 +807,6 @@
     if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED)
         return;
 
-    /* We already use ^NDISSTATQRY? to poll the connection status, so only
-     * handle network-initiated disconnection here. */
-    if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTING) {
-        /* MM_BEARER_CONNECTION_STATUS_DISCONNECTING is used to indicate that the
-         * reporting of disconnection should be delayed. See MMBroadbandModemHuawei's
-         * bearer_report_connection_status for details. */
-        if (mm_base_bearer_get_status (bearer) == MM_BEARER_STATUS_CONNECTED &&
-            self->priv->network_disconnect_pending_id == 0) {
-            mm_obj_dbg (self, "delay network-initiated disconnection of bearer");
-            self->priv->network_disconnect_pending_id = (g_timeout_add_seconds (
-                                                             4,
-                                                             (GSourceFunc) network_disconnect_3gpp_delayed,
-                                                             self));
-        }
-        return;
-    }
-
     /* Report disconnected right away */
     MM_BASE_BEARER_CLASS (mm_broadband_bearer_huawei_parent_class)->report_connection_status (
         bearer,
@@ -882,19 +835,6 @@
     return MM_BASE_BEARER (bearer);
 }
 
-static void
-dispose (GObject *object)
-{
-    MMBroadbandBearerHuawei *self = MM_BROADBAND_BEARER_HUAWEI (object);
-
-    if (self->priv->network_disconnect_pending_id != 0) {
-        g_source_remove (self->priv->network_disconnect_pending_id);
-        self->priv->network_disconnect_pending_id = 0;
-    }
-
-    G_OBJECT_CLASS (mm_broadband_bearer_huawei_parent_class)->dispose (object);
-}
-
 void
 mm_broadband_bearer_huawei_new (MMBroadbandModemHuawei *modem,
                                 MMBearerProperties *config,
@@ -931,8 +871,6 @@
 
     g_type_class_add_private (object_class, sizeof (MMBroadbandBearerHuaweiPrivate));
 
-    object_class->dispose = dispose;
-
     base_bearer_class->report_connection_status = report_connection_status;
     base_bearer_class->load_connection_status = NULL;
     base_bearer_class->load_connection_status_finish = NULL;
diff --git a/plugins/huawei/mm-broadband-modem-huawei.c b/plugins/huawei/mm-broadband-modem-huawei.c
index bf009f2..a676b7a 100644
--- a/plugins/huawei/mm-broadband-modem-huawei.c
+++ b/plugins/huawei/mm-broadband-modem-huawei.c
@@ -128,6 +128,7 @@
     GRegex *ecclist_regex;
     GRegex *ltersrp_regex;
     GRegex *cschannelinfo_regex;
+    GRegex *ccallstate_regex;
     GRegex *eons_regex;
 
     FeatureSupport ndisdup_support;
@@ -913,7 +914,7 @@
 
     task = g_task_new (self, NULL, callback, user_data);
 
-    bands_string = mm_common_build_bands_string ((MMModemBand *)bands_array->data,
+    bands_string = mm_common_build_bands_string ((MMModemBand *)(gpointer)bands_array->data,
                                                  bands_array->len);
 
     if (!bands_array_to_huawei (bands_array, &huawei_band)) {
@@ -1647,14 +1648,11 @@
 {
     if (ndisstat_result->ipv4_available) {
         /* TODO: MMBroadbandBearerHuawei does not currently support IPv6.
-         * When it does, we should check the IP family associated with each bearer.
-         *
-         * Also, send DISCONNECTING so that we give some time before actually
-         * disconnecting the connection */
+         * When it does, we should check the IP family associated with each bearer. */
         mm_base_bearer_report_connection_status (bearer,
                                                  ndisstat_result->ipv4_connected ?
                                                  MM_BEARER_CONNECTION_STATUS_CONNECTED :
-                                                 MM_BEARER_CONNECTION_STATUS_DISCONNECTING);
+                                                 MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
     }
 }
 
@@ -1792,6 +1790,7 @@
         g_free (str);
         return;
     }
+    g_free (str);
 
     detailed_signal_clear (&self->priv->detailed_signal);
 
@@ -2227,7 +2226,7 @@
 
     found = peek_port_at_for_data (self, port);
     if (!found)
-        mm_obj_warn (self, "couldn't find associated cdc-wdm port for %s", mm_port_get_device (port));
+        mm_obj_dbg (self, "couldn't find associated cdc-wdm port for %s", mm_port_get_device (port));
     return found;
 }
 
@@ -4406,6 +4405,10 @@
             NULL, NULL, NULL);
         mm_port_serial_at_add_unsolicited_msg_handler (
             port,
+            self->priv->ccallstate_regex,
+            NULL, NULL, NULL);
+        mm_port_serial_at_add_unsolicited_msg_handler (
+            port,
             self->priv->eons_regex,
             NULL, NULL, NULL);
     }
@@ -4541,6 +4544,8 @@
                                              G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
     self->priv->cschannelinfo_regex = g_regex_new ("\\r\\n\\^CSCHANNELINFO:.+\\r\\n",
                                                     G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+    self->priv->ccallstate_regex = g_regex_new ("\\r\\n\\^CCALLSTATE:.+\\r\\n",
+                                                G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
     self->priv->eons_regex = g_regex_new ("\\r\\n\\^EONS:.+\\r\\n",
                                           G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
@@ -4600,6 +4605,7 @@
     g_regex_unref (self->priv->ecclist_regex);
     g_regex_unref (self->priv->ltersrp_regex);
     g_regex_unref (self->priv->cschannelinfo_regex);
+    g_regex_unref (self->priv->ccallstate_regex);
     g_regex_unref (self->priv->eons_regex);
 
     if (self->priv->syscfg_supported_modes)
diff --git a/plugins/huawei/mm-modem-helpers-huawei.c b/plugins/huawei/mm-modem-helpers-huawei.c
index a1422be..f8baf82 100644
--- a/plugins/huawei/mm-modem-helpers-huawei.c
+++ b/plugins/huawei/mm-modem-helpers-huawei.c
@@ -163,6 +163,7 @@
     gchar buf[9];
     gsize len, bin_len;
     gboolean success = FALSE;
+    guint32 aux;
 
     s = g_match_info_fetch (match_info, match_index);
     g_return_val_if_fail (s != NULL, FALSE);
@@ -190,7 +191,8 @@
     if (!bin || bin_len != 4)
         goto done;
 
-    *out_addr = GUINT32_SWAP_LE_BE (*((guint32 *) bin));
+    memcpy (&aux, bin, 4);
+    *out_addr = GUINT32_SWAP_LE_BE (aux);
     success = TRUE;
 
 done:
diff --git a/plugins/novatel/mm-broadband-modem-novatel-lte.c b/plugins/novatel/mm-broadband-modem-novatel-lte.c
index d2a3bde..bcb509f 100644
--- a/plugins/novatel/mm-broadband-modem-novatel-lte.c
+++ b/plugins/novatel/mm-broadband-modem-novatel-lte.c
@@ -223,25 +223,25 @@
                                            GVariant **result,
                                            GError **result_error)
 {
-    GArray *array;
-    GStrv own_numbers;
-    gchar *mdn;
+    g_auto(GStrv)  own_numbers = NULL;
+    GPtrArray     *array;
+    gchar         *mdn;
 
     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 FALSE;
     }
 
     mdn = g_strdup (mm_strip_tag (response, "$NWMDN:"));
-    array = g_array_new (TRUE, TRUE, sizeof (gchar *));
-    g_array_append_val (array, mdn);
-    own_numbers = (GStrv) g_array_free (array, FALSE);
+
+    array = g_ptr_array_new ();
+    g_ptr_array_add (array, mdn);
+    g_ptr_array_add (array, NULL);
+    own_numbers = (GStrv) g_ptr_array_free (array, FALSE);
 
     *result = g_variant_new_strv ((const gchar *const *) own_numbers, -1);
-    g_strfreev (own_numbers);
     return TRUE;
 }
 
diff --git a/plugins/quectel/77-mm-quectel-port-types.rules b/plugins/quectel/77-mm-quectel-port-types.rules
index 7113858..caa7b10 100644
--- a/plugins/quectel/77-mm-quectel-port-types.rules
+++ b/plugins/quectel/77-mm-quectel-port-types.rules
@@ -27,6 +27,16 @@
 ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0191", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
 ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0191", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
 
+# Quectel EG95
+#  ttyUSB0 (if #0): QCDM/DIAG port
+#  ttyUSB1 (if #1): GPS data port
+#  ttyUSB2 (if #2): AT primary port
+#  ttyUSB3 (if #3): AT secondary port
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0195", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0195", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0195", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0195", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
 # Quectel BG96
 #  ttyUSB0 (if #0): QCDM/DIAG port
 #  ttyUSB1 (if #1): GPS data port
diff --git a/plugins/telit/77-mm-telit-port-types.rules b/plugins/telit/77-mm-telit-port-types.rules
index 6ec5a7e..edacd9e 100644
--- a/plugins/telit/77-mm-telit-port-types.rules
+++ b/plugins/telit/77-mm-telit-port-types.rules
@@ -40,6 +40,13 @@
 ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
 ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
 
+# LE910C1 with default usb cfg
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+
 # ME910
 ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1101", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
 ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1101", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
diff --git a/plugins/telit/mm-modem-helpers-telit.c b/plugins/telit/mm-modem-helpers-telit.c
index f77afee..b0d843b 100644
--- a/plugins/telit/mm-modem-helpers-telit.c
+++ b/plugins/telit/mm-modem-helpers-telit.c
@@ -253,7 +253,7 @@
         if (flag2g == -1) {
             gchar *bands_str;
 
-            bands_str = mm_common_build_bands_string ((const MMModemBand *)(bands_array->data), bands_array->len);
+            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);
@@ -272,7 +272,7 @@
         if (flag3g == -1) {
             gchar *bands_str;
 
-            bands_str = mm_common_build_bands_string ((const MMModemBand *)(bands_array->data), bands_array->len);
+            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);
diff --git a/plugins/tests/test-helpers.c b/plugins/tests/test-helpers.c
index 1fe2cdc..8148d90 100644
--- a/plugins/tests/test-helpers.c
+++ b/plugins/tests/test-helpers.c
@@ -38,12 +38,12 @@
 
     g_assert (bands);
     mm_common_bands_garray_sort (bands);
-    bands_str = mm_common_build_bands_string ((MMModemBand *)(bands->data), bands->len);
+    bands_str = mm_common_build_bands_string ((MMModemBand *)(gpointer)(bands->data), bands->len);
 
     expected_bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), n_expected_bands);
     g_array_append_vals (expected_bands_array, expected_bands, n_expected_bands);
     mm_common_bands_garray_sort (expected_bands_array);
-    expected_bands_str = mm_common_build_bands_string ((MMModemBand *)(expected_bands_array->data), expected_bands_array->len);
+    expected_bands_str = mm_common_build_bands_string ((MMModemBand *)(gpointer)(expected_bands_array->data), expected_bands_array->len);
     g_array_unref (expected_bands_array);
 
     g_assert_cmpstr (bands_str, ==, expected_bands_str);
diff --git a/plugins/ublox/77-mm-ublox-port-types.rules b/plugins/ublox/77-mm-ublox-port-types.rules
index 9f8263e..c2a1ac9 100644
--- a/plugins/ublox/77-mm-ublox-port-types.rules
+++ b/plugins/ublox/77-mm-ublox-port-types.rules
@@ -7,6 +7,13 @@
 
 SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
 
+# Fully ignore u-blox GPS devices
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a5", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a6", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a7", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a8", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a9", ENV{ID_MM_DEVICE_IGNORE}="1"
+
 # Toby-L4 port types
 #  ttyACM0 (if #2): secondary (ignore)
 #  ttyACM1 (if #4): debug port (ignore)
diff --git a/plugins/ublox/mm-modem-helpers-ublox.c b/plugins/ublox/mm-modem-helpers-ublox.c
index fe1719c..ca3039a 100644
--- a/plugins/ublox/mm-modem-helpers-ublox.c
+++ b/plugins/ublox/mm-modem-helpers-ublox.c
@@ -1609,7 +1609,7 @@
         gchar *tmpstr;
 
         bands = uact_num_array_to_band_array (nums);
-        tmpstr = mm_common_build_bands_string ((MMModemBand *)(bands->data), bands->len);
+        tmpstr = mm_common_build_bands_string ((MMModemBand *)(gpointer)(bands->data), bands->len);
         mm_obj_dbg (log_object, "modem reports support for %s bands: %s", group, tmpstr);
         g_free (tmpstr);
 
diff --git a/plugins/wavecom/mm-broadband-modem-wavecom.c b/plugins/wavecom/mm-broadband-modem-wavecom.c
index 8362055..43919ed 100644
--- a/plugins/wavecom/mm-broadband-modem-wavecom.c
+++ b/plugins/wavecom/mm-broadband-modem-wavecom.c
@@ -217,7 +217,7 @@
                            MMModemMode   *preferred,
                            GError       **error)
 {
-    LoadCurrentModesResult *result;
+    g_autofree LoadCurrentModesResult *result = NULL;
 
     result = g_task_propagate_pointer (G_TASK (res), error);
     if (!result)
@@ -225,7 +225,6 @@
 
     *allowed   = result->allowed;
     *preferred = result->preferred;
-    g_free (result);
     return TRUE;
 }
 
@@ -234,11 +233,11 @@
                  GAsyncResult *res,
                  GTask        *task)
 {
-    GRegex                 *r;
-    GMatchInfo             *match_info = NULL;
-    LoadCurrentModesResult *result;
-    const gchar            *response;
-    GError                 *error = NULL;
+    g_autoptr(GRegex)                  r = NULL;
+    g_autoptr(GMatchInfo)              match_info = NULL;
+    g_autofree LoadCurrentModesResult *result = NULL;
+    const gchar                       *response;
+    GError                            *error = NULL;
 
     response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
     if (!response) {
@@ -305,19 +304,15 @@
         }
     }
 
-    if (result->allowed == MM_MODEM_MODE_NONE) {
+    if (result->allowed == MM_MODEM_MODE_NONE)
         g_task_return_new_error (task,
                                  MM_CORE_ERROR,
                                  MM_CORE_ERROR_FAILED,
                                  "Unknown wireless data service reply: '%s'",
                                  response);
-        g_free (result);
-    } else
-        g_task_return_pointer (task, result, g_free);
+    else
+        g_task_return_pointer (task, g_steal_pointer (&result), g_free);
     g_object_unref (task);
-
-    g_regex_unref (r);
-    g_match_info_free (match_info);
 }
 
 static void
@@ -325,9 +320,9 @@
                         GAsyncResult *res,
                         GTask        *task)
 {
-    LoadCurrentModesResult  result;
-    const gchar            *response;
-    GError                 *error = NULL;
+    g_autofree LoadCurrentModesResult *result = NULL;
+    const gchar                       *response;
+    GError                            *error = NULL;
 
     response = mm_base_modem_at_command_finish (self, res, &error);
     if (!response) {
@@ -352,37 +347,38 @@
         return;
     }
 
-    result.allowed = MM_MODEM_MODE_NONE;
-    result.preferred = MM_MODEM_MODE_NONE;
+    result = g_new0 (LoadCurrentModesResult, 1);
+    result->allowed = MM_MODEM_MODE_NONE;
+    result->preferred = MM_MODEM_MODE_NONE;
 
     if (strncmp (response,
                  WAVECOM_MS_CLASS_B_IDSTR,
                  strlen (WAVECOM_MS_CLASS_B_IDSTR)) == 0) {
         mm_obj_dbg (self, "configured as a Class B mobile station");
-        result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_CS);
-        result.preferred = MM_MODEM_MODE_2G;
+        result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_CS);
+        result->preferred = MM_MODEM_MODE_2G;
     } else if (strncmp (response,
                         WAVECOM_MS_CLASS_CG_IDSTR,
                         strlen (WAVECOM_MS_CLASS_CG_IDSTR)) == 0) {
         mm_obj_dbg (self, "configured as a Class CG mobile station");
-        result.allowed = MM_MODEM_MODE_2G;
-        result.preferred = MM_MODEM_MODE_NONE;
+        result->allowed = MM_MODEM_MODE_2G;
+        result->preferred = MM_MODEM_MODE_NONE;
     } else if (strncmp (response,
                         WAVECOM_MS_CLASS_CC_IDSTR,
                         strlen (WAVECOM_MS_CLASS_CC_IDSTR)) == 0) {
         mm_obj_dbg (self, "configured as a Class CC mobile station");
-        result.allowed = MM_MODEM_MODE_CS;
-        result.preferred = MM_MODEM_MODE_NONE;
+        result->allowed = MM_MODEM_MODE_CS;
+        result->preferred = MM_MODEM_MODE_NONE;
     }
 
-    if (result.allowed == MM_MODEM_MODE_NONE)
+    if (result->allowed == MM_MODEM_MODE_NONE)
         g_task_return_new_error (task,
                                  MM_CORE_ERROR,
                                  MM_CORE_ERROR_FAILED,
                                  "Unknown mobile station class: '%s'",
                                  response);
     else
-        g_task_return_boolean (task, TRUE);
+        g_task_return_pointer (task, g_steal_pointer (&result), g_free);
     g_object_unref (task);
 }
 
@@ -522,8 +518,8 @@
     }
 
     if (!ctx->cgclass_command) {
-        gchar *allowed_str;
-        gchar *preferred_str;
+        g_autofree gchar *allowed_str = NULL;
+        g_autofree gchar *preferred_str = NULL;
 
         allowed_str = mm_modem_mode_build_string_from_mask (allowed);
         preferred_str = mm_modem_mode_build_string_from_mask (preferred);
@@ -534,8 +530,6 @@
                                  "supported by the modem.",
                                  allowed_str, preferred_str);
         g_object_unref (task);
-        g_free (allowed_str);
-        g_free (preferred_str);
         return;
     }
 
@@ -766,11 +760,10 @@
               GArray *bands_array)
 {
     MMBroadbandModemWavecom *self;
-    GArray                  *bands_array_final;
     guint                    wavecom_band = 0;
     guint                    i;
-    gchar                   *bands_string;
-    gchar                   *cmd;
+    g_autoptr(GArray)        bands_array_final = NULL;
+    g_autofree gchar        *cmd = NULL;
 
     self = g_task_get_source_object (task);
 
@@ -797,18 +790,16 @@
         }
     }
 
-    bands_string = mm_common_build_bands_string ((MMModemBand *)bands_array_final->data,
-                                                 bands_array_final->len);
-    g_array_unref (bands_array_final);
-
     if (wavecom_band == 0) {
+        g_autofree gchar *bands_string = NULL;
+
+        bands_string = mm_common_build_bands_string ((MMModemBand *)(gpointer)bands_array_final->data, bands_array_final->len);
         g_task_return_new_error (task,
                                  MM_CORE_ERROR,
                                  MM_CORE_ERROR_UNSUPPORTED,
                                  "The given band combination is not supported: '%s'",
                                  bands_string);
         g_object_unref (task);
-        g_free (bands_string);
         return;
     }
 
@@ -819,8 +810,6 @@
                               FALSE,
                               (GAsyncReadyCallback)wmbs_set_ready,
                               task);
-    g_free (cmd);
-    g_free (bands_string);
 }
 
 static void
@@ -828,11 +817,10 @@
               GArray *bands_array)
 {
     MMBroadbandModemWavecom *self;
-    GArray                  *bands_array_final;
     gchar                    wavecom_band = '\0';
     guint                    i;
-    gchar                   *bands_string;
-    gchar                   *cmd;
+    g_autofree gchar        *cmd = NULL;
+    g_autoptr(GArray)        bands_array_final = NULL;
 
     self = g_task_get_source_object (task);
 
@@ -857,30 +845,28 @@
         bands_array_final = g_array_ref (bands_array);
 
     for (i = 0; wavecom_band == '\0' && i < G_N_ELEMENTS (bands_2g); i++) {
-        GArray *supported_combination;
+        g_autoptr(GArray) supported_combination = NULL;
 
         supported_combination = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), bands_2g[i].n_mm_bands);
         g_array_append_vals (supported_combination, bands_2g[i].mm_bands, bands_2g[i].n_mm_bands);
 
         /* Check if the given array is exactly one of the supported combinations */
-        if (mm_common_bands_garray_cmp (bands_array_final, supported_combination))
+        if (mm_common_bands_garray_cmp (bands_array_final, supported_combination)) {
             wavecom_band = bands_2g[i].wavecom_band;
-
-        g_array_unref (supported_combination);
+            break;
+        }
     }
 
-    bands_string = mm_common_build_bands_string ((MMModemBand *)bands_array_final->data,
-                                                 bands_array_final->len);
-    g_array_unref (bands_array_final);
-
     if (wavecom_band == '\0') {
+        g_autofree gchar *bands_string = NULL;
+
+        bands_string = mm_common_build_bands_string ((MMModemBand *)(gpointer)bands_array_final->data, bands_array_final->len);
         g_task_return_new_error (task,
                                  MM_CORE_ERROR,
                                  MM_CORE_ERROR_UNSUPPORTED,
                                  "The given band combination is not supported: '%s'",
                                  bands_string);
         g_object_unref (task);
-        g_free (bands_string);
         return;
     }
 
@@ -891,9 +877,6 @@
                               FALSE,
                               (GAsyncReadyCallback)wmbs_set_ready,
                               task);
-
-    g_free (cmd);
-    g_free (bands_string);
 }
 
 static void
@@ -1039,9 +1022,8 @@
 parse_network_registration_mode (const gchar *reply,
                                  guint       *mode)
 {
-    GRegex     *r;
-    GMatchInfo *match_info;
-    gboolean    parsed = FALSE;
+    g_autoptr(GRegex)     r = NULL;
+    g_autoptr(GMatchInfo) match_info = NULL;
 
     g_assert (mode != NULL);
 
@@ -1052,14 +1034,8 @@
     g_assert (r != NULL);
 
     g_regex_match (r, reply, 0, &match_info);
-    if (g_match_info_matches (match_info) &&
-        mm_get_uint_from_match_info (match_info, 1, mode))
-        parsed = TRUE;
 
-    g_match_info_free (match_info);
-    g_regex_unref (r);
-
-    return parsed;
+    return (g_match_info_matches (match_info) && mm_get_uint_from_match_info (match_info, 1, mode));
 }
 
 static void
@@ -1072,14 +1048,18 @@
     guint        mode;
 
     response = mm_base_modem_at_command_finish (self, res, &error);
-    if (!response)
-        goto out;
+    if (!response) {
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
 
     if (!parse_network_registration_mode (response, &mode)) {
-        error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
-                             "Couldn't parse current network registration mode: '%s'",
-                             response);
-        goto out;
+        g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+                                 "Couldn't parse current network registration mode: '%s'",
+                                 response);
+        g_object_unref (task);
+        return;
     }
 
     /* If the modem is not configured for automatic registration, run parent */
@@ -1089,12 +1069,7 @@
     }
 
     mm_obj_dbg (self, "device is already in automatic registration mode, not requesting it again");
-
-out:
-    if (error)
-        g_task_return_error (task, error);
-    else
-        g_task_return_boolean (task, TRUE);
+    g_task_return_boolean (task, TRUE);
     g_object_unref (task);
 }
 
diff --git a/plugins/xmm/mm-shared-xmm.c b/plugins/xmm/mm-shared-xmm.c
index 6045758..746aff3 100644
--- a/plugins/xmm/mm-shared-xmm.c
+++ b/plugins/xmm/mm-shared-xmm.c
@@ -509,7 +509,7 @@
     if (unapplied_bands->len > 0) {
         gchar *str;
 
-        str = mm_common_build_bands_string ((const MMModemBand *)unapplied_bands->data, unapplied_bands->len);
+        str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)unapplied_bands->data, unapplied_bands->len);
         inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
                                    "Cannot update bands for modes not currently allowed: %s", str);
         g_free (str);
diff --git a/plugins/zte/77-mm-zte-port-types.rules b/plugins/zte/77-mm-zte-port-types.rules
index f213195..4d2ffc1 100644
--- a/plugins/zte/77-mm-zte-port-types.rules
+++ b/plugins/zte/77-mm-zte-port-types.rules
@@ -181,6 +181,9 @@
 ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1254", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
 ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1254", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
 
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1268", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1268", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
 ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1515", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
 ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1515", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
 
diff --git a/src/77-mm-usb-device-blacklist.rules b/src/77-mm-usb-device-blacklist.rules
index 983fe43..6ad4f87 100644
--- a/src/77-mm-usb-device-blacklist.rules
+++ b/src/77-mm-usb-device-blacklist.rules
@@ -81,6 +81,9 @@
 ATTRS{idVendor}=="1b4f", ATTRS{idProduct}=="9207", ENV{ID_MM_TTY_BLACKLIST}="1"
 ATTRS{idVendor}=="1b4f", ATTRS{idProduct}=="9208", ENV{ID_MM_TTY_BLACKLIST}="1"
 
+# Chinese clone of Arduino nano with a LGT8F328P MCU
+ATTRS{idVendor}=="04d9", ATTRS{idProduct}=="b534", ENV{ID_MM_TTY_BLACKLIST}="1"
+
 # Adafruit Flora
 ATTRS{idVendor}=="239a", ATTRS{idProduct}=="0004", ENV{ID_MM_TTY_BLACKLIST}="1"
 ATTRS{idVendor}=="239a", ATTRS{idProduct}=="8004", ENV{ID_MM_TTY_BLACKLIST}="1"
@@ -106,11 +109,6 @@
 # PS-360 OEM (GPS sold with MS Street and Trips 2005)
 ATTRS{idVendor}=="067b", ATTRS{idProduct}=="aaa0", ENV{ID_MM_TTY_BLACKLIST}="1"
 
-# u-blox AG, u-blox 5 GPS chips
-ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a5", ENV{ID_MM_TTY_BLACKLIST}="1"
-ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a6", ENV{ID_MM_TTY_BLACKLIST}="1"
-ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a7", ENV{ID_MM_TTY_BLACKLIST}="1"
-
 # Garmin GPS devices
 DRIVERS=="garmin_gps", ENV{ID_MM_TTY_BLACKLIST}="1"
 
@@ -208,4 +206,8 @@
 # All devices from Prusa Research
 ATTRS{idVendor}=="2c99", ENV{ID_MM_TTY_BLACKLIST}="1"
 
+# USB-CEC adapters
+ATTRS{idVendor}=="2548", ATTRS{idProduct}=="1001", ENV{ID_MM_TTY_BLACKLIST}="1"
+ATTRS{idVendor}=="2548", ATTRS{idProduct}=="1002", ENV{ID_MM_TTY_BLACKLIST}="1"
+
 LABEL="mm_usb_device_blacklist_end"
diff --git a/src/kerneldevice/mm-kernel-device.c b/src/kerneldevice/mm-kernel-device.c
index abe0ddd..fa86539 100644
--- a/src/kerneldevice/mm-kernel-device.c
+++ b/src/kerneldevice/mm-kernel-device.c
@@ -203,6 +203,9 @@
     g_return_val_if_fail (MM_IS_KERNEL_DEVICE (a), FALSE);
     g_return_val_if_fail (MM_IS_KERNEL_DEVICE (b), FALSE);
 
+    if (G_OBJECT_TYPE (a) != G_OBJECT_TYPE (b))
+        return G_OBJECT_TYPE (a) < G_OBJECT_TYPE (b);
+
     return (MM_KERNEL_DEVICE_GET_CLASS (a)->cmp ?
             MM_KERNEL_DEVICE_GET_CLASS (a)->cmp (a, b) :
             FALSE);
diff --git a/src/mm-bearer-qmi.c b/src/mm-bearer-qmi.c
index 4ec93d7..1afb6c2 100644
--- a/src/mm-bearer-qmi.c
+++ b/src/mm-bearer-qmi.c
@@ -542,7 +542,7 @@
     GError *error = NULL;
     QmiMessageWdsStartNetworkOutput *output;
 
-    self = g_task_get_task_data (task);
+    self = g_task_get_source_object (task);
     ctx  = g_task_get_task_data (task);
 
     g_assert (ctx->running_ipv4 || ctx->running_ipv6);
@@ -1372,8 +1372,7 @@
 
     case CONNECT_STEP_IP_FAMILY_IPV4:
         /* If client is new enough, select IP family */
-        if (!ctx->no_ip_family_preference &&
-            qmi_client_check_version (QMI_CLIENT (ctx->client_ipv4), 1, 9)) {
+        if (!ctx->no_ip_family_preference) {
             QmiMessageWdsSetIpFamilyInput *input;
 
             mm_obj_dbg (self, "setting default IP family to: IPv4");
@@ -1467,31 +1466,23 @@
         ctx->step++;
     } /* fall through */
 
-    case CONNECT_STEP_IP_FAMILY_IPV6:
+    case CONNECT_STEP_IP_FAMILY_IPV6: {
+        QmiMessageWdsSetIpFamilyInput *input;
 
         g_assert (ctx->no_ip_family_preference == FALSE);
 
-        /* If client is new enough, select IP family */
-        if (qmi_client_check_version (QMI_CLIENT (ctx->client_ipv6), 1, 9)) {
-            QmiMessageWdsSetIpFamilyInput *input;
-
-            mm_obj_dbg (self, "setting default IP family to: IPv6");
-            input = qmi_message_wds_set_ip_family_input_new ();
-            qmi_message_wds_set_ip_family_input_set_preference (input, QMI_WDS_IP_FAMILY_IPV6, NULL);
-            qmi_client_wds_set_ip_family (ctx->client_ipv6,
-                                          input,
-                                          10,
-                                          g_task_get_cancellable (task),
-                                          (GAsyncReadyCallback)set_ip_family_ready,
-                                          task);
-            qmi_message_wds_set_ip_family_input_unref (input);
-            return;
-        }
-
-        ctx->default_ip_family_set = FALSE;
-
-        ctx->step++;
-        /* fall through */
+        mm_obj_dbg (self, "setting default IP family to: IPv6");
+        input = qmi_message_wds_set_ip_family_input_new ();
+        qmi_message_wds_set_ip_family_input_set_preference (input, QMI_WDS_IP_FAMILY_IPV6, NULL);
+        qmi_client_wds_set_ip_family (ctx->client_ipv6,
+                                      input,
+                                      10,
+                                      g_task_get_cancellable (task),
+                                      (GAsyncReadyCallback)set_ip_family_ready,
+                                      task);
+        qmi_message_wds_set_ip_family_input_unref (input);
+        return;
+    }
 
     case CONNECT_STEP_ENABLE_INDICATIONS_IPV6:
         common_setup_cleanup_packet_service_status_unsolicited_events (ctx->self,
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c
index 0d86a5c..a2acb07 100644
--- a/src/mm-broadband-modem-qmi.c
+++ b/src/mm-broadband-modem-qmi.c
@@ -128,6 +128,7 @@
 
     /* 3GPP USSD helpers */
     guint ussd_indication_id;
+    guint ussd_release_indication_id;
     gboolean ussd_unsolicited_events_enabled;
     gboolean ussd_unsolicited_events_setup;
     GTask *pending_ussd_action;
@@ -1714,24 +1715,20 @@
     mm_obj_dbg (self, "loading signal quality...");
 
 #if defined WITH_NEWEST_QMI_COMMANDS
-    /* Signal info introduced in NAS 1.8 */
-    if (qmi_client_check_version (client, 1, 8)) {
-        qmi_client_nas_get_signal_info (QMI_CLIENT_NAS (client),
-                                        NULL,
-                                        10,
-                                        NULL,
-                                        (GAsyncReadyCallback)get_signal_info_ready,
-                                        task);
-        return;
-    }
-#endif /* WITH_NEWEST_QMI_COMMANDS */
-
+    qmi_client_nas_get_signal_info (QMI_CLIENT_NAS (client),
+                                    NULL,
+                                    10,
+                                    NULL,
+                                    (GAsyncReadyCallback)get_signal_info_ready,
+                                    task);
+#else
     qmi_client_nas_get_signal_strength (QMI_CLIENT_NAS (client),
                                         NULL,
                                         10,
                                         NULL,
                                         (GAsyncReadyCallback)get_signal_strength_ready,
                                         task);
+#endif /* WITH_NEWEST_QMI_COMMANDS */
 }
 
 /*****************************************************************************/
@@ -2855,7 +2852,7 @@
     else
         qmi_indication_nas_serving_system_output_get_data_service_capability (indication_output, &data_service_capabilities, NULL);
 
-    if (data_service_capabilities)
+    if (data_service_capabilities && data_service_capabilities->len > 0)
         mm_access_technologies =
             mm_modem_access_technologies_from_qmi_data_capability_array (data_service_capabilities);
     else
@@ -2982,11 +2979,24 @@
             g_free (new_operator_id);
     }
 
-    /* Report new registration states */
+    /* Report new registration states. The QMI serving system API reports "CS"
+     * and "PS" registration states, and if the device is in LTE, we'll take the "PS"
+     * one as "EPS". But, if the device is not in LTE, we should also set the "EPS"
+     * state as unknown, so that the "PS" one takes precedence when building
+     * the consolidated registration state (otherwise we may be using some old cached
+     * "EPS" state wrongly). */
     mm_iface_modem_3gpp_update_cs_registration_state (MM_IFACE_MODEM_3GPP (self), mm_cs_registration_state);
     mm_iface_modem_3gpp_update_ps_registration_state (MM_IFACE_MODEM_3GPP (self), mm_ps_registration_state);
-    if (mm_access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_LTE)
-        mm_iface_modem_3gpp_update_eps_registration_state (MM_IFACE_MODEM_3GPP (self), mm_ps_registration_state);
+    if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self)))
+        mm_iface_modem_3gpp_update_eps_registration_state (MM_IFACE_MODEM_3GPP (self),
+                                                           (mm_access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_LTE) ?
+                                                           mm_ps_registration_state : MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN);
+    /* Same thing for "5GS" state */
+    if (mm_iface_modem_is_3gpp_5gnr (MM_IFACE_MODEM (self)))
+        mm_iface_modem_3gpp_update_5gs_registration_state (MM_IFACE_MODEM_3GPP (self),
+                                                           (mm_access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_5GNR) ?
+                                                           mm_ps_registration_state : MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN);
+
 
     /* Get 3GPP location LAC/TAC and CI */
     lac = 0;
@@ -3576,24 +3586,20 @@
     task = g_task_new (self, NULL, callback, user_data);
 
 #if defined WITH_NEWEST_QMI_COMMANDS
-    /* System Info was added in NAS 1.8 */
-    if (qmi_client_check_version (client, 1, 8)) {
-        qmi_client_nas_get_system_info (QMI_CLIENT_NAS (client),
-                                        NULL,
-                                        10,
-                                        NULL,
-                                        (GAsyncReadyCallback)get_system_info_ready,
-                                        task);
-        return;
-    }
-#endif /* WITH_NEWEST_QMI_COMMANDS */
-
+    qmi_client_nas_get_system_info (QMI_CLIENT_NAS (client),
+                                    NULL,
+                                    10,
+                                    NULL,
+                                    (GAsyncReadyCallback)get_system_info_ready,
+                                    task);
+#else
     qmi_client_nas_get_serving_system (QMI_CLIENT_NAS (client),
                                        NULL,
                                        10,
                                        NULL,
                                        (GAsyncReadyCallback)get_serving_system_3gpp_ready,
                                        task);
+#endif /* WITH_NEWEST_QMI_COMMANDS */
 }
 
 /*****************************************************************************/
@@ -3632,9 +3638,9 @@
 }
 
 static gboolean
-modem_3gpp_enable_disable_unsolicited_registration_events_finish (MMIfaceModem3gpp *self,
-                                                                  GAsyncResult *res,
-                                                                  GError **error)
+modem_3gpp_enable_disable_unsolicited_registration_events_finish (MMIfaceModem3gpp  *self,
+                                                                  GAsyncResult      *res,
+                                                                  GError           **error)
 {
     return g_task_propagate_boolean (G_TASK (res), error);
 }
@@ -3642,28 +3648,28 @@
 static void
 ri_serving_system_or_system_info_ready (QmiClientNas *client,
                                         GAsyncResult *res,
-                                        GTask *task)
+                                        GTask        *task)
 {
-    MMBroadbandModemQmi *self;
-    UnsolicitedRegistrationEventsContext *ctx;
-    QmiMessageNasRegisterIndicationsOutput *output = NULL;
-    GError *error = NULL;
+    MMBroadbandModemQmi                               *self;
+    UnsolicitedRegistrationEventsContext              *ctx;
+    g_autoptr(QmiMessageNasRegisterIndicationsOutput)  output = NULL;
+    g_autoptr(GError)                                  error = NULL;
 
     self = g_task_get_source_object (task);
-    ctx = g_task_get_task_data (task);
+    ctx  = g_task_get_task_data     (task);
 
     output = qmi_client_nas_register_indications_finish (client, res, &error);
-    if (!output) {
-        mm_obj_dbg (self, "QMI operation failed: '%s'", error->message);
-        g_error_free (error);
-    } else if (!qmi_message_nas_register_indications_output_get_result (output, &error)) {
+    if (!output || !qmi_message_nas_register_indications_output_get_result (output, &error)) {
         mm_obj_dbg (self, "couldn't register indications: '%s'", error->message);
-        g_error_free (error);
+        if (ctx->enable) {
+#if defined WITH_NEWEST_QMI_COMMANDS
+            mm_obj_dbg (self, "assuming system info indications are always enabled");
+#else
+            mm_obj_dbg (self, "assuming serving system indications are always enabled");
+#endif
+        }
     }
 
-    if (output)
-        qmi_message_nas_register_indications_output_unref (output);
-
     /* Just ignore errors for now */
     self->priv->unsolicited_registration_events_enabled = ctx->enable;
     g_task_return_boolean (task, TRUE);
@@ -3673,8 +3679,8 @@
 static void
 common_enable_disable_unsolicited_registration_events_serving_system (GTask *task)
 {
-    UnsolicitedRegistrationEventsContext *ctx;
-    QmiMessageNasRegisterIndicationsInput *input;
+    UnsolicitedRegistrationEventsContext             *ctx;
+    g_autoptr(QmiMessageNasRegisterIndicationsInput)  input = NULL;
 
     ctx = g_task_get_task_data (task);
     input = qmi_message_nas_register_indications_input_new ();
@@ -3686,15 +3692,14 @@
         NULL,
         (GAsyncReadyCallback)ri_serving_system_or_system_info_ready,
         task);
-    qmi_message_nas_register_indications_input_unref (input);
 }
 
 #if defined WITH_NEWEST_QMI_COMMANDS
 static void
 common_enable_disable_unsolicited_registration_events_system_info (GTask *task)
 {
-    UnsolicitedRegistrationEventsContext *ctx;
-    QmiMessageNasRegisterIndicationsInput *input;
+    UnsolicitedRegistrationEventsContext             *ctx;
+    g_autoptr(QmiMessageNasRegisterIndicationsInput)  input = NULL;
 
     ctx = g_task_get_task_data (task);
     input = qmi_message_nas_register_indications_input_new ();
@@ -3706,20 +3711,18 @@
         NULL,
         (GAsyncReadyCallback)ri_serving_system_or_system_info_ready,
         task);
-    qmi_message_nas_register_indications_input_unref (input);
 }
 #endif /* WITH_NEWEST_QMI_COMMANDS */
 
 static void
-modem_3gpp_disable_unsolicited_registration_events (MMIfaceModem3gpp *_self,
-                                                    gboolean cs_supported,
-                                                    gboolean ps_supported,
-                                                    gboolean eps_supported,
-                                                    GAsyncReadyCallback callback,
-                                                    gpointer user_data)
+modem_3gpp_disable_unsolicited_registration_events (MMIfaceModem3gpp    *self,
+                                                    gboolean             cs_supported,
+                                                    gboolean             ps_supported,
+                                                    gboolean             eps_supported,
+                                                    GAsyncReadyCallback  callback,
+                                                    gpointer             user_data)
 {
-    MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
-    GTask *task;
+    GTask     *task;
     QmiClient *client = NULL;
 
     if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
@@ -3727,46 +3730,28 @@
                                       callback, user_data))
         return;
 
-    task = unsolicited_registration_events_task_new (self,
+    task = unsolicited_registration_events_task_new (MM_BROADBAND_MODEM_QMI (self),
                                                      client,
                                                      FALSE,
                                                      callback,
                                                      user_data);
 
 #if defined WITH_NEWEST_QMI_COMMANDS
-    /* System Info was added in NAS 1.8 */
-    if (qmi_client_check_version (client, 1, 8)) {
-        common_enable_disable_unsolicited_registration_events_system_info (task);
-        return;
-    }
+    common_enable_disable_unsolicited_registration_events_system_info (task);
+#else
+    common_enable_disable_unsolicited_registration_events_serving_system (task);
 #endif /* WITH_NEWEST_QMI_COMMANDS */
-
-    /* Ability to explicitly enable/disable serving system indications was
-     * added in NAS 1.2 */
-    if (qmi_client_check_version (client, 1, 2)) {
-        common_enable_disable_unsolicited_registration_events_serving_system (task);
-        return;
-    }
-
-    /* Devices with NAS < 1.2 will just always issue serving system indications */
-    self->priv->unsolicited_registration_events_enabled = FALSE;
-    g_task_return_new_error (task,
-                             MM_CORE_ERROR,
-                             MM_CORE_ERROR_FAILED,
-                             "Device doesn't allow disabling registration events");
-    g_object_unref (task);
 }
 
 static void
-modem_3gpp_enable_unsolicited_registration_events (MMIfaceModem3gpp *_self,
-                                                   gboolean cs_supported,
-                                                   gboolean ps_supported,
-                                                   gboolean eps_supported,
-                                                   GAsyncReadyCallback callback,
-                                                   gpointer user_data)
+modem_3gpp_enable_unsolicited_registration_events (MMIfaceModem3gpp    *self,
+                                                   gboolean             cs_supported,
+                                                   gboolean             ps_supported,
+                                                   gboolean             eps_supported,
+                                                   GAsyncReadyCallback  callback,
+                                                   gpointer             user_data)
 {
-    MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
-    GTask *task;
+    GTask     *task;
     QmiClient *client = NULL;
 
     if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
@@ -3774,24 +3759,17 @@
                                       callback, user_data))
         return;
 
-    task = unsolicited_registration_events_task_new (self,
+    task = unsolicited_registration_events_task_new (MM_BROADBAND_MODEM_QMI (self),
                                                      client,
                                                      TRUE,
                                                      callback,
                                                      user_data);
 
-    /* Ability to explicitly enable/disable serving system indications was
-     * added in NAS 1.2 */
-    if (qmi_client_check_version (client, 1, 2)) {
-        common_enable_disable_unsolicited_registration_events_serving_system (task);
-        return;
-    }
-
-    /* Devices with NAS < 1.2 will just always issue serving system indications */
-    mm_obj_dbg (self, "assuming serving system indications are always enabled");
-    self->priv->unsolicited_registration_events_enabled = TRUE;
-    g_task_return_boolean (task, TRUE);
-    g_object_unref (task);
+#if defined WITH_NEWEST_QMI_COMMANDS
+    common_enable_disable_unsolicited_registration_events_system_info (task);
+#else
+    common_enable_disable_unsolicited_registration_events_serving_system (task);
+#endif /* WITH_NEWEST_QMI_COMMANDS */
 }
 
 /*****************************************************************************/
@@ -4797,38 +4775,34 @@
     self->priv->unsolicited_registration_events_setup = enable;
 
 #if defined WITH_NEWEST_QMI_COMMANDS
-    /* Signal info introduced in NAS 1.8 */
-    if (qmi_client_check_version (client, 1, 8)) {
-        /* Connect/Disconnect "System Info" indications */
-        if (enable) {
-            g_assert (self->priv->system_info_indication_id == 0);
-            self->priv->system_info_indication_id =
-                g_signal_connect (client,
-                                  "system-info",
-                                  G_CALLBACK (system_info_indication_cb),
-                                  self);
-        } else {
-            g_assert (self->priv->system_info_indication_id != 0);
-            g_signal_handler_disconnect (client, self->priv->system_info_indication_id);
-            self->priv->system_info_indication_id = 0;
-        }
-    } else
-#endif /* WITH_NEWEST_QMI_COMMANDS */
-    {
-        /* Connect/Disconnect "Serving System" indications */
-        if (enable) {
-            g_assert (self->priv->serving_system_indication_id == 0);
-            self->priv->serving_system_indication_id =
-                g_signal_connect (client,
-                                  "serving-system",
-                                  G_CALLBACK (serving_system_indication_cb),
-                                  self);
-        } else {
-            g_assert (self->priv->serving_system_indication_id != 0);
-            g_signal_handler_disconnect (client, self->priv->serving_system_indication_id);
-            self->priv->serving_system_indication_id = 0;
-        }
+    /* Connect/Disconnect "System Info" indications */
+    if (enable) {
+        g_assert (self->priv->system_info_indication_id == 0);
+        self->priv->system_info_indication_id =
+            g_signal_connect (client,
+                              "system-info",
+                              G_CALLBACK (system_info_indication_cb),
+                              self);
+    } else {
+        g_assert (self->priv->system_info_indication_id != 0);
+        g_signal_handler_disconnect (client, self->priv->system_info_indication_id);
+        self->priv->system_info_indication_id = 0;
     }
+#else
+    /* Connect/Disconnect "Serving System" indications */
+    if (enable) {
+        g_assert (self->priv->serving_system_indication_id == 0);
+        self->priv->serving_system_indication_id =
+            g_signal_connect (client,
+                              "serving-system",
+                              G_CALLBACK (serving_system_indication_cb),
+                              self);
+    } else {
+        g_assert (self->priv->serving_system_indication_id != 0);
+        g_signal_handler_disconnect (client, self->priv->serving_system_indication_id);
+        self->priv->serving_system_indication_id = 0;
+    }
+#endif /* WITH_NEWEST_QMI_COMMANDS */
 
     g_task_return_boolean (task, TRUE);
     g_object_unref (task);
@@ -4930,19 +4904,11 @@
 }
 
 /*****************************************************************************/
-/* Enabling/disabling unsolicited events (3GPP and CDMA interface)
- *
- * If NAS >= 1.8:
- *   - Config Signal Info (only when enabling)
- *   - Register Indications with Signal Info
- *
- * If NAS < 1.8:
- *   - Set Event Report with Signal Strength
- */
+/* Enabling/disabling unsolicited events (3GPP and CDMA interface) */
 
 typedef struct {
     QmiClientNas *client;
-    gboolean enable;
+    gboolean      enable;
 } EnableUnsolicitedEventsContext;
 
 static void
@@ -4953,9 +4919,9 @@
 }
 
 static gboolean
-common_enable_disable_unsolicited_events_finish (MMBroadbandModemQmi *self,
-                                                 GAsyncResult *res,
-                                                 GError **error)
+common_enable_disable_unsolicited_events_finish (MMBroadbandModemQmi  *self,
+                                                 GAsyncResult         *res,
+                                                 GError              **error)
 {
     return g_task_propagate_boolean (G_TASK (res), error);
 }
@@ -4963,27 +4929,19 @@
 static void
 ser_signal_strength_ready (QmiClientNas *client,
                            GAsyncResult *res,
-                           GTask *task)
+                           GTask        *task)
 {
-    MMBroadbandModemQmi *self;
-    EnableUnsolicitedEventsContext *ctx;
-    QmiMessageNasSetEventReportOutput *output = NULL;
-    GError *error = NULL;
+    MMBroadbandModemQmi                          *self;
+    EnableUnsolicitedEventsContext               *ctx;
+    g_autoptr(QmiMessageNasSetEventReportOutput)  output = NULL;
+    g_autoptr(GError)                             error = NULL;
 
     self = g_task_get_source_object (task);
-    ctx = g_task_get_task_data (task);
+    ctx  = g_task_get_task_data     (task);
 
     output = qmi_client_nas_set_event_report_finish (client, res, &error);
-    if (!output) {
-        mm_obj_dbg (self, "QMI operation failed: '%s'", error->message);
-        g_error_free (error);
-    } else if (!qmi_message_nas_set_event_report_output_get_result (output, &error)) {
+    if (!output || !qmi_message_nas_set_event_report_output_get_result (output, &error))
         mm_obj_dbg (self, "couldn't set event report: '%s'", error->message);
-        g_error_free (error);
-    }
-
-    if (output)
-        qmi_message_nas_set_event_report_output_unref (output);
 
     /* Just ignore errors for now */
     self->priv->unsolicited_events_enabled = ctx->enable;
@@ -4994,30 +4952,28 @@
 static void
 common_enable_disable_unsolicited_events_signal_strength (GTask *task)
 {
-    EnableUnsolicitedEventsContext *ctx;
+    EnableUnsolicitedEventsContext              *ctx;
+    g_autoptr(QmiMessageNasSetEventReportInput)  input = NULL;
+    g_autoptr(GArray)                            thresholds = NULL;
 
     /* The device doesn't really like to have many threshold values, so don't
      * grow this array without checking first */
     static const gint8 thresholds_data[] = { -80, -40, 0, 40, 80 };
-    QmiMessageNasSetEventReportInput *input;
-    GArray *thresholds;
 
     ctx = g_task_get_task_data (task);
-    input = qmi_message_nas_set_event_report_input_new ();
 
-    /* Prepare thresholds, separated 20 each */
+    /* Only set thresholds during enable, but always create the array anyway,
+     * as the TLV setter expects it */
     thresholds = g_array_sized_new (FALSE, FALSE, sizeof (gint8), G_N_ELEMENTS (thresholds_data));
-
-    /* Only set thresholds during enable */
     if (ctx->enable)
         g_array_append_vals (thresholds, thresholds_data, G_N_ELEMENTS (thresholds_data));
 
+    input = qmi_message_nas_set_event_report_input_new ();
     qmi_message_nas_set_event_report_input_set_signal_strength_indicator (
         input,
         ctx->enable,
         thresholds,
         NULL);
-    g_array_unref (thresholds);
     qmi_client_nas_set_event_report (
         ctx->client,
         input,
@@ -5025,7 +4981,6 @@
         NULL,
         (GAsyncReadyCallback)ser_signal_strength_ready,
         task);
-    qmi_message_nas_set_event_report_input_unref (input);
 }
 
 #if defined WITH_NEWEST_QMI_COMMANDS
@@ -5033,27 +4988,19 @@
 static void
 ri_signal_info_ready (QmiClientNas *client,
                       GAsyncResult *res,
-                      GTask *task)
+                      GTask        *task)
 {
-    MMBroadbandModemQmi *self;
-    EnableUnsolicitedEventsContext *ctx;
-    QmiMessageNasRegisterIndicationsOutput *output = NULL;
-    GError *error = NULL;
+    MMBroadbandModemQmi                               *self;
+    EnableUnsolicitedEventsContext                    *ctx;
+    g_autoptr(QmiMessageNasRegisterIndicationsOutput)  output = NULL;
+    g_autoptr(GError)                                  error = NULL;
 
     self = g_task_get_source_object (task);
-    ctx = g_task_get_task_data (task);
+    ctx  = g_task_get_task_data     (task);
 
     output = qmi_client_nas_register_indications_finish (client, res, &error);
-    if (!output) {
-        mm_obj_dbg (self, "QMI operation failed: '%s'", error->message);
-        g_error_free (error);
-    } else if (!qmi_message_nas_register_indications_output_get_result (output, &error)) {
+    if (!output || !qmi_message_nas_register_indications_output_get_result (output, &error))
         mm_obj_dbg (self, "couldn't register indications: '%s'", error->message);
-        g_error_free (error);
-    }
-
-    if (output)
-        qmi_message_nas_register_indications_output_unref (output);
 
     /* Just ignore errors for now */
     self->priv->unsolicited_events_enabled = ctx->enable;
@@ -5064,8 +5011,8 @@
 static void
 common_enable_disable_unsolicited_events_signal_info (GTask *task)
 {
-    EnableUnsolicitedEventsContext *ctx;
-    QmiMessageNasRegisterIndicationsInput *input;
+    EnableUnsolicitedEventsContext                   *ctx;
+    g_autoptr(QmiMessageNasRegisterIndicationsInput)  input = NULL;
 
     ctx = g_task_get_task_data (task);
     input = qmi_message_nas_register_indications_input_new ();
@@ -5077,28 +5024,19 @@
         NULL,
         (GAsyncReadyCallback)ri_signal_info_ready,
         task);
-    qmi_message_nas_register_indications_input_unref (input);
 }
 
 static void
 config_signal_info_ready (QmiClientNas *client,
                           GAsyncResult *res,
-                          GTask *task)
+                          GTask        *task)
 {
-    QmiMessageNasConfigSignalInfoOutput *output = NULL;
-    GError *error = NULL;
+    g_autoptr(QmiMessageNasConfigSignalInfoOutput) output = NULL;
+    g_autoptr(GError)                              error = NULL;
 
     output = qmi_client_nas_config_signal_info_finish (client, res, &error);
-    if (!output) {
-        mm_obj_dbg (self, "QMI operation failed: '%s'", error->message);
-        g_error_free (error);
-    } else if (!qmi_message_nas_config_signal_info_output_get_result (output, &error)) {
+    if (!output || !qmi_message_nas_config_signal_info_output_get_result (output, &error))
         mm_obj_dbg (self, "couldn't config signal info: '%s'", error->message);
-        g_error_free (error);
-    }
-
-    if (output)
-        qmi_message_nas_config_signal_info_output_unref (output);
 
     /* Keep on */
     common_enable_disable_unsolicited_events_signal_info (task);
@@ -5107,12 +5045,8 @@
 static void
 common_enable_disable_unsolicited_events_signal_info_config (GTask *task)
 {
-    EnableUnsolicitedEventsContext *ctx;
-    /* RSSI values go between -105 and -60 for 3GPP technologies,
-     * and from -105 to -90 in 3GPP2 technologies (approx). */
-    static const gint8 thresholds_data[] = { -100, -97, -95, -92, -90, -85, -80, -75, -70, -65 };
-    QmiMessageNasConfigSignalInfoInput *input;
-    GArray *thresholds;
+    EnableUnsolicitedEventsContext                *ctx;
+    g_autoptr(QmiMessageNasConfigSignalInfoInput)  input = NULL;
 
     ctx = g_task_get_task_data (task);
 
@@ -5125,14 +5059,20 @@
     input = qmi_message_nas_config_signal_info_input_new ();
 
     /* Prepare thresholds, separated 20 each */
-    thresholds = g_array_sized_new (FALSE, FALSE, sizeof (gint8), G_N_ELEMENTS (thresholds_data));
-    g_array_append_vals (thresholds, thresholds_data, G_N_ELEMENTS (thresholds_data));
+    {
+        /* RSSI values go between -105 and -60 for 3GPP technologies,
+         * and from -105 to -90 in 3GPP2 technologies (approx). */
+        static const gint8 thresholds_data[] = { -100, -97, -95, -92, -90, -85, -80, -75, -70, -65 };
+        g_autoptr(GArray)  thresholds = NULL;
 
-    qmi_message_nas_config_signal_info_input_set_rssi_threshold (
-        input,
-        thresholds,
-        NULL);
-    g_array_unref (thresholds);
+        thresholds = g_array_sized_new (FALSE, FALSE, sizeof (gint8), G_N_ELEMENTS (thresholds_data));
+        g_array_append_vals (thresholds, thresholds_data, G_N_ELEMENTS (thresholds_data));
+        qmi_message_nas_config_signal_info_input_set_rssi_threshold (
+            input,
+            thresholds,
+            NULL);
+    }
+
     qmi_client_nas_config_signal_info (
         ctx->client,
         input,
@@ -5140,20 +5080,19 @@
         NULL,
         (GAsyncReadyCallback)config_signal_info_ready,
         task);
-    qmi_message_nas_config_signal_info_input_unref (input);
 }
 
 #endif /* WITH_NEWEST_QMI_COMMANDS */
 
 static void
 common_enable_disable_unsolicited_events (MMBroadbandModemQmi *self,
-                                          gboolean enable,
-                                          GAsyncReadyCallback callback,
-                                          gpointer user_data)
+                                          gboolean             enable,
+                                          GAsyncReadyCallback  callback,
+                                          gpointer             user_data)
 {
     EnableUnsolicitedEventsContext *ctx;
-    GTask *task;
-    QmiClient *client = NULL;
+    GTask                          *task;
+    QmiClient                      *client = NULL;
 
     if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
                                       QMI_SERVICE_NAS, &client,
@@ -5176,14 +5115,10 @@
     g_task_set_task_data (task, ctx, (GDestroyNotify)enable_unsolicited_events_context_free);
 
 #if defined WITH_NEWEST_QMI_COMMANDS
-    /* Signal info introduced in NAS 1.8 */
-    if (qmi_client_check_version (client, 1, 8)) {
-        common_enable_disable_unsolicited_events_signal_info_config (task);
-        return;
-    }
-#endif /* WITH_NEWEST_QMI_COMMANDS */
-
+    common_enable_disable_unsolicited_events_signal_info_config (task);
+#else
     common_enable_disable_unsolicited_events_signal_strength (task);
+#endif /* WITH_NEWEST_QMI_COMMANDS */
 }
 
 /*****************************************************************************/
@@ -5380,21 +5315,17 @@
     }
 
 #if defined WITH_NEWEST_QMI_COMMANDS
-    /* Connect/Disconnect "Signal Info" indications.
-     * Signal info introduced in NAS 1.8 */
-    if (qmi_client_check_version (client, 1, 8)) {
-        if (enable) {
-            g_assert (self->priv->signal_info_indication_id == 0);
-            self->priv->signal_info_indication_id =
-                g_signal_connect (client,
-                                  "signal-info",
-                                  G_CALLBACK (signal_info_indication_cb),
-                                  self);
-        } else {
-            g_assert (self->priv->signal_info_indication_id != 0);
-            g_signal_handler_disconnect (client, self->priv->signal_info_indication_id);
-            self->priv->signal_info_indication_id = 0;
-        }
+    if (enable) {
+        g_assert (self->priv->signal_info_indication_id == 0);
+        self->priv->signal_info_indication_id =
+            g_signal_connect (client,
+                              "signal-info",
+                              G_CALLBACK (signal_info_indication_cb),
+                              self);
+    } else {
+        g_assert (self->priv->signal_info_indication_id != 0);
+        g_signal_handler_disconnect (client, self->priv->signal_info_indication_id);
+        self->priv->signal_info_indication_id = 0;
     }
 #endif /* WITH_NEWEST_QMI_COMMANDS */
 
@@ -7531,6 +7462,30 @@
     process_ussd_message (self, user_action, utf8, error);
 }
 
+static void
+ussd_release_indication_cb (QmiClientVoice      *client,
+                            MMBroadbandModemQmi *self)
+{
+    GTask  *pending_task;
+    GError *error;
+
+    mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self),
+                                           MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE);
+
+    error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "USSD terminated by network");
+
+    pending_task = g_steal_pointer (&self->priv->pending_ussd_action);
+    if (pending_task) {
+        g_task_return_error (pending_task, error);
+        g_object_unref (pending_task);
+        return;
+    }
+
+    /* If no pending task, just report the error */
+    mm_obj_dbg (self, "USSD release indication: %s", error->message);
+    g_error_free (error);
+}
+
 /*****************************************************************************/
 /* Setup/cleanup unsolicited events */
 
@@ -7574,10 +7529,19 @@
                               "ussd",
                               G_CALLBACK (ussd_indication_cb),
                               self);
+        g_assert (self->priv->ussd_release_indication_id == 0);
+        self->priv->ussd_release_indication_id =
+            g_signal_connect (client,
+                              "release-ussd",
+                              G_CALLBACK (ussd_release_indication_cb),
+                              self);
     } else {
         g_assert (self->priv->ussd_indication_id != 0);
         g_signal_handler_disconnect (client, self->priv->ussd_indication_id);
         self->priv->ussd_indication_id = 0;
+        g_assert (self->priv->ussd_release_indication_id != 0);
+        g_signal_handler_disconnect (client, self->priv->ussd_release_indication_id);
+        self->priv->ussd_release_indication_id = 0;
     }
 
     g_task_return_boolean (task, TRUE);
@@ -8646,6 +8610,8 @@
 
     input = qmi_message_dms_set_firmware_preference_input_new ();
     qmi_message_dms_set_firmware_preference_input_set_list (input, array, NULL);
+    g_array_unref (array);
+
     qmi_client_dms_set_firmware_preference (
         QMI_CLIENT_DMS (client),
         input,
@@ -8717,32 +8683,25 @@
 } SignalLoadValuesResult;
 
 typedef struct {
-    QmiClientNas *client;
-    SignalLoadValuesStep step;
+    QmiClientNas           *client;
+    SignalLoadValuesStep    step;
     SignalLoadValuesResult *values_result;
 } SignalLoadValuesContext;
 
 static void
 signal_load_values_result_free (SignalLoadValuesResult *result)
 {
-    if (result->cdma)
-        g_object_unref (result->cdma);
-    if (result->evdo)
-        g_object_unref (result->evdo);
-    if (result->gsm)
-        g_object_unref (result->gsm);
-    if (result->umts)
-        g_object_unref (result->umts);
-    if (result->lte)
-        g_object_unref (result->lte);
+    g_clear_object (&result->cdma);
+    g_clear_object (&result->evdo);
+    g_clear_object (&result->gsm);
+    g_clear_object (&result->lte);
     g_slice_free (SignalLoadValuesResult, result);
 }
 
 static void
 signal_load_values_context_free (SignalLoadValuesContext *ctx)
 {
-    if (ctx->values_result)
-        signal_load_values_result_free (ctx->values_result);
+    g_clear_pointer (&ctx->values_result, (GDestroyNotify)signal_load_values_result_free);
     g_slice_free (SignalLoadValuesContext, ctx);
 }
 
@@ -8767,14 +8726,14 @@
 }
 
 static gboolean
-signal_load_values_finish (MMIfaceModemSignal *self,
-                           GAsyncResult *res,
-                           MMSignal **cdma,
-                           MMSignal **evdo,
-                           MMSignal **gsm,
-                           MMSignal **umts,
-                           MMSignal **lte,
-                           GError **error)
+signal_load_values_finish (MMIfaceModemSignal  *self,
+                           GAsyncResult        *res,
+                           MMSignal           **cdma,
+                           MMSignal           **evdo,
+                           MMSignal           **gsm,
+                           MMSignal           **umts,
+                           MMSignal           **lte,
+                           GError             **error)
 {
     SignalLoadValuesResult *values_result;
 
@@ -8796,28 +8755,26 @@
 static void
 signal_load_values_get_signal_strength_ready (QmiClientNas *client,
                                               GAsyncResult *res,
-                                              GTask *task)
+                                              GTask        *task)
 {
-    MMBroadbandModemQmi *self;
-    SignalLoadValuesContext *ctx;
-    QmiMessageNasGetSignalStrengthOutput *output;
-    GArray *array;
-    gint32 aux_int32;
-    gint16 aux_int16;
-    gint8 aux_int8;
-    QmiNasRadioInterface radio_interface;
-    QmiNasEvdoSinrLevel sinr;
+    MMBroadbandModemQmi                             *self;
+    SignalLoadValuesContext                         *ctx;
+    GArray                                          *array;
+    gint32                                           aux_int32;
+    gint16                                           aux_int16;
+    gint8                                            aux_int8;
+    QmiNasRadioInterface                             radio_interface;
+    QmiNasEvdoSinrLevel                              sinr;
+    g_autoptr(QmiMessageNasGetSignalStrengthOutput)  output = NULL;
 
     self = g_task_get_source_object (task);
-    ctx = g_task_get_task_data (task);
+    ctx  = g_task_get_task_data     (task);
 
     output = qmi_client_nas_get_signal_strength_finish (client, res, NULL);
     if (!output || !qmi_message_nas_get_signal_strength_output_get_result (output, NULL)) {
         /* No hard errors, go on to next step */
         ctx->step++;
         signal_load_values_context_step (task);
-        if (output)
-            qmi_message_nas_get_signal_strength_output_unref (output);
         return;
     }
 
@@ -8943,8 +8900,6 @@
             mm_signal_set_sinr (ctx->values_result->evdo, get_db_from_sinr_level (self, sinr));
     }
 
-    qmi_message_nas_get_signal_strength_output_unref (output);
-
     /* Go on */
     ctx->step++;
     signal_load_values_context_step (task);
@@ -8953,29 +8908,27 @@
 static void
 signal_load_values_get_signal_info_ready (QmiClientNas *client,
                                           GAsyncResult *res,
-                                          GTask *task)
+                                          GTask        *task)
 {
-    MMBroadbandModemQmi *self;
-    SignalLoadValuesContext *ctx;
-    QmiMessageNasGetSignalInfoOutput *output;
-    gint8 rssi;
-    gint16 ecio;
-    QmiNasEvdoSinrLevel sinr_level;
-    gint32 io;
-    gint8 rsrq;
-    gint16 rsrp;
-    gint16 snr;
+    MMBroadbandModemQmi                         *self;
+    SignalLoadValuesContext                     *ctx;
+    gint8                                        rssi;
+    gint16                                       ecio;
+    QmiNasEvdoSinrLevel                          sinr_level;
+    gint32                                       io;
+    gint8                                        rsrq;
+    gint16                                       rsrp;
+    gint16                                       snr;
+    g_autoptr(QmiMessageNasGetSignalInfoOutput)  output = NULL;
 
     self = g_task_get_source_object (task);
-    ctx  = g_task_get_task_data (task);
+    ctx  = g_task_get_task_data     (task);
 
     output = qmi_client_nas_get_signal_info_finish (client, res, NULL);
     if (!output || !qmi_message_nas_get_signal_info_output_get_result (output, NULL)) {
         /* No hard errors, go on to next step */
         ctx->step++;
         signal_load_values_context_step (task);
-        if (output)
-            qmi_message_nas_get_signal_info_output_unref (output);
         return;
     }
 
@@ -9038,8 +8991,6 @@
         mm_signal_set_snr (ctx->values_result->lte, (0.1) * ((gdouble)snr));
     }
 
-    qmi_message_nas_get_signal_info_output_unref (output);
-
     /* Keep on */
     ctx->step++;
     signal_load_values_context_step (task);
@@ -9066,22 +9017,18 @@
         /* Fall through */
 
     case SIGNAL_LOAD_VALUES_STEP_SIGNAL_INFO:
-        if (qmi_client_check_version (QMI_CLIENT (ctx->client), 1, 8)) {
-            qmi_client_nas_get_signal_info (ctx->client,
-                                            NULL,
-                                            5,
-                                            NULL,
-                                            (GAsyncReadyCallback)signal_load_values_get_signal_info_ready,
-                                            task);
-            return;
-        }
-        ctx->step++;
-        /* Fall through */
+        qmi_client_nas_get_signal_info (ctx->client,
+                                        NULL,
+                                        5,
+                                        NULL,
+                                        (GAsyncReadyCallback)signal_load_values_get_signal_info_ready,
+                                        task);
+        return;
 
     case SIGNAL_LOAD_VALUES_STEP_SIGNAL_STRENGTH:
         /* If already loaded with signal info, don't try signal strength */
         if (!VALUES_RESULT_LOADED (ctx)) {
-            QmiMessageNasGetSignalStrengthInput *input;
+            g_autoptr(QmiMessageNasGetSignalStrengthInput) input = NULL;
 
             input = qmi_message_nas_get_signal_strength_input_new ();
             qmi_message_nas_get_signal_strength_input_set_request_mask (
@@ -9100,7 +9047,6 @@
                                                 NULL,
                                                 (GAsyncReadyCallback)signal_load_values_get_signal_strength_ready,
                                                 task);
-            qmi_message_nas_get_signal_strength_input_unref (input);
             return;
         }
         ctx->step++;
@@ -9108,22 +9054,15 @@
 
     case SIGNAL_LOAD_VALUES_STEP_SIGNAL_LAST:
         /* If any result is set, succeed */
-        if (VALUES_RESULT_LOADED (ctx)) {
-            SignalLoadValuesResult *values_result;
-
-            /* Steal results from context in order to return them */
-            values_result = ctx->values_result;
-            ctx->values_result = NULL;
-
+        if (VALUES_RESULT_LOADED (ctx))
             g_task_return_pointer (task,
-                                   values_result,
+                                   g_steal_pointer (&ctx->values_result),
                                    (GDestroyNotify)signal_load_values_result_free);
-        } else {
+        else
             g_task_return_new_error (task,
                                      MM_CORE_ERROR,
                                      MM_CORE_ERROR_FAILED,
                                      "No way to load extended signal information");
-        }
         g_object_unref (task);
         return;
 
@@ -9137,14 +9076,14 @@
 }
 
 static void
-signal_load_values (MMIfaceModemSignal *self,
-                    GCancellable *cancellable,
-                    GAsyncReadyCallback callback,
-                    gpointer user_data)
+signal_load_values (MMIfaceModemSignal  *self,
+                    GCancellable        *cancellable,
+                    GAsyncReadyCallback  callback,
+                    gpointer             user_data)
 {
     SignalLoadValuesContext *ctx;
-    GTask *task;
-    QmiClient *client = NULL;
+    GTask                   *task;
+    QmiClient               *client = NULL;
 
     mm_obj_dbg (self, "loading extended signal information...");
 
@@ -9158,9 +9097,7 @@
     ctx->step = SIGNAL_LOAD_VALUES_STEP_SIGNAL_FIRST;
 
     task = g_task_new (self, cancellable, callback, user_data);
-    g_task_set_task_data (task,
-                          ctx,
-                          (GDestroyNotify)signal_load_values_context_free);
+    g_task_set_task_data (task, ctx, (GDestroyNotify)signal_load_values_context_free);
 
     signal_load_values_context_step (task);
 }
diff --git a/src/mm-charsets.c b/src/mm-charsets.c
index bf0de2b..e48cec3 100644
--- a/src/mm-charsets.c
+++ b/src/mm-charsets.c
@@ -458,7 +458,8 @@
             g_byte_array_append (utf8, (guint8 *) "?", 1);
     }
 
-    g_byte_array_append (utf8, (guint8 *) "\0", 1);  /* NULL terminator */
+    /* Always make sure returned string is NUL terminated */
+    g_byte_array_append (utf8, (guint8 *) "\0", 1);
     return g_byte_array_free (utf8, FALSE);
 }
 
@@ -471,7 +472,6 @@
     int i = 0;
 
     g_return_val_if_fail (utf8 != NULL, NULL);
-    g_return_val_if_fail (out_len != NULL, NULL);
     g_return_val_if_fail (g_utf8_validate (utf8, -1, NULL), NULL);
 
     /* worst case initial length */
@@ -480,7 +480,8 @@
     if (*utf8 == 0x00) {
         /* Zero-length string */
         g_byte_array_append (gsm, (guint8 *) "\0", 1);
-        *out_len = 0;
+        if (out_len)
+            *out_len = 0;
         return g_byte_array_free (gsm, FALSE);
     }
 
@@ -501,7 +502,12 @@
         i++;
     }
 
-    *out_len = gsm->len;
+    /* Output length doesn't consider terminating NUL byte */
+    if (out_len)
+        *out_len = gsm->len;
+
+    /* Always make sure returned string is NUL terminated */
+    g_byte_array_append (gsm, (guint8 *) "\0", 1);
     return g_byte_array_free (gsm, FALSE);
 }
 
@@ -757,6 +763,10 @@
         break;
 
     case MM_MODEM_CHARSET_GSM:
+        utf8 = (gchar *) mm_charset_gsm_unpacked_to_utf8 ((const guint8 *) str, strlen (str));
+        g_free (str);
+        break;
+
     case MM_MODEM_CHARSET_8859_1:
     case MM_MODEM_CHARSET_PCCP437:
     case MM_MODEM_CHARSET_PCDN: {
@@ -877,12 +887,14 @@
         break;
 
     case MM_MODEM_CHARSET_HEX:
-        /* FIXME: What encoding is this? */
-        g_warn_if_reached ();
         encoded = str;
         break;
 
     case MM_MODEM_CHARSET_GSM:
+        encoded = (gchar *) mm_charset_utf8_to_unpacked_gsm (str, NULL);
+        g_free (str);
+        break;
+
     case MM_MODEM_CHARSET_8859_1:
     case MM_MODEM_CHARSET_PCCP437:
     case MM_MODEM_CHARSET_PCDN: {
diff --git a/src/mm-context.c b/src/mm-context.c
index 406c744..8f20d2e 100644
--- a/src/mm-context.c
+++ b/src/mm-context.c
@@ -38,7 +38,7 @@
 static gboolean      help_flag;
 static gboolean      version_flag;
 static gboolean      debug;
-static MMFilterRule  filter_policy = MM_FILTER_POLICY_DEFAULT;
+static MMFilterRule  filter_policy = MM_FILTER_POLICY_STRICT;
 static gboolean      no_auto_scan = NO_AUTO_SCAN_DEFAULT;
 static const gchar  *initial_kernel_events;
 
@@ -48,8 +48,8 @@
                           gpointer      data,
                           GError      **error)
 {
-    if (!g_ascii_strcasecmp (value, "default")) {
-        filter_policy = MM_FILTER_POLICY_DEFAULT;
+    if (!g_ascii_strcasecmp (value, "legacy")) {
+        filter_policy = MM_FILTER_POLICY_LEGACY;
         return TRUE;
     }
 
@@ -77,7 +77,7 @@
 static const GOptionEntry entries[] = {
     {
         "filter-policy", 0, 0, G_OPTION_ARG_CALLBACK, filter_policy_option_arg,
-        "Filter policy: one of DEFAULT, WHITELIST-ONLY, STRICT, PARANOID",
+        "Filter policy: one of LEGACY, WHITELIST-ONLY, STRICT, PARANOID",
         "[POLICY]"
     },
     {
diff --git a/src/mm-filter.c b/src/mm-filter.c
index cfa620b..18ea08f 100644
--- a/src/mm-filter.c
+++ b/src/mm-filter.c
@@ -39,6 +39,7 @@
 struct _MMFilterPrivate {
     MMFilterRule  enabled_rules;
     GList        *plugin_whitelist_tags;
+    GArray       *plugin_whitelist_vendor_ids;
     GArray       *plugin_whitelist_product_ids;
 };
 
@@ -55,6 +56,27 @@
 }
 
 void
+mm_filter_register_plugin_whitelist_vendor_id (MMFilter *self,
+                                               guint16   vid)
+{
+    guint i;
+
+    if (!self->priv->plugin_whitelist_vendor_ids)
+        self->priv->plugin_whitelist_vendor_ids = g_array_sized_new (FALSE, FALSE, sizeof (guint16), 64);
+
+    for (i = 0; i < self->priv->plugin_whitelist_vendor_ids->len; i++) {
+        guint16 item;
+
+        item = g_array_index (self->priv->plugin_whitelist_vendor_ids, guint16, i);
+        if (item == vid)
+            return;
+    }
+
+    g_array_append_val (self->priv->plugin_whitelist_vendor_ids, vid);
+    mm_obj_dbg (self, "registered plugin whitelist vendor id: %04x", vid);
+}
+
+void
 mm_filter_register_plugin_whitelist_product_id (MMFilter *self,
                                                 guint16   vid,
                                                 guint16   pid)
@@ -140,6 +162,20 @@
                 }
             }
         }
+
+        if (vid && self->priv->plugin_whitelist_vendor_ids) {
+            guint i;
+
+            for (i = 0; i < self->priv->plugin_whitelist_vendor_ids->len; i++) {
+                guint16 item;
+
+                item = g_array_index (self->priv->plugin_whitelist_vendor_ids, guint16, i);
+                if (item == vid) {
+                    mm_obj_dbg (self, "(%s/%s) port allowed: device is whitelisted by plugin (vid)", subsystem, name);
+                    return TRUE;
+                }
+            }
+        }
     }
 
     /* If this is a virtual device, don't allow it */
@@ -483,6 +519,7 @@
 {
     MMFilter *self = MM_FILTER (object);
 
+    g_clear_pointer (&self->priv->plugin_whitelist_vendor_ids, g_array_unref);
     g_clear_pointer (&self->priv->plugin_whitelist_product_ids, g_array_unref);
     g_list_free_full (self->priv->plugin_whitelist_tags, g_free);
 
diff --git a/src/mm-filter.h b/src/mm-filter.h
index 0fc4ade..c357997 100644
--- a/src/mm-filter.h
+++ b/src/mm-filter.h
@@ -80,9 +80,9 @@
      MM_FILTER_RULE_TTY_WITH_NET          | \
      MM_FILTER_RULE_TTY_DEFAULT_FORBIDDEN)
 
-/* This is the default ModemManager policy that tries to automatically probe
+/* This is the legacy ModemManager policy that tries to automatically probe
  * device ports unless they're blacklisted in some way or another. */
-#define MM_FILTER_POLICY_DEFAULT           \
+#define MM_FILTER_POLICY_LEGACY            \
     (MM_FILTER_RULE_EXPLICIT_WHITELIST   | \
      MM_FILTER_RULE_EXPLICIT_BLACKLIST   | \
      MM_FILTER_RULE_VIRTUAL              | \
@@ -145,6 +145,8 @@
 
 void     mm_filter_register_plugin_whitelist_tag        (MMFilter    *self,
                                                          const gchar *tag);
+void     mm_filter_register_plugin_whitelist_vendor_id  (MMFilter    *self,
+                                                         guint16      vid);
 void     mm_filter_register_plugin_whitelist_product_id (MMFilter    *self,
                                                          guint16      vid,
                                                          guint16      pid);
diff --git a/src/mm-iface-modem-firmware.c b/src/mm-iface-modem-firmware.c
index 9268c4b..5aedc4a 100644
--- a/src/mm-iface-modem-firmware.c
+++ b/src/mm-iface-modem-firmware.c
@@ -76,10 +76,13 @@
 
     /* Build array of dicts */
     g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
-    for (l = ctx->list; l; l = g_list_next (l))
-        g_variant_builder_add_value (
-            &builder,
-            mm_firmware_properties_get_dictionary (MM_FIRMWARE_PROPERTIES (l->data)));
+    for (l = ctx->list; l; l = g_list_next (l)) {
+        GVariant *dict;
+
+        dict = mm_firmware_properties_get_dictionary (MM_FIRMWARE_PROPERTIES (l->data));
+        g_variant_builder_add_value (&builder, dict);
+        g_variant_unref (dict);
+    }
 
     mm_gdbus_modem_firmware_complete_list (
         ctx->skeleton,
diff --git a/src/mm-iface-modem-location.c b/src/mm-iface-modem-location.c
index f40f48a..ce307cc 100644
--- a/src/mm-iface-modem-location.c
+++ b/src/mm-iface-modem-location.c
@@ -269,7 +269,7 @@
         g_assert (ctx->location_gps_nmea != NULL);
         if (mm_location_gps_nmea_add_trace (ctx->location_gps_nmea, nmea_trace) &&
             (ctx->location_gps_nmea_last_time == 0 ||
-             time (NULL) - ctx->location_gps_nmea_last_time >= mm_gdbus_modem_location_get_gps_refresh_rate (skeleton))) {
+             time (NULL) - ctx->location_gps_nmea_last_time >= (glong)mm_gdbus_modem_location_get_gps_refresh_rate (skeleton))) {
             ctx->location_gps_nmea_last_time = time (NULL);
             update_nmea = TRUE;
         }
@@ -279,7 +279,7 @@
         g_assert (ctx->location_gps_raw != NULL);
         if (mm_location_gps_raw_add_trace (ctx->location_gps_raw, nmea_trace) &&
             (ctx->location_gps_raw_last_time == 0 ||
-             time (NULL) - ctx->location_gps_raw_last_time >= mm_gdbus_modem_location_get_gps_refresh_rate (skeleton))) {
+             time (NULL) - ctx->location_gps_raw_last_time >= (glong)mm_gdbus_modem_location_get_gps_refresh_rate (skeleton))) {
             ctx->location_gps_raw_last_time = time (NULL);
             update_raw = TRUE;
         }
diff --git a/src/mm-iface-modem-messaging.c b/src/mm-iface-modem-messaging.c
index b5be318..a49ba77 100644
--- a/src/mm-iface-modem-messaging.c
+++ b/src/mm-iface-modem-messaging.c
@@ -1151,11 +1151,11 @@
         skip_unknown_storages (storage_ctx->supported_mem2);
         skip_unknown_storages (storage_ctx->supported_mem3);
 
-        mem1 = mm_common_build_sms_storages_string ((MMSmsStorage *)storage_ctx->supported_mem1->data,
+        mem1 = mm_common_build_sms_storages_string ((MMSmsStorage *)(gpointer)storage_ctx->supported_mem1->data,
                                                     storage_ctx->supported_mem1->len);
-        mem2 = mm_common_build_sms_storages_string ((MMSmsStorage *)storage_ctx->supported_mem2->data,
+        mem2 = mm_common_build_sms_storages_string ((MMSmsStorage *)(gpointer)storage_ctx->supported_mem2->data,
                                                     storage_ctx->supported_mem2->len);
-        mem3 = mm_common_build_sms_storages_string ((MMSmsStorage *)storage_ctx->supported_mem3->data,
+        mem3 = mm_common_build_sms_storages_string ((MMSmsStorage *)(gpointer)storage_ctx->supported_mem3->data,
                                                     storage_ctx->supported_mem3->len);
 
         mm_obj_dbg (self, "supported storages loaded:");
diff --git a/src/mm-iface-modem-voice.c b/src/mm-iface-modem-voice.c
index ba6b52c..03aaeb0 100644
--- a/src/mm-iface-modem-voice.c
+++ b/src/mm-iface-modem-voice.c
@@ -1896,7 +1896,7 @@
                 self,
                 (GAsyncReadyCallback) setup_in_call_unsolicited_events_ready,
                 task);
-            break;
+            return;
         }
         ctx->step++;
         /* fall-through */
@@ -1907,7 +1907,7 @@
                 self,
                 (GAsyncReadyCallback) setup_in_call_audio_channel_ready,
                 task);
-            break;
+            return;
         }
         ctx->step++;
         /* fall-through */
@@ -2031,7 +2031,7 @@
                 self,
                 (GAsyncReadyCallback) cleanup_in_call_audio_channel_ready,
                 task);
-            break;
+            return;
         }
         ctx->step++;
         /* fall-through */
@@ -2042,7 +2042,7 @@
                 self,
                 (GAsyncReadyCallback) cleanup_in_call_unsolicited_events_ready,
                 task);
-            break;
+            return;
         }
         ctx->step++;
         /* fall-through */
@@ -2419,11 +2419,13 @@
         mm_3gpp_call_info_list_free (call_info_list);
     }
 
-    /* setup the polling again */
-    g_assert (!ctx->polling_id);
-    ctx->polling_id = g_timeout_add_seconds (CALL_LIST_POLLING_TIMEOUT_SECS,
-                                             (GSourceFunc) call_list_poll,
-                                             self);
+    /* setup the polling again, but only if it hasn't been done already while
+     * we reported calls (e.g. a new incoming call may have been detected that
+     * also triggers the poll setup) */
+    if (!ctx->polling_id)
+        ctx->polling_id = g_timeout_add_seconds (CALL_LIST_POLLING_TIMEOUT_SECS,
+                                                 (GSourceFunc) call_list_poll,
+                                                 self);
 }
 
 static void
diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c
index 14e64ec..48105af 100644
--- a/src/mm-iface-modem.c
+++ b/src/mm-iface-modem.c
@@ -2355,8 +2355,8 @@
             goto out;
         }
 
-        requested_str = mm_common_build_bands_string ((MMModemBand *)requested_bands->data, requested_bands->len);
-        current_str   = mm_common_build_bands_string ((MMModemBand *)current_bands->data, current_bands->len);
+        requested_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)requested_bands->data, requested_bands->len);
+        current_str   = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)current_bands->data, current_bands->len);
         error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
                              "reloaded current bands (%s) different to the requested ones (%s)",
                              current_str, requested_str);
@@ -2471,7 +2471,7 @@
                     gchar *supported_bands_str;
 
                     supported_bands_str = (mm_common_build_bands_string (
-                                               (const MMModemBand *)supported_bands_array->data,
+                                               (const MMModemBand *)(gconstpointer)supported_bands_array->data,
                                                supported_bands_array->len));
                     g_set_error (error,
                                  MM_CORE_ERROR,
@@ -2532,8 +2532,7 @@
         return;
     }
 
-    bands_string = mm_common_build_bands_string ((MMModemBand *)bands_array->data,
-                                                 bands_array->len);
+    bands_string = mm_common_build_bands_string ((const MMModemBand *)(gpointer)bands_array->data, bands_array->len);
 
     /* Get list of supported bands */
     ctx->supported_bands_array = (mm_common_bands_variant_to_garray (
@@ -3804,15 +3803,11 @@
     ENABLING_STEP_SET_POWER_STATE,
     ENABLING_STEP_CHECK_FOR_SIM_SWAP,
     ENABLING_STEP_FLOW_CONTROL,
-    ENABLING_STEP_SUPPORTED_CHARSETS,
-    ENABLING_STEP_CHARSET,
     ENABLING_STEP_LAST
 } EnablingStep;
 
 struct _EnablingContext {
     EnablingStep step;
-    MMModemCharset supported_charsets;
-    const MMModemCharset *current_charset;
     MmGdbusModem *skeleton;
 };
 
@@ -3892,52 +3887,6 @@
     interface_enabling_step (task);
 }
 
-static void
-load_supported_charsets_ready (MMIfaceModem *self,
-                               GAsyncResult *res,
-                               GTask *task)
-{
-    EnablingContext *ctx;
-    GError *error = NULL;
-
-    ctx = g_task_get_task_data (task);
-
-    ctx->supported_charsets =
-        MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets_finish (self, res, &error);
-    if (error) {
-        mm_obj_warn (self, "couldn't load supported charsets: %s", error->message);
-        g_error_free (error);
-    }
-
-    /* Go on to next step */
-    ctx->step++;
-    interface_enabling_step (task);
-}
-
-static void
-setup_charset_ready (MMIfaceModem *self,
-                     GAsyncResult *res,
-                     GTask *task)
-{
-    EnablingContext *ctx;
-    GError *error = NULL;
-
-    ctx = g_task_get_task_data (task);
-
-    if (!MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset_finish (self, res, &error)) {
-        mm_obj_dbg (self, "couldn't set charset '%s': %s",
-                    mm_modem_charset_to_string (*ctx->current_charset),
-                    error->message);
-        g_error_free (error);
-
-        /* Will retry step with some other charset type */
-    } else
-        /* Done, Go on to next step */
-        ctx->step++;
-
-    interface_enabling_step (task);
-}
-
 static const MMModemCharset best_charsets[] = {
     MM_MODEM_CHARSET_UTF8,
     MM_MODEM_CHARSET_UCS2,
@@ -3998,59 +3947,6 @@
         ctx->step++;
         /* fall-through */
 
-    case ENABLING_STEP_SUPPORTED_CHARSETS:
-        if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets &&
-            MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets_finish) {
-            MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets (
-                self,
-                (GAsyncReadyCallback)load_supported_charsets_ready,
-                task);
-            return;
-        }
-        ctx->step++;
-        /* fall-through */
-
-    case ENABLING_STEP_CHARSET:
-        /* Only try to set charsets if we were able to load supported ones */
-        if (ctx->supported_charsets > 0 &&
-            MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset &&
-            MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset_finish) {
-            gboolean next_to_try = FALSE;
-
-            while (!next_to_try) {
-                if (!ctx->current_charset)
-                    /* Switch the device's charset; we prefer UTF-8, but UCS2 will do too */
-                    ctx->current_charset = &best_charsets[0];
-                else
-                    /* Try with the next one */
-                    ctx->current_charset++;
-
-                if (*ctx->current_charset == MM_MODEM_CHARSET_UNKNOWN)
-                    break;
-
-                if (ctx->supported_charsets & (*ctx->current_charset))
-                    next_to_try = TRUE;
-            }
-
-            if (next_to_try) {
-                MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset (
-                    self,
-                    *ctx->current_charset,
-                    (GAsyncReadyCallback)setup_charset_ready,
-                    task);
-                return;
-            }
-
-            g_task_return_new_error (task,
-                                     MM_CORE_ERROR,
-                                     MM_CORE_ERROR_FAILED,
-                                     "Failed to find a usable modem character set");
-            g_object_unref (task);
-            return;
-        }
-        ctx->step++;
-        /* fall-through */
-
     case ENABLING_STEP_LAST:
         /* We are done without errors! */
         g_task_return_boolean (task, TRUE);
@@ -4104,6 +4000,8 @@
     INITIALIZATION_STEP_FIRST,
     INITIALIZATION_STEP_CURRENT_CAPABILITIES,
     INITIALIZATION_STEP_SUPPORTED_CAPABILITIES,
+    INITIALIZATION_STEP_SUPPORTED_CHARSETS,
+    INITIALIZATION_STEP_CHARSET,
     INITIALIZATION_STEP_BEARERS,
     INITIALIZATION_STEP_MANUFACTURER,
     INITIALIZATION_STEP_MODEL,
@@ -4129,6 +4027,8 @@
 struct _InitializationContext {
     InitializationStep step;
     MmGdbusModem *skeleton;
+    MMModemCharset supported_charsets;
+    const MMModemCharset *current_charset;
     GError *fatal_error;
 };
 
@@ -4344,6 +4244,52 @@
 STR_REPLY_READY_FN (device_identifier, "device identifier")
 
 static void
+load_supported_charsets_ready (MMIfaceModem *self,
+                               GAsyncResult *res,
+                               GTask *task)
+{
+    InitializationContext *ctx;
+    GError *error = NULL;
+
+    ctx = g_task_get_task_data (task);
+
+    ctx->supported_charsets =
+        MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets_finish (self, res, &error);
+    if (error) {
+        mm_obj_warn (self, "couldn't load supported charsets: %s", error->message);
+        g_error_free (error);
+    }
+
+    /* Go on to next step */
+    ctx->step++;
+    interface_initialization_step (task);
+}
+
+static void
+setup_charset_ready (MMIfaceModem *self,
+                     GAsyncResult *res,
+                     GTask *task)
+{
+    InitializationContext *ctx;
+    GError *error = NULL;
+
+    ctx = g_task_get_task_data (task);
+
+    if (!MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset_finish (self, res, &error)) {
+        mm_obj_dbg (self, "couldn't set charset '%s': %s",
+                    mm_modem_charset_to_string (*ctx->current_charset),
+                    error->message);
+        g_error_free (error);
+
+        /* Will retry step with some other charset type */
+    } else
+        /* Done, Go on to next step */
+        ctx->step++;
+
+    interface_initialization_step (task);
+}
+
+static void
 load_supported_modes_ready (MMIfaceModem *self,
                             GAsyncResult *res,
                             GTask *task)
@@ -4834,6 +4780,59 @@
         ctx->step++;
     } /* fall-through */
 
+    case INITIALIZATION_STEP_SUPPORTED_CHARSETS:
+        if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets &&
+            MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets_finish) {
+            MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets (
+                self,
+                (GAsyncReadyCallback)load_supported_charsets_ready,
+                task);
+            return;
+        }
+        ctx->step++;
+        /* fall-through */
+
+    case INITIALIZATION_STEP_CHARSET:
+        /* Only try to set charsets if we were able to load supported ones */
+        if (ctx->supported_charsets > 0 &&
+            MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset &&
+            MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset_finish) {
+            gboolean next_to_try = FALSE;
+
+            while (!next_to_try) {
+                if (!ctx->current_charset)
+                    /* Switch the device's charset; we prefer UTF-8, but UCS2 will do too */
+                    ctx->current_charset = &best_charsets[0];
+                else
+                    /* Try with the next one */
+                    ctx->current_charset++;
+
+                if (*ctx->current_charset == MM_MODEM_CHARSET_UNKNOWN)
+                    break;
+
+                if (ctx->supported_charsets & (*ctx->current_charset))
+                    next_to_try = TRUE;
+            }
+
+            if (next_to_try) {
+                MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset (
+                    self,
+                    *ctx->current_charset,
+                    (GAsyncReadyCallback)setup_charset_ready,
+                    task);
+                return;
+            }
+
+            g_task_return_new_error (task,
+                                     MM_CORE_ERROR,
+                                     MM_CORE_ERROR_FAILED,
+                                     "Failed to find a usable modem character set");
+            g_object_unref (task);
+            return;
+        }
+        ctx->step++;
+        /* fall-through */
+
     case INITIALIZATION_STEP_BEARERS: {
         MMBearerList *list = NULL;
 
@@ -5575,6 +5574,12 @@
 }
 
 gboolean
+mm_iface_modem_is_3gpp_5gnr (MMIfaceModem *self)
+{
+    return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_5GNR);
+}
+
+gboolean
 mm_iface_modem_is_cdma (MMIfaceModem *self)
 {
     return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_CDMA_EVDO);
diff --git a/src/mm-iface-modem.h b/src/mm-iface-modem.h
index 541d6f6..2df3b59 100644
--- a/src/mm-iface-modem.h
+++ b/src/mm-iface-modem.h
@@ -391,6 +391,7 @@
 gboolean          mm_iface_modem_is_3gpp                  (MMIfaceModem *self);
 gboolean          mm_iface_modem_is_3gpp_only             (MMIfaceModem *self);
 gboolean          mm_iface_modem_is_3gpp_lte              (MMIfaceModem *self);
+gboolean          mm_iface_modem_is_3gpp_5gnr             (MMIfaceModem *self);
 gboolean          mm_iface_modem_is_cdma                  (MMIfaceModem *self);
 gboolean          mm_iface_modem_is_cdma_only             (MMIfaceModem *self);
 
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index 0099ebe..cb7cbd7 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -3207,9 +3207,9 @@
 GStrv
 mm_3gpp_parse_cnum_exec_response (const gchar *reply)
 {
-    GArray *array = NULL;
-    GRegex *r;
-    GMatchInfo *match_info;
+    g_autoptr(GPtrArray)  array = NULL;
+    g_autoptr(GRegex)     r = NULL;
+    g_autoptr(GMatchInfo) match_info = NULL;
 
     /* Empty strings also return NULL list */
     if (!reply || !reply[0])
@@ -3219,26 +3219,22 @@
                      G_REGEX_UNGREEDY, 0, NULL);
     g_assert (r != NULL);
 
+    array = g_ptr_array_new ();
     g_regex_match (r, reply, 0, &match_info);
     while (g_match_info_matches (match_info)) {
-        gchar *number;
+        g_autofree gchar *number = NULL;
 
         number = g_match_info_fetch_named (match_info, "num");
-
-        if (number && number[0]) {
-            if (!array)
-                array = g_array_new (TRUE, TRUE, sizeof (gchar *));
-            g_array_append_val (array, number);
-        } else
-            g_free (number);
-
+        if (number && number[0])
+            g_ptr_array_add (array, g_steal_pointer (&number));
         g_match_info_next (match_info, NULL);
     }
 
-    g_match_info_free (match_info);
-    g_regex_unref (r);
+    if (!array->len)
+        return NULL;
 
-    return (array ? (GStrv) g_array_free (array, FALSE) : NULL);
+    g_ptr_array_add (array, NULL);
+    return (GStrv) g_ptr_array_free (g_steal_pointer (&array), FALSE);
 }
 
 /*************************************************************************/
diff --git a/src/mm-plugin-manager.c b/src/mm-plugin-manager.c
index 9ee4eda..e76c9a8 100644
--- a/src/mm-plugin-manager.c
+++ b/src/mm-plugin-manager.c
@@ -1617,6 +1617,21 @@
 }
 
 static void
+register_plugin_whitelist_vendor_ids (MMPluginManager *self,
+                                       MMPlugin        *plugin)
+{
+    const guint16 *vendor_ids;
+    guint          i;
+
+    if (!mm_filter_check_rule_enabled (self->priv->filter, MM_FILTER_RULE_PLUGIN_WHITELIST))
+        return;
+
+    vendor_ids = mm_plugin_get_allowed_vendor_ids (plugin);
+    for (i = 0; vendor_ids && vendor_ids[i]; i++)
+        mm_filter_register_plugin_whitelist_vendor_id (self->priv->filter, vendor_ids[i]);
+}
+
+static void
 register_plugin_whitelist_product_ids (MMPluginManager *self,
                                        MMPlugin        *plugin)
 {
@@ -1812,7 +1827,8 @@
             self->priv->plugins = g_list_append (self->priv->plugins, plugin);
 
         /* Register plugin whitelist rules in filter, if any */
-        register_plugin_whitelist_tags (self, plugin);
+        register_plugin_whitelist_tags        (self, plugin);
+        register_plugin_whitelist_vendor_ids  (self, plugin);
         register_plugin_whitelist_product_ids (self, plugin);
     }
 
diff --git a/src/mm-plugin.c b/src/mm-plugin.c
index 4f3ec18..adfe4bf 100644
--- a/src/mm-plugin.c
+++ b/src/mm-plugin.c
@@ -152,6 +152,12 @@
     return (const gchar **) self->priv->udev_tags;
 }
 
+const guint16 *
+mm_plugin_get_allowed_vendor_ids (MMPlugin *self)
+{
+    return self->priv->vendor_ids;
+}
+
 const mm_uint16_pair *
 mm_plugin_get_allowed_product_ids (MMPlugin *self)
 {
diff --git a/src/mm-plugin.h b/src/mm-plugin.h
index fe0469c..f586352 100644
--- a/src/mm-plugin.h
+++ b/src/mm-plugin.h
@@ -126,6 +126,7 @@
 
 const gchar           *mm_plugin_get_name                (MMPlugin *self);
 const gchar          **mm_plugin_get_allowed_udev_tags   (MMPlugin *self);
+const guint16         *mm_plugin_get_allowed_vendor_ids  (MMPlugin *self);
 const mm_uint16_pair  *mm_plugin_get_allowed_product_ids (MMPlugin *self);
 gboolean               mm_plugin_is_generic              (MMPlugin *self);
 
diff --git a/src/mm-port-qmi.c b/src/mm-port-qmi.c
index 3f39bfc..83d92b9 100644
--- a/src/mm-port-qmi.c
+++ b/src/mm-port-qmi.c
@@ -56,8 +56,10 @@
             QmiClient *found;
 
             found = info->client;
-            if (steal)
+            if (steal) {
                 self->priv->services = g_list_delete_link (self->priv->services, l);
+                g_free (info);
+            }
             return found;
         }
     }
diff --git a/src/mm-port.c b/src/mm-port.c
index b9a8020..285c89a 100644
--- a/src/mm-port.c
+++ b/src/mm-port.c
@@ -114,7 +114,7 @@
     self = MM_PORT (_self);
     return g_strdup_printf ("%s/%s",
                             mm_port_get_device (self),
-                            mm_modem_port_type_get_string (mm_port_get_port_type (self)));
+                            mm_port_type_get_string (mm_port_get_port_type (self)));
 }
 
 /*****************************************************************************/
diff --git a/src/mm-shared-qmi.c b/src/mm-shared-qmi.c
index 1b18719..bbcc169 100644
--- a/src/mm-shared-qmi.c
+++ b/src/mm-shared-qmi.c
@@ -3866,6 +3866,230 @@
 }
 
 /*****************************************************************************/
+/* Location: internal helper: setup minimum required NMEA traces */
+
+typedef struct {
+    QmiClientLoc *client;
+    guint         timeout_id;
+    gulong        indication_id;
+} SetupRequiredNmeaTracesContext;
+
+static void
+setup_required_nmea_traces_cleanup_action (SetupRequiredNmeaTracesContext *ctx)
+{
+    if (ctx->indication_id) {
+        g_signal_handler_disconnect (ctx->client, ctx->indication_id);
+        ctx->indication_id = 0;
+    }
+    if (ctx->timeout_id) {
+        g_source_remove (ctx->timeout_id);
+        ctx->timeout_id = 0;
+    }
+}
+
+static void
+setup_required_nmea_traces_context_free (SetupRequiredNmeaTracesContext *ctx)
+{
+    setup_required_nmea_traces_cleanup_action (ctx);
+    g_clear_object (&ctx->client);
+    g_slice_free (SetupRequiredNmeaTracesContext, ctx);
+}
+
+static gboolean
+setup_required_nmea_traces_finish (MMSharedQmi   *self,
+                                   GAsyncResult  *res,
+                                   GError       **error)
+{
+    return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static gboolean
+setup_required_nmea_traces_timeout (GTask *task)
+{
+    SetupRequiredNmeaTracesContext *ctx;
+
+    ctx = g_task_get_task_data (task);
+    g_assert (ctx->timeout_id);
+    setup_required_nmea_traces_cleanup_action (ctx);
+
+    g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
+                             "Operation timed out");
+    g_object_unref (task);
+    return G_SOURCE_REMOVE;
+}
+
+static void
+loc_set_nmea_types_indication_cb (QmiClientLoc                       *client,
+                                  QmiIndicationLocSetNmeaTypesOutput *output,
+                                  GTask                              *task)
+{
+    SetupRequiredNmeaTracesContext *ctx;
+    QmiLocIndicationStatus          status;
+    GError                         *error = NULL;
+
+    ctx = g_task_get_task_data (task);
+    g_assert (ctx->indication_id);
+    setup_required_nmea_traces_cleanup_action (ctx);
+
+    if (!qmi_indication_loc_set_nmea_types_output_get_indication_status (output, &status, &error) ||
+        !mm_error_from_qmi_loc_indication_status (status, &error))
+        g_task_return_error (task, error);
+    else
+        g_task_return_boolean (task, TRUE);
+    g_object_unref (task);
+}
+
+static void
+loc_set_nmea_types_ready (QmiClientLoc *client,
+                          GAsyncResult *res,
+                          GTask        *task)
+{
+    SetupRequiredNmeaTracesContext             *ctx;
+    GError                                     *error = NULL;
+    g_autoptr(QmiMessageLocSetNmeaTypesOutput)  output = NULL;
+
+    output = qmi_client_loc_set_nmea_types_finish (client, res, &error);
+    if (!output || !qmi_message_loc_set_nmea_types_output_get_result (output, &error)) {
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    /* The task ownership is shared between signal and timeout; the one which is
+     * scheduled first will cancel the other. */
+    ctx = g_task_get_task_data (task);
+    g_assert (!ctx->indication_id);
+    ctx->indication_id = g_signal_connect (ctx->client,
+                                           "set-nmea-types",
+                                           G_CALLBACK (loc_set_nmea_types_indication_cb),
+                                           task);
+    g_assert (!ctx->timeout_id);
+    ctx->timeout_id = g_timeout_add_seconds (10,
+                                             (GSourceFunc)setup_required_nmea_traces_timeout,
+                                             task);
+}
+
+static void
+loc_get_nmea_types_indication_cb (QmiClientLoc                       *client,
+                                  QmiIndicationLocGetNmeaTypesOutput *output,
+                                  GTask                              *task)
+{
+    SetupRequiredNmeaTracesContext            *ctx;
+    QmiLocIndicationStatus                     status;
+    QmiLocNmeaType                             nmea_types_mask = 0;
+    QmiLocNmeaType                             desired_nmea_types_mask = (QMI_LOC_NMEA_TYPE_GGA | QMI_LOC_NMEA_TYPE_GSA | QMI_LOC_NMEA_TYPE_GSV);
+    GError                                    *error = NULL;
+    g_autoptr(QmiMessageLocSetNmeaTypesInput)  input = NULL;
+
+    ctx = g_task_get_task_data (task);
+    g_assert (ctx->indication_id);
+    setup_required_nmea_traces_cleanup_action (ctx);
+
+    if (!qmi_indication_loc_get_nmea_types_output_get_indication_status (output, &status, &error) ||
+        !mm_error_from_qmi_loc_indication_status (status, &error)) {
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    qmi_indication_loc_get_nmea_types_output_get_nmea_types (output, &nmea_types_mask, NULL);
+
+    /* If the configured NMEA types already include GGA, GSV and GSA, we're fine. For raw
+     * GPS sources GGA is the only required one, the other two are given for completeness */
+    if ((nmea_types_mask & desired_nmea_types_mask) == desired_nmea_types_mask) {
+        g_task_return_boolean (task, TRUE);
+        g_object_unref (task);
+        return;
+    }
+
+    input = qmi_message_loc_set_nmea_types_input_new ();
+    qmi_message_loc_set_nmea_types_input_set_nmea_types (input, (nmea_types_mask | desired_nmea_types_mask), NULL);
+    qmi_client_loc_set_nmea_types (ctx->client,
+                                   input,
+                                   10,
+                                   NULL,
+                                   (GAsyncReadyCallback)loc_set_nmea_types_ready,
+                                   task);
+}
+
+static void
+loc_get_nmea_types_ready (QmiClientLoc *client,
+                          GAsyncResult *res,
+                          GTask        *task)
+{
+    SetupRequiredNmeaTracesContext             *ctx;
+    GError                                     *error = NULL;
+    g_autoptr(QmiMessageLocGetNmeaTypesOutput)  output = NULL;
+
+    output = qmi_client_loc_get_nmea_types_finish (client, res, &error);
+    if (!output || !qmi_message_loc_get_nmea_types_output_get_result (output, &error)) {
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    /* The task ownership is shared between signal and timeout; the one which is
+     * scheduled first will cancel the other. */
+    ctx = g_task_get_task_data (task);
+    g_assert (!ctx->indication_id);
+    ctx->indication_id = g_signal_connect (ctx->client,
+                                           "get-nmea-types",
+                                           G_CALLBACK (loc_get_nmea_types_indication_cb),
+                                           task);
+    g_assert (!ctx->timeout_id);
+    ctx->timeout_id = g_timeout_add_seconds (10,
+                                             (GSourceFunc)setup_required_nmea_traces_timeout,
+                                             task);
+}
+
+static void
+setup_required_nmea_traces (MMSharedQmi         *self,
+                            GAsyncReadyCallback  callback,
+                            gpointer             user_data)
+{
+    QmiClient *client;
+    GTask     *task;
+
+    task = g_task_new (self, NULL, callback, user_data);
+
+    /* If using PDS, no further setup required */
+    client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+                                        QMI_SERVICE_PDS,
+                                        MM_PORT_QMI_FLAG_DEFAULT,
+                                        NULL);
+    if (client) {
+        g_task_return_boolean (task, TRUE);
+        g_object_unref (task);
+        return;
+    }
+
+    /* Otherwise LOC */
+    client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+                                        QMI_SERVICE_LOC,
+                                        MM_PORT_QMI_FLAG_DEFAULT,
+                                        NULL);
+    if (client) {
+        SetupRequiredNmeaTracesContext *ctx;
+
+        ctx = g_slice_new0 (SetupRequiredNmeaTracesContext);
+        ctx->client = g_object_ref (client);
+        g_task_set_task_data (task, ctx, (GDestroyNotify)setup_required_nmea_traces_context_free);
+
+        qmi_client_loc_get_nmea_types (ctx->client,
+                                       NULL,
+                                       10,
+                                       NULL,
+                                       (GAsyncReadyCallback)loc_get_nmea_types_ready,
+                                       task);
+        return;
+    }
+
+    g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+                             "Couldn't find any PDS/LOC client");
+    g_object_unref (task);
+}
+
+/*****************************************************************************/
 /* Location: internal helper: start gps engine */
 
 static gboolean
@@ -4751,6 +4975,22 @@
 }
 
 static void
+setup_required_nmea_traces_ready (MMSharedQmi  *self,
+                                  GAsyncResult *res,
+                                  GTask        *task)
+{
+    g_autoptr(GError) error = NULL;
+
+    /* don't treat this error as fatal */
+    if (!setup_required_nmea_traces_finish (self, res, &error))
+        mm_obj_warn (self, "couldn't setup required NMEA traces: %s", error->message);
+
+    start_gps_engine (self,
+                      (GAsyncReadyCallback)start_gps_engine_ready,
+                      task);
+}
+
+static void
 set_gps_operation_mode_agps_ready (MMSharedQmi  *self,
                                    GAsyncResult *res,
                                    GTask        *task)
@@ -4822,11 +5062,11 @@
         return;
     }
 
-    /* Only start GPS engine if not done already */
+    /* Only setup NMEA traces and start GPS engine if not done already */
     if (!(priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW))) {
-        start_gps_engine (self,
-                          (GAsyncReadyCallback)start_gps_engine_ready,
-                          task);
+        setup_required_nmea_traces (self,
+                                    (GAsyncReadyCallback)setup_required_nmea_traces_ready,
+                                    task);
         return;
     }
 
diff --git a/src/mm-sms-part-3gpp.c b/src/mm-sms-part-3gpp.c
index 183d286..c4f6154 100644
--- a/src/mm-sms-part-3gpp.c
+++ b/src/mm-sms-part-3gpp.c
@@ -271,10 +271,10 @@
          * in UCS-2BE.
          */
         mm_obj_dbg (log_object, "converting SMS part text from UTF-16BE to UTF-8...");
-        utf8 = g_convert ((const gchar *) text, len, "UTF8", "UTF16BE", NULL, NULL, NULL);
+        utf8 = g_convert ((const gchar *) text, len, "UTF-8", "UTF-16BE", NULL, NULL, NULL);
         if (!utf8) {
-            mm_obj_dbg (log_object, "converting SMS part text from UCS-2BE to UTF8...");
-            utf8 = g_convert ((const gchar *) text, len, "UTF8", "UCS-2BE", NULL, NULL, NULL);
+            mm_obj_dbg (log_object, "converting SMS part text from UCS-2BE to UTF-8...");
+            utf8 = g_convert ((const gchar *) text, len, "UTF-8", "UCS-2BE", NULL, NULL, NULL);
         }
         if (!utf8) {
             mm_obj_warn (log_object, "couldn't convert SMS part contents from UTF-16BE/UCS-2BE to UTF-8: not decoding any text");
diff --git a/src/tests/test-charsets.c b/src/tests/test-charsets.c
index 5c9e187..0931d7e 100644
--- a/src/tests/test-charsets.c
+++ b/src/tests/test-charsets.c
@@ -316,13 +316,15 @@
 static void
 test_take_convert_ucs2_hex_utf8 (void)
 {
-    gchar *src, *converted;
+    gchar *src, *converted, *utf8;
 
     /* Ensure hex-encoded UCS-2 works */
     src = g_strdup ("0054002d004d006f00620069006c0065");
     converted = mm_charset_take_and_convert_to_utf8 (src, MM_MODEM_CHARSET_UCS2);
     g_assert_cmpstr (converted, ==, "T-Mobile");
-    g_free (converted);
+    utf8 = mm_utf8_take_and_convert_to_charset (converted, MM_MODEM_CHARSET_UCS2);
+    g_assert_cmpstr (utf8, ==, "0054002D004D006F00620069006C0065");
+    g_free (utf8);
 }
 
 static void
@@ -348,6 +350,19 @@
     g_assert (converted == NULL);
 }
 
+static void
+test_take_convert_gsm_utf8 (void)
+{
+    gchar *src, *converted, *utf8;
+
+    src = g_strdup ("T-Mobile");
+    converted = mm_charset_take_and_convert_to_utf8 (src, MM_MODEM_CHARSET_GSM);
+    g_assert_cmpstr (converted, ==, "T-Mobile");
+    utf8 = mm_utf8_take_and_convert_to_charset (converted, MM_MODEM_CHARSET_GSM);
+    g_assert_cmpstr (utf8, ==, "T-Mobile");
+    g_free (utf8);
+}
+
 struct charset_can_convert_to_test_s {
     const char *utf8;
     gboolean    to_gsm;
@@ -430,6 +445,7 @@
     g_test_add_func ("/MM/charsets/take-convert/ucs2/hex",         test_take_convert_ucs2_hex_utf8);
     g_test_add_func ("/MM/charsets/take-convert/ucs2/bad-ascii",   test_take_convert_ucs2_bad_ascii);
     g_test_add_func ("/MM/charsets/take-convert/ucs2/bad-ascii-2", test_take_convert_ucs2_bad_ascii2);
+    g_test_add_func ("/MM/charsets/take-convert/gsm",              test_take_convert_gsm_utf8);
 
     g_test_add_func ("/MM/charsets/can-convert-to", test_charset_can_covert_to);
 
diff --git a/src/tests/test-qcdm-serial-port.c b/src/tests/test-qcdm-serial-port.c
index 0b14282..2a9e1c4 100644
--- a/src/tests/test-qcdm-serial-port.c
+++ b/src/tests/test-qcdm-serial-port.c
@@ -54,7 +54,7 @@
         status = 0;
         ret = waitpid (d->child, &status, WNOHANG);
         g_get_current_time (&now);
-        if (d->child && (now.tv_sec - start.tv_sec > timeout)) {
+        if (d->child && (now.tv_sec - start.tv_sec > (glong)timeout)) {
             /* Kill it */
             if (g_test_verbose ())
                 g_message ("Killing running child process %d", d->child);