Merge cros/upstream to cros/master

Part of an uprev that contains the following commits:

" 95fec5ad Merge cros/upstream to cros/master (Pavan Holla)"
" 1c4ba762 plugin, telit: set last band to EUTRAN_85 (Alexey Orishko)"
" 8ecc0f64 plugin, telit: add support for LPWA modem (Alexey Orishko)"
" 908ab333 foxconn: updating the T77W968 requires MCFG+APPS version (Fanice.luo)"
" 1ef46a68 fibocom: Don't disconnect initial EPS bearer (Sven Schwermer)"
" 71a9739f fibocom: Set initial EPS bearer CID for MA510 (Sven Schwermer)"
" c509159d fibocom: Add initial EPS bearer support (Sven Schwermer)"
" 855977c4 fibocom: Implement power down/off (Sven Schwermer)"
" e5bb33a4 fibocom: Monitor connection status using +GTRNDIS? (Sven Schwermer)"
" 04ed52a7 mm-iface-modem: always require a SIM card in 3GPP modules (Akash Aggarwal)"
" ead9f180 fcc-unlock: add FCC unlock support for Quectel EM05-G (Aleksander Morgado)"
" fab04810 foxconn: remove QMI_SERVICE_FOX from Generic MBIM modem object (Fanice.luo)"
" a93c1f6f i18n: Update German translation (Jürgen Benvenuti)"
" 1db9cf46 sms-part-3gpp: coding style fixes (Aleksander Morgado)"
" 795103cf broadband-modem-mbim: ensure message array contains valid PDUs (Aleksander Morgado)"
" 125ef272 sim-mbim: fix race condition when sync requested during preload (Aleksander Morgado)"
" eee9a6f6 iface-modem-3gpp: disallow Scan() or Register() if Locked (Aleksander Morgado)"
" 976a3d4d libmm-glib,common-helpers: minor coding style fix in mm_new_iso8601_time() (Aleksander Morgado)"
" bfba2650 libmm-glib,common-helpers: don't assume new_from_unix_utc() always succeeds (Aleksander Morgado)"
" 4628056a mm-modem-helpers.c: adjust the RING regex (Angus Ainslie)"
" a74f11ae sim-mbim: fallback to AT commands if reading GID via MBIM fails (Aleksander Morgado)"
" fa6d6455 base-sim: implement GID1 and GID2 loading using AT commands (Aleksander Morgado)"
" 651ddb95 plugins,telit: LM940 has LTE band ext after given version (Carlo Lobrano)"
" 072c8eba plugins,telit: LM960 has LTE extended bands (Carlo Lobrano)"
" 7f82c38d plugins,telit: Detect alternate_3g_bands flag from modem model (Carlo Lobrano)"
" 72ca6676 plugins,telit: Detect ext_4g_bands flag from modem model (Carlo Lobrano)"
" b0c5756b plugins,telit: Add MM_TELIT_MODEL_FN990 (Carlo Lobrano)"
" 049c5ab2 plugins,telit: remove unnecessary argument (Carlo Lobrano)"
" 3ab3d5e8 plugins,telit: refactor to reduce scope of variable (Carlo Lobrano)"
" a55384d7 plugins,telit: refactor functions dealing with #BND (Carlo Lobrano)"
" 99cde839 cinterion: Fix CDC-ECM support for ELS61-E2 (Christian Taedcke)"
" a25b45f7 cinterion: add support for mode setting using SXRAT (Christian Taedcke)"
" fcd393a6 quectel: Trigger reprobe upon RDY URC (Sven Schwermer)"
" 2c66d43b broadband-modem-qmi: select last used level when enabling SAR (Aleksander Morgado)"
" 1ea5a577 iface-modem-sar: report updated level on SAR enable (Aleksander Morgado)"
" 950d36d4 iface-modem-sar: avoid changing level if already in the target level (Aleksander Morgado)"
" 72457c25 iface-modem-sar: avoid changing state if already in the target state (Aleksander Morgado)"
" b50c24ac iface-modem-sar: disallow changing SAR level if SAR disabled (Aleksander Morgado)"
" 0994087f iface-modem-sar: fix set power level async method completion (Aleksander Morgado)"
" 258b8f8e broadband-modem-qmi: attempt to preallocate SAR client (Aleksander Morgado)"
" 04e7f777 broadband-modem-qmi: Implement the SAR interface (Madhav)"
" 4a36f50d iface-modem-3gpp-profile-manager: fix requested IP type normalization (Aleksander Morgado)"

BUG=None
TEST=None

Cq-Depend: chromium:3960706,chromium:3961586,chromium:3961280
Change-Id: If0c85b7d141b3b07f78a8d6c4a59a0b36000c742
diff --git a/cli/mmcli-output.c b/cli/mmcli-output.c
index 1bb17f8..ef1c7cb 100644
--- a/cli/mmcli-output.c
+++ b/cli/mmcli-output.c
@@ -621,11 +621,11 @@
 /* (Custom) Bearer start date output */
 
 void
-mmcli_output_start_date (guint64    value)
+mmcli_output_start_date (guint64 value)
 {
     /* Merge value and recent flag in a single item in human output */
     if (selected_type == MMC_OUTPUT_TYPE_HUMAN) {
-        output_item_new_take_single (MMC_F_BEARER_STATS_START_DATE, mm_new_iso8601_time_from_unix_time (value));
+        output_item_new_take_single (MMC_F_BEARER_STATS_START_DATE, mm_new_iso8601_time_from_unix_time (value, NULL));
         return;
     }
 
diff --git a/data/dispatcher-fcc-unlock/2c7c b/data/dispatcher-fcc-unlock/2c7c
new file mode 100644
index 0000000..1161aec
--- /dev/null
+++ b/data/dispatcher-fcc-unlock/2c7c
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# SPDX-License-Identifier: CC0-1.0
+# 2022 Leah Oswald <mail@leahoswald.de>
+#
+# Queltec EM05-G FCC unlock mechanism
+#
+
+# require program name and at least 2 arguments
+[ $# -lt 2 ] && exit 1
+
+# first argument is DBus path, not needed here
+shift
+
+# second and next arguments are control port names
+for PORT in "$@"; do
+  # match port name
+  echo "$PORT" | grep -q cdc-wdm && {
+    CDC_WDM_PORT=$PORT
+    break
+  }
+done
+
+# fail if no cdc-wdm port exposed
+[ -n "$CDC_WDM_PORT" ] || exit 2
+
+# run mbimcli operation
+mbimcli --device-open-proxy --device="/dev/$CDC_WDM_PORT" --quectel-set-radio-state=on
+exit $?
diff --git a/data/dispatcher-fcc-unlock/Makefile.am b/data/dispatcher-fcc-unlock/Makefile.am
index a98a68f..7f6224c 100644
--- a/data/dispatcher-fcc-unlock/Makefile.am
+++ b/data/dispatcher-fcc-unlock/Makefile.am
@@ -11,6 +11,7 @@
 	105b \
 	1199 \
 	1eac \
+	2c7c \
 	$(NULL)
 
 EXTRA_DIST = $(fccunlockavailable_SCRIPTS)
@@ -26,6 +27,7 @@
 	$(LN_S) -f 1199 413c:81a3; \
 	$(LN_S) -f 1199 413c:81a8; \
 	$(LN_S) -f 1eac 1eac:1001; \
+	$(LN_S) -f 2c7c 2c7c:030a; \
 	$(NULL)
 
 uninstall-hook:
diff --git a/data/dispatcher-fcc-unlock/meson.build b/data/dispatcher-fcc-unlock/meson.build
index f945fbf..5dc3b6a 100644
--- a/data/dispatcher-fcc-unlock/meson.build
+++ b/data/dispatcher-fcc-unlock/meson.build
@@ -14,6 +14,7 @@
   '105b',
   '1199',
   '1eac',
+  '2c7c',
 )
 
 install_data(
@@ -29,6 +30,7 @@
   '413c:81a3': '1199',
   '413c:81a8': '1199',
   '1eac:1001': '1eac',
+  '2c7c:030a': '2c7c',
 }
 
 ln_cmd = 'ln -fs @0@ ${DESTDIR}@1@'
diff --git a/libmm-glib/mm-common-helpers.c b/libmm-glib/mm-common-helpers.c
index 085177b..5f3af4a 100644
--- a/libmm-glib/mm-common-helpers.c
+++ b/libmm-glib/mm-common-helpers.c
@@ -1752,11 +1752,18 @@
 }
 
 gchar *
-mm_new_iso8601_time_from_unix_time (guint64 timestamp)
+mm_new_iso8601_time_from_unix_time (guint64   timestamp,
+                                    GError  **error)
 {
     g_autoptr(GDateTime) dt = NULL;
 
     dt = g_date_time_new_from_unix_utc ((gint64)timestamp);
+    if (!dt) {
+        g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+                     "Invalid unix time: %" G_GUINT64_FORMAT,
+                     timestamp);
+        return NULL;
+    }
 
     return date_time_format_iso8601 (dt);
 }
@@ -1782,11 +1789,9 @@
     } else
         dt = g_date_time_new_utc (year, month, day, hour, minute, second);
 
-    if (dt == NULL) {
-        g_set_error (error,
-                     MM_CORE_ERROR,
-                     MM_CORE_ERROR_INVALID_ARGS,
-                     "Invalid input for date: got year:%u, month:%u, day:%u, hour:%u, minute:%u, second:%u",
+    if (!dt) {
+        g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+                     "Invalid date: year: %u, month: %u, day: %u, hour: %u, minute: %u, second: %u",
                      year, month, day, hour, minute, second);
         return NULL;
     }
diff --git a/libmm-glib/mm-common-helpers.h b/libmm-glib/mm-common-helpers.h
index 490f882..a92d5b5 100644
--- a/libmm-glib/mm-common-helpers.h
+++ b/libmm-glib/mm-common-helpers.h
@@ -208,16 +208,17 @@
 gchar    *mm_get_string_unquoted_from_match_info (GMatchInfo  *match_info,
                                                   guint32      match_index);
 
-gchar    *mm_new_iso8601_time_from_unix_time     (guint64  timestamp);
-gchar    *mm_new_iso8601_time                    (guint    year,
-                                                  guint    month,
-                                                  guint    day,
-                                                  guint    hour,
-                                                  guint    minute,
-                                                  guint    second,
-                                                  gboolean have_offset,
-                                                  gint     offset_minutes,
-                                                  GError **error);
+gchar    *mm_new_iso8601_time_from_unix_time     (guint64    timestamp,
+                                                  GError   **error);
+gchar    *mm_new_iso8601_time                    (guint      year,
+                                                  guint      month,
+                                                  guint      day,
+                                                  guint      hour,
+                                                  guint      minute,
+                                                  guint      second,
+                                                  gboolean   have_offset,
+                                                  gint       offset_minutes,
+                                                  GError   **error);
 
 /******************************************************************************/
 /* Type checkers and conversion utilities */
diff --git a/libmm-glib/tests/test-common-helpers.c b/libmm-glib/tests/test-common-helpers.c
index a1a2e61..fff37a8 100644
--- a/libmm-glib/tests/test-common-helpers.c
+++ b/libmm-glib/tests/test-common-helpers.c
@@ -604,7 +604,8 @@
     gchar *date = NULL;
     GError *error = NULL;
 
-    date = mm_new_iso8601_time_from_unix_time (1634307342);
+    date = mm_new_iso8601_time_from_unix_time (1634307342, &error);
+    g_assert_no_error (error);
     g_assert_cmpstr (date, ==, "2021-10-15T14:15:42Z");
     g_free (date);
 
@@ -636,6 +637,12 @@
     g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS);
     g_assert_null (date);
     g_clear_error (&error);
+
+    /* Too far into the future */
+    date = mm_new_iso8601_time_from_unix_time (G_MAXINT64, &error);
+    g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS);
+    g_assert_null (date);
+    g_clear_error (&error);
 }
 
 /**************************************************************/
diff --git a/plugins/cinterion/mm-broadband-bearer-cinterion.c b/plugins/cinterion/mm-broadband-bearer-cinterion.c
index e7ff392..e67d461 100644
--- a/plugins/cinterion/mm-broadband-bearer-cinterion.c
+++ b/plugins/cinterion/mm-broadband-bearer-cinterion.c
@@ -272,6 +272,23 @@
 }
 
 static void
+swwan_dial_operation_ready (MMBaseModem                *modem,
+                            GAsyncResult               *res,
+                            MMBroadbandBearerCinterion *self) /* full ref! */
+{
+    GError *error = NULL;
+
+    if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
+        mm_obj_warn (self, "data connection attempt failed: %s", error->message);
+        mm_base_bearer_report_connection_status (MM_BASE_BEARER (self),
+                                                 MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+        g_error_free (error);
+    }
+
+    g_object_unref (self);
+}
+
+static void
 handle_cancel_dial (GTask *task)
 {
     Dial3gppContext *ctx;
@@ -299,6 +316,8 @@
 {
     MMBroadbandBearerCinterion *self;
     Dial3gppContext            *ctx;
+    MMCinterionModemFamily      modem_family;
+    gboolean                    default_swwan_behavior;
 
     self = g_task_get_source_object (task);
     ctx  = g_task_get_task_data (task);
@@ -310,16 +329,19 @@
         return;
     }
 
+    modem_family = mm_broadband_modem_cinterion_get_family (MM_BROADBAND_MODEM_CINTERION (ctx->modem));
+    default_swwan_behavior = modem_family == MM_CINTERION_MODEM_FAMILY_DEFAULT;
+
     switch (ctx->step) {
     case DIAL_3GPP_CONTEXT_STEP_FIRST:
         ctx->step++;
         /* fall through */
 
     case DIAL_3GPP_CONTEXT_STEP_AUTH: {
-        gchar *command;
+        g_autofree gchar *command = NULL;
 
         command = mm_cinterion_build_auth_string (self,
-                                                  mm_broadband_modem_cinterion_get_family (MM_BROADBAND_MODEM_CINTERION (ctx->modem)),
+                                                  modem_family,
                                                   mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)),
                                                   ctx->cid);
 
@@ -336,7 +358,6 @@
                                            NULL,
                                            (GAsyncReadyCallback) common_dial_operation_ready,
                                            task);
-            g_free (command);
             return;
         }
 
@@ -345,13 +366,31 @@
     } /* fall through */
 
     case DIAL_3GPP_CONTEXT_STEP_START_SWWAN: {
-        gchar *command;
+        g_autofree gchar *command = NULL;
 
         mm_obj_dbg (self, "dial step %u/%u: starting SWWAN interface %u connection...",
                     ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST, usb_interface_configs[ctx->usb_interface_config_index].swwan_index);
         command = g_strdup_printf ("^SWWAN=1,%u,%u",
                                    ctx->cid,
                                    usb_interface_configs[ctx->usb_interface_config_index].swwan_index);
+
+        if (default_swwan_behavior) {
+            mm_base_modem_at_command_full (ctx->modem,
+                                           ctx->primary,
+                                           command,
+                                           MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
+                                           FALSE,
+                                           FALSE,
+                                           NULL,
+                                           (GAsyncReadyCallback) common_dial_operation_ready,
+                                           task);
+            return;
+        }
+
+        /* We "jump" to the last step here here since the modem expects the
+         * DHCP discover packet while ^SWWAN runs. If the command fails,
+         * we'll mark the bearer disconnected later in the callback.
+         */
         mm_base_modem_at_command_full (ctx->modem,
                                        ctx->primary,
                                        command,
@@ -359,13 +398,15 @@
                                        FALSE,
                                        FALSE,
                                        NULL,
-                                       (GAsyncReadyCallback) common_dial_operation_ready,
-                                       task);
-        g_free (command);
+                                       (GAsyncReadyCallback) swwan_dial_operation_ready,
+                                       g_object_ref (self));
+        ctx->step = DIAL_3GPP_CONTEXT_STEP_LAST;
+        dial_3gpp_context_step (task);
         return;
     }
 
     case DIAL_3GPP_CONTEXT_STEP_VALIDATE_CONNECTION:
+        g_assert (default_swwan_behavior);
         mm_obj_dbg (self, "dial step %u/%u: checking SWWAN interface %u status...",
                     ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST, usb_interface_configs[ctx->usb_interface_config_index].swwan_index);
         load_connection_status_by_cid (ctx->self,
diff --git a/plugins/cinterion/mm-broadband-modem-cinterion.c b/plugins/cinterion/mm-broadband-modem-cinterion.c
index 3be8da8..9079135 100644
--- a/plugins/cinterion/mm-broadband-modem-cinterion.c
+++ b/plugins/cinterion/mm-broadband-modem-cinterion.c
@@ -88,6 +88,10 @@
     GArray *cnmi_supported_ds;
     GArray *cnmi_supported_bfr;
 
+    /* Cached supported rats for SXRAT */
+    GArray *sxrat_supported_rat;
+    GArray *sxrat_supported_pref1;
+
     /* ignore regex */
     GRegex *sysstart_regex;
     /* +CIEV indications as configured via AT^SIND */
@@ -100,6 +104,10 @@
     FeatureSupport sind_psinfo_support;
     FeatureSupport smoni_support;
     FeatureSupport sind_simstatus_support;
+    FeatureSupport sxrat_support;
+
+    /* Mode combination to apply if "any" requested */
+    MMModemMode any_allowed;
 
     /* Flags for model-based behaviors */
     MMCinterionModemFamily modem_family;
@@ -1797,15 +1805,186 @@
 }
 
 static void
-load_supported_modes (MMIfaceModem *self,
+sxrat_load_supported_modes_ready (MMBroadbandModemCinterion *self,
+                                  GTask        *task)
+{
+    GArray *combinations;
+    MMModemModeCombination mode;
+
+    g_assert (self->priv->sxrat_supported_rat);
+    g_assert (self->priv->sxrat_supported_pref1);
+
+    /* Build list of combinations */
+    combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 3);
+
+    if (value_supported (self->priv->sxrat_supported_rat, 0)) {
+        /* 2G only */
+        mode.allowed = MM_MODEM_MODE_2G;
+        mode.preferred = MM_MODEM_MODE_NONE;
+        g_array_append_val (combinations, mode);
+    }
+    if (value_supported (self->priv->sxrat_supported_rat, 1)) {
+        /* 2G+3G with none preferred */
+        mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+        mode.preferred = MM_MODEM_MODE_NONE;
+        g_array_append_val (combinations, mode);
+
+        self->priv->any_allowed = mode.allowed;
+
+        if (value_supported (self->priv->sxrat_supported_pref1, 0)) {
+            /* 2G preferred */
+            mode.preferred = MM_MODEM_MODE_2G;
+            g_array_append_val (combinations, mode);
+        }
+        if (value_supported (self->priv->sxrat_supported_pref1, 2)) {
+            /* 3G preferred */
+            mode.preferred = MM_MODEM_MODE_3G;
+            g_array_append_val (combinations, mode);
+        }
+    }
+    if (value_supported (self->priv->sxrat_supported_rat, 2)) {
+        /* 3G only */
+        mode.allowed = MM_MODEM_MODE_3G;
+        mode.preferred = MM_MODEM_MODE_NONE;
+        g_array_append_val (combinations, mode);
+    }
+    if (value_supported (self->priv->sxrat_supported_rat, 3)) {
+        /* 4G only */
+        mode.allowed = MM_MODEM_MODE_4G;
+        mode.preferred = MM_MODEM_MODE_NONE;
+        g_array_append_val (combinations, mode);
+    }
+    if (value_supported (self->priv->sxrat_supported_rat, 4)) {
+        /* 3G+4G with none preferred */
+        mode.allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+        mode.preferred = MM_MODEM_MODE_NONE;
+        g_array_append_val (combinations, mode);
+
+        self->priv->any_allowed = mode.allowed;
+
+        if (value_supported (self->priv->sxrat_supported_pref1, 2)) {
+            /* 3G preferred */
+            mode.preferred = MM_MODEM_MODE_3G;
+            g_array_append_val (combinations, mode);
+        }
+        if (value_supported (self->priv->sxrat_supported_pref1, 3)) {
+            /* 4G preferred */
+            mode.preferred = MM_MODEM_MODE_4G;
+            g_array_append_val (combinations, mode);
+        }
+    }
+    if (value_supported (self->priv->sxrat_supported_rat, 5)) {
+        /* 2G+4G with none preferred */
+        mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G);
+        mode.preferred = MM_MODEM_MODE_NONE;
+        g_array_append_val (combinations, mode);
+
+        self->priv->any_allowed = mode.allowed;
+
+        if (value_supported (self->priv->sxrat_supported_pref1, 0)) {
+            /* 2G preferred */
+            mode.preferred = MM_MODEM_MODE_2G;
+            g_array_append_val (combinations, mode);
+        }
+        if (value_supported (self->priv->sxrat_supported_pref1, 3)) {
+            /* 4G preferred */
+            mode.preferred = MM_MODEM_MODE_4G;
+            g_array_append_val (combinations, mode);
+        }
+    }
+    if (value_supported (self->priv->sxrat_supported_rat, 6)) {
+        /* 2G+3G+4G with none preferred */
+        mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+        mode.preferred = MM_MODEM_MODE_NONE;
+        g_array_append_val (combinations, mode);
+
+        self->priv->any_allowed = mode.allowed;
+
+        if (value_supported (self->priv->sxrat_supported_pref1, 0)) {
+            /* 2G preferred */
+            mode.preferred = MM_MODEM_MODE_2G;
+            g_array_append_val (combinations, mode);
+        }
+        if (value_supported (self->priv->sxrat_supported_pref1, 2)) {
+            /* 3G preferred */
+            mode.preferred = MM_MODEM_MODE_3G;
+            g_array_append_val (combinations, mode);
+        }
+        if (value_supported (self->priv->sxrat_supported_pref1, 3)) {
+            /* 4G preferred */
+            mode.preferred = MM_MODEM_MODE_4G;
+            g_array_append_val (combinations, mode);
+        }
+    }
+
+    g_task_return_pointer (task, combinations, (GDestroyNotify) g_array_unref);
+    g_object_unref (task);
+}
+
+static void
+sxrat_test_ready (MMBaseModem *_self,
+                  GAsyncResult *res,
+                  GTask *task)
+{
+    MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+    g_autoptr(GError)          error = NULL;
+    const gchar               *response;
+
+    response = mm_base_modem_at_command_finish (_self, res, &error);
+    if (!error) {
+        mm_cinterion_parse_sxrat_test (response,
+                                       &self->priv->sxrat_supported_rat,
+                                       &self->priv->sxrat_supported_pref1,
+                                       NULL,
+                                       &error);
+        if (!error) {
+            self->priv->sxrat_support = FEATURE_SUPPORTED;
+            sxrat_load_supported_modes_ready (self, task);
+            return;
+        }
+        mm_obj_warn (self, "error reading SXRAT response: %s", error->message);
+    }
+
+    self->priv->sxrat_support = FEATURE_NOT_SUPPORTED;
+
+    /* Run parent's loading in case SXRAT is not supported */
+    iface_modem_parent->load_supported_modes (
+        MM_IFACE_MODEM (self),
+        (GAsyncReadyCallback)parent_load_supported_modes_ready,
+        task);
+}
+
+static void
+load_supported_modes (MMIfaceModem *_self,
                       GAsyncReadyCallback callback,
                       gpointer user_data)
 {
+    MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+    GTask *task;
+
+    task = g_task_new (self, NULL, callback, user_data);
+
+    /* First check SXRAT support, if not already done */
+    if (self->priv->sxrat_support == FEATURE_SUPPORT_UNKNOWN) {
+        mm_base_modem_at_command (MM_BASE_MODEM (self),
+                                  "^SXRAT=?",
+                                  3,
+                                  TRUE,
+                                  (GAsyncReadyCallback)sxrat_test_ready,
+                                  task);
+        return;
+    }
+
+    if (self->priv->sxrat_support == FEATURE_SUPPORTED) {
+        sxrat_load_supported_modes_ready (self, task);
+        return;
+    }
+
     /* Run parent's loading */
     iface_modem_parent->load_supported_modes (
         MM_IFACE_MODEM (self),
         (GAsyncReadyCallback)parent_load_supported_modes_ready,
-        g_task_new (self, NULL, callback, user_data));
+        task);
 }
 
 /*****************************************************************************/
@@ -1849,20 +2028,15 @@
 }
 
 static void
-set_current_modes (MMIfaceModem        *_self,
-                   MMModemMode          allowed,
-                   MMModemMode          preferred,
-                   GAsyncReadyCallback  callback,
-                   gpointer             user_data)
+cops_set_current_modes (MMBroadbandModemCinterion *self,
+                        MMModemMode allowed,
+                        MMModemMode preferred,
+                        GTask *task)
 {
-    MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
-    gchar                     *command;
-    GTask                     *task;
+    gchar *command;
 
     g_assert (preferred == MM_MODEM_MODE_NONE);
 
-    task = g_task_new (self, NULL, callback, user_data);
-
     /* We will try to simulate the possible allowed modes here. The
      * Cinterion devices do not seem to allow setting preferred access
      * technology in devices, but they allow restricting to a given
@@ -1874,11 +2048,11 @@
      *   which is based on the quality of the connection.
      */
 
-    if (mm_iface_modem_is_4g (_self) && allowed == MM_MODEM_MODE_4G)
+    if (mm_iface_modem_is_4g (MM_IFACE_MODEM (self)) && allowed == MM_MODEM_MODE_4G)
         command = g_strdup ("+COPS=,,,7");
-    else if (mm_iface_modem_is_3g (_self) && allowed == MM_MODEM_MODE_3G)
+    else if (mm_iface_modem_is_3g (MM_IFACE_MODEM (self)) && allowed == MM_MODEM_MODE_3G)
         command = g_strdup ("+COPS=,,,2");
-    else if (mm_iface_modem_is_2g (_self) && allowed == MM_MODEM_MODE_2G)
+    else if (mm_iface_modem_is_2g (MM_IFACE_MODEM (self)) && allowed == MM_MODEM_MODE_2G)
         command = g_strdup ("+COPS=,,,0");
     else {
         /* For any other combination (e.g. ANY or  no AcT given, defaults to Auto. For this case, we cannot provide
@@ -1902,6 +2076,60 @@
     g_free (command);
 }
 
+static void
+sxrat_set_current_modes (MMBroadbandModemCinterion *self,
+                         MMModemMode allowed,
+                         MMModemMode preferred,
+                         GTask *task)
+{
+    gchar *command;
+    GError *error = NULL;
+
+    g_assert (self->priv->any_allowed != MM_MODEM_MODE_NONE);
+
+    /* Handle ANY */
+    if (allowed == MM_MODEM_MODE_ANY)
+        allowed = self->priv->any_allowed;
+
+    command = mm_cinterion_build_sxrat_set_command (allowed, preferred, &error);
+
+    if (!command) {
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    mm_base_modem_at_command (
+        MM_BASE_MODEM (self),
+        command,
+        30,
+        FALSE,
+        (GAsyncReadyCallback)allowed_access_technology_update_ready,
+        task);
+
+    g_free (command);
+}
+
+static void
+set_current_modes (MMIfaceModem        *_self,
+                   MMModemMode          allowed,
+                   MMModemMode          preferred,
+                   GAsyncReadyCallback  callback,
+                   gpointer             user_data)
+{
+    MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+    GTask                     *task;
+
+    task = g_task_new (self, NULL, callback, user_data);
+
+    if (self->priv->sxrat_support == FEATURE_SUPPORTED)
+        sxrat_set_current_modes (self, allowed, preferred, task);
+    else if (self->priv->sxrat_support == FEATURE_NOT_SUPPORTED)
+        cops_set_current_modes (self, allowed, preferred, task);
+    else
+        g_assert_not_reached ();
+}
+
 /*****************************************************************************/
 /* Supported bands (Modem interface) */
 
@@ -2917,6 +3145,7 @@
     self->priv->swwan_support          = FEATURE_SUPPORT_UNKNOWN;
     self->priv->smoni_support          = FEATURE_SUPPORT_UNKNOWN;
     self->priv->sind_simstatus_support = FEATURE_SUPPORT_UNKNOWN;
+    self->priv->sxrat_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);
@@ -2924,6 +3153,8 @@
                                               G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
     self->priv->scks_regex = g_regex_new ("\\^SCKS:\\s*([0-3])\\r\\n",
                                           G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+
+    self->priv->any_allowed = MM_MODEM_MODE_NONE;
 }
 
 static void
@@ -2943,6 +3174,10 @@
         g_array_unref (self->priv->cnmi_supported_ds);
     if (self->priv->cnmi_supported_bfr)
         g_array_unref (self->priv->cnmi_supported_bfr);
+    if (self->priv->sxrat_supported_rat)
+        g_array_unref (self->priv->sxrat_supported_rat);
+    if (self->priv->sxrat_supported_pref1)
+        g_array_unref (self->priv->sxrat_supported_pref1);
 
     g_regex_unref (self->priv->ciev_regex);
     g_regex_unref (self->priv->sysstart_regex);
diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.c b/plugins/cinterion/mm-modem-helpers-cinterion.c
index 9219bff..f22a998 100644
--- a/plugins/cinterion/mm-modem-helpers-cinterion.c
+++ b/plugins/cinterion/mm-modem-helpers-cinterion.c
@@ -638,6 +638,94 @@
 }
 
 /*****************************************************************************/
+/* ^SXRAT test parser
+ *
+ * Example (ELS61-E2):
+ *   AT^SXRAT=?
+ *   ^SXRAT: (0-6),(0,2,3),(0,2,3)
+ */
+
+gboolean
+mm_cinterion_parse_sxrat_test (const gchar *response,
+                               GArray **supported_rat,
+                               GArray **supported_pref1,
+                               GArray **supported_pref2,
+                               GError **error)
+{
+    g_autoptr(GRegex)      r = NULL;
+    g_autoptr(GMatchInfo)  match_info = NULL;
+    GError                *inner_error = NULL;
+    GArray                *tmp_supported_rat = NULL;
+    GArray                *tmp_supported_pref1 = NULL;
+    GArray                *tmp_supported_pref2 = NULL;
+
+    if (!response) {
+        g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response");
+        return FALSE;
+    }
+
+    r = g_regex_new ("\\^SXRAT:\\s*\\(([^\\)]*)\\),\\(([^\\)]*)\\)(,\\(([^\\)]*)\\))?(?:\\r\\n)?",
+                     G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
+                     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)) {
+        if (supported_rat) {
+            g_autofree gchar *str = NULL;
+
+            str = mm_get_string_unquoted_from_match_info (match_info, 1);
+            tmp_supported_rat = mm_parse_uint_list (str, &inner_error);
+
+            if (inner_error)
+                goto out;
+        }
+        if (supported_pref1) {
+            g_autofree gchar *str = NULL;
+
+            str = mm_get_string_unquoted_from_match_info (match_info, 2);
+            tmp_supported_pref1 = mm_parse_uint_list (str, &inner_error);
+
+            if (inner_error)
+                goto out;
+        }
+        if (supported_pref2) {
+            g_autofree gchar *str = NULL;
+
+            /* this match is optional */
+            str = mm_get_string_unquoted_from_match_info (match_info, 4);
+            if (str) {
+                tmp_supported_pref2 = mm_parse_uint_list (str, &inner_error);
+
+                if (inner_error)
+                    goto out;
+            }
+        }
+    }
+
+out:
+
+    if (inner_error) {
+        g_clear_pointer (&tmp_supported_rat,   g_array_unref);
+        g_clear_pointer (&tmp_supported_pref1, g_array_unref);
+        g_clear_pointer (&tmp_supported_pref2, g_array_unref);
+        g_propagate_error (error, inner_error);
+        return FALSE;
+    }
+
+    if (supported_rat)
+        *supported_rat = tmp_supported_rat;
+    if (supported_pref1)
+        *supported_pref1 = tmp_supported_pref1;
+    if (supported_pref2)
+        *supported_pref2 = tmp_supported_pref2;
+
+    return TRUE;
+}
+
+/*****************************************************************************/
 /* Build Cinterion-specific band value */
 
 gboolean
@@ -1651,3 +1739,66 @@
                             quoted_passwd,
                             quoted_user);
 }
+
+/*****************************************************************************/
+/* ^SXRAT set command builder */
+
+/* Index of the array is the centerion-specific sxrat value */
+static const MMModemMode sxrat_combinations[] = {
+    [0] = ( MM_MODEM_MODE_2G ),
+    [1] = ( MM_MODEM_MODE_2G | MM_MODEM_MODE_3G ),
+    [2] = (                    MM_MODEM_MODE_3G ),
+    [3] = (                                       MM_MODEM_MODE_4G ),
+    [4] = (                    MM_MODEM_MODE_3G | MM_MODEM_MODE_4G ),
+    [5] = ( MM_MODEM_MODE_2G |                    MM_MODEM_MODE_4G ),
+    [6] = ( MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G ),
+};
+
+static gboolean
+append_sxrat_rat_value (GString      *str,
+                        MMModemMode   mode,
+                        GError      **error)
+{
+    guint i;
+
+    for (i = 0; i < G_N_ELEMENTS (sxrat_combinations); i++) {
+        if (sxrat_combinations[i] == mode) {
+            g_string_append_printf (str, "%u", i);
+            return TRUE;
+        }
+    }
+
+    g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+                 "No AcT value matches requested mode");
+    return FALSE;
+}
+
+gchar *
+mm_cinterion_build_sxrat_set_command (MMModemMode allowed,
+                                      MMModemMode preferred,
+                                      GError **error)
+{
+    GString *command;
+
+    command = g_string_new ("^SXRAT=");
+    if (!append_sxrat_rat_value (command, allowed, error)) {
+        g_string_free (command, TRUE);
+        return NULL;
+    }
+
+    if (preferred != MM_MODEM_MODE_NONE) {
+        if (mm_count_bits_set (preferred) != 1) {
+            *error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+                                  "AcT preferred value should be a single AcT");
+            g_string_free (command, TRUE);
+            return NULL;
+        }
+        g_string_append (command, ",");
+        if (!append_sxrat_rat_value (command, preferred, error)) {
+            g_string_free (command, TRUE);
+            return NULL;
+        }
+    }
+
+    return g_string_free (command, FALSE);
+}
diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.h b/plugins/cinterion/mm-modem-helpers-cinterion.h
index 7bbd789..3155d0c 100644
--- a/plugins/cinterion/mm-modem-helpers-cinterion.h
+++ b/plugins/cinterion/mm-modem-helpers-cinterion.h
@@ -84,6 +84,15 @@
                                        GError **error);
 
 /*****************************************************************************/
+/* ^SXRAT test parser */
+
+gboolean mm_cinterion_parse_sxrat_test (const gchar *response,
+                                        GArray **supported_rat,
+                                        GArray **supported_pref1,
+                                        GArray **supported_pref2,
+                                        GError **error);
+
+/*****************************************************************************/
 /* Build Cinterion-specific band value */
 
 gboolean mm_cinterion_build_band (GArray                       *bands,
@@ -192,4 +201,11 @@
                                        MMBearerProperties     *config,
                                        guint                   cid);
 
+/*****************************************************************************/
+/* ^SXRAT set command helper */
+
+gchar *mm_cinterion_build_sxrat_set_command (MMModemMode allowed,
+                                             MMModemMode preferred,
+                                             GError **error);
+
 #endif  /* MM_MODEM_HELPERS_CINTERION_H */
diff --git a/plugins/cinterion/tests/test-modem-helpers-cinterion.c b/plugins/cinterion/tests/test-modem-helpers-cinterion.c
index 8332700..d481619 100644
--- a/plugins/cinterion/tests/test-modem-helpers-cinterion.c
+++ b/plugins/cinterion/tests/test-modem-helpers-cinterion.c
@@ -1744,6 +1744,187 @@
 }
 
 /*****************************************************************************/
+/* Test ^SXRAT responses */
+
+static void
+common_test_sxrat (const gchar *response,
+                   const GArray *expected_rat,
+                   const GArray *expected_pref1,
+                   const GArray *expected_pref2)
+{
+    GArray *supported_rat = NULL;
+    GArray *supported_pref1 = NULL;
+    GArray *supported_pref2 = NULL;
+    GError *error = NULL;
+    gboolean res;
+
+    g_assert (expected_rat != NULL);
+    g_assert (expected_pref1 != NULL);
+
+    res = mm_cinterion_parse_sxrat_test (response,
+                                         &supported_rat,
+                                         &supported_pref1,
+                                         &supported_pref2,
+                                         &error);
+    g_assert_no_error (error);
+    g_assert (res == TRUE);
+    g_assert (supported_rat != NULL);
+    g_assert (supported_pref1 != NULL);
+    if (expected_pref2)
+        g_assert (supported_pref2 != NULL);
+    else
+        g_assert (supported_pref2 == NULL);
+
+    compare_arrays (supported_rat, expected_rat);
+    compare_arrays (supported_pref1, expected_pref1);
+    if (expected_pref2)
+        compare_arrays (supported_pref2, expected_pref2);
+
+    g_array_unref (supported_rat);
+    g_array_unref (supported_pref1);
+    if (supported_pref2)
+        g_array_unref (supported_pref2);
+}
+
+static void
+test_sxrat_response_els61 (void)
+{
+    GArray *expected_rat;
+    GArray *expected_pref1;
+    GArray *expected_pref2;
+    guint val;
+    const gchar *response =
+        "^SXRAT: (0-6),(0,2,3),(0,2,3)\r\n"
+        "\r\n";
+
+    expected_rat = g_array_sized_new (FALSE, FALSE, sizeof (guint), 7);
+    val = 0, g_array_append_val (expected_rat, val);
+    val = 1, g_array_append_val (expected_rat, val);
+    val = 2, g_array_append_val (expected_rat, val);
+    val = 3, g_array_append_val (expected_rat, val);
+    val = 4, g_array_append_val (expected_rat, val);
+    val = 5, g_array_append_val (expected_rat, val);
+    val = 6, g_array_append_val (expected_rat, val);
+
+    expected_pref1 = g_array_sized_new (FALSE, FALSE, sizeof (guint), 3);
+    val = 0, g_array_append_val (expected_pref1, val);
+    val = 2, g_array_append_val (expected_pref1, val);
+    val = 3, g_array_append_val (expected_pref1, val);
+
+    expected_pref2 = g_array_sized_new (FALSE, FALSE, sizeof (guint), 3);
+    val = 0, g_array_append_val (expected_pref2, val);
+    val = 2, g_array_append_val (expected_pref2, val);
+    val = 3, g_array_append_val (expected_pref2, val);
+
+    common_test_sxrat (response,
+                       expected_rat,
+                       expected_pref1,
+                       expected_pref2);
+
+    g_array_unref (expected_rat);
+    g_array_unref (expected_pref1);
+    g_array_unref (expected_pref2);
+}
+
+static void
+test_sxrat_response_other (void)
+{
+    GArray *expected_rat;
+    GArray *expected_pref1;
+    GArray *expected_pref2 = NULL;
+    guint val;
+    const gchar *response =
+        "^SXRAT: (0-2),(0,2)\r\n"
+        "\r\n";
+
+    expected_rat = g_array_sized_new (FALSE, FALSE, sizeof (guint), 3);
+    val = 0, g_array_append_val (expected_rat, val);
+    val = 1, g_array_append_val (expected_rat, val);
+    val = 2, g_array_append_val (expected_rat, val);
+
+    expected_pref1 = g_array_sized_new (FALSE, FALSE, sizeof (guint), 3);
+    val = 0, g_array_append_val (expected_pref1, val);
+    val = 2, g_array_append_val (expected_pref1, val);
+
+    common_test_sxrat (response,
+                       expected_rat,
+                       expected_pref1,
+                       expected_pref2);
+
+    g_array_unref (expected_rat);
+    g_array_unref (expected_pref1);
+}
+
+typedef struct {
+    const gchar *str;
+    MMModemMode  allowed;
+    MMModemMode  preferred;
+    gboolean     success;
+} SxratBuildTest;
+
+static const SxratBuildTest sxrat_build_tests[] = {
+    {
+     .str = "^SXRAT=0",
+     .allowed = MM_MODEM_MODE_2G,
+     .preferred = MM_MODEM_MODE_NONE,
+     .success = TRUE,
+    },
+    {
+     .str = "^SXRAT=3",
+     .allowed = MM_MODEM_MODE_4G,
+     .preferred = MM_MODEM_MODE_NONE,
+     .success = TRUE,
+    },
+    {
+     .str = "^SXRAT=1,2",
+     .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+     .preferred = MM_MODEM_MODE_3G,
+     .success = TRUE,
+    },
+    {
+     .str = "^SXRAT=6,3",
+     .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+     .preferred = MM_MODEM_MODE_4G,
+     .success = TRUE,
+    },
+    {
+     .str = NULL,
+     .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+     .preferred = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+     .success = FALSE,
+    },
+    {
+     .str = NULL,
+     .allowed = MM_MODEM_MODE_5G,
+     .preferred = MM_MODEM_MODE_NONE,
+     .success = FALSE,
+    },
+
+};
+
+static void
+test_sxrat (void)
+{
+    guint i;
+
+    for (i = 0; i < G_N_ELEMENTS (sxrat_build_tests); i++) {
+        GError   *error = NULL;
+        gchar* result;
+
+        result = mm_cinterion_build_sxrat_set_command (sxrat_build_tests[i].allowed,
+                                                       sxrat_build_tests[i].preferred,
+                                                       &error);
+        if (sxrat_build_tests[i].success) {
+            g_assert_no_error (error);
+            g_assert (result);
+            g_assert_cmpstr (result, ==, sxrat_build_tests[i].str);
+        } else {
+            g_assert (error);
+            g_assert (!result);
+        }
+    }
+}
+/*****************************************************************************/
 
 int main (int argc, char **argv)
 {
@@ -1778,6 +1959,9 @@
     g_test_add_func ("/MM/cinterion/smoni/query_response_to_signal", test_smoni_response_to_signal);
     g_test_add_func ("/MM/cinterion/scfg/provcfg",            test_provcfg_response);
     g_test_add_func ("/MM/cinterion/sgauth",                  test_sgauth_response);
+    g_test_add_func ("/MM/cinterion/sxrat",                   test_sxrat);
+    g_test_add_func ("/MM/cinterion/sxrat/response/els61",    test_sxrat_response_els61);
+    g_test_add_func ("/MM/cinterion/sxrat/response/other",    test_sxrat_response_other);
 
     return g_test_run ();
 }
diff --git a/plugins/fibocom/77-mm-fibocom-port-types.rules b/plugins/fibocom/77-mm-fibocom-port-types.rules
index 1b6c105..a2b9cfc 100644
--- a/plugins/fibocom/77-mm-fibocom-port-types.rules
+++ b/plugins/fibocom/77-mm-fibocom-port-types.rules
@@ -63,6 +63,7 @@
 ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a4", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_IGNORE}="1"
 ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a4", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
 
+
 # Fibocom MA510-GL (GTUSBMODE=31)
 #  ttyUSB0 (if #0): debug port (ignore)
 #  ttyUSB1 (if #1): AT port
@@ -70,12 +71,14 @@
 ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0106", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
 ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0106", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
 ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0106", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0106", ENV{ID_MM_FIBOCOM_INITIAL_EPS_CID}="1"
 
 # Fibocom MA510-GL (GTUSBMODE=32)
 #  ttyUSB1 (if #0): AT port
 #  ttyUSB2 (if #1): AT port
 ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="010a", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
 ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="010a", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="010a", ENV{ID_MM_FIBOCOM_INITIAL_EPS_CID}="1"
 
 # Fibocom L610 (GTUSBMODE=31)
 #  ttyUSB0 (if #0): AT port
diff --git a/plugins/fibocom/mm-broadband-bearer-fibocom-ecm.c b/plugins/fibocom/mm-broadband-bearer-fibocom-ecm.c
index 6d045e1..bc4ee1e 100644
--- a/plugins/fibocom/mm-broadband-bearer-fibocom-ecm.c
+++ b/plugins/fibocom/mm-broadband-bearer-fibocom-ecm.c
@@ -64,6 +64,73 @@
 }
 
 /*****************************************************************************/
+/* Connection status monitoring                                              */
+
+static MMBearerConnectionStatus
+load_connection_status_finish (MMBaseBearer  *bearer,
+                               GAsyncResult  *res,
+                               GError       **error)
+{
+    GError *inner_error = NULL;
+    gssize value;
+
+    value = g_task_propagate_int (G_TASK (res), &inner_error);
+    if (inner_error) {
+        g_propagate_error (error, inner_error);
+        return MM_BEARER_CONNECTION_STATUS_UNKNOWN;
+    }
+    return (MMBearerConnectionStatus) value;
+}
+
+static void
+gtrndis_query_ready (MMBaseModem  *modem,
+                     GAsyncResult *res,
+                     GTask        *task)
+{
+    MMBaseBearer *bearer;
+    GError       *error = NULL;
+    const gchar  *result;
+    guint         state;
+    guint         cid;
+
+    bearer = g_task_get_source_object (task);
+    result = mm_base_modem_at_command_finish (modem, res, &error);
+    if (!result)
+        g_task_return_error (task, error);
+    else if (!parse_gtrndis_read_response (result, &state, &cid, &error))
+        g_task_return_error (task, error);
+    else if (!state || (gint) cid != mm_base_bearer_get_profile_id (bearer))
+        g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+    else
+        g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_CONNECTED);
+
+    g_object_unref (task);
+}
+
+static void
+load_connection_status (MMBaseBearer        *bearer,
+                        GAsyncReadyCallback  callback,
+                        gpointer             user_data)
+{
+    GTask       *task;
+    MMBaseModem *modem = NULL;
+
+    task = g_task_new (bearer, NULL, callback, user_data);
+
+    g_object_get (MM_BASE_BEARER (bearer),
+                  MM_BASE_BEARER_MODEM, &modem,
+                  NULL);
+
+    mm_base_modem_at_command (modem,
+                              "+GTRNDIS?",
+                              3,
+                              FALSE,
+                              (GAsyncReadyCallback) gtrndis_query_ready,
+                              task);
+    g_object_unref (modem);
+}
+
+/*****************************************************************************/
 /* 3GPP Connect                                                              */
 
 typedef struct {
@@ -458,8 +525,12 @@
 static void
 mm_broadband_bearer_fibocom_ecm_class_init (MMBroadbandBearerFibocomEcmClass *klass)
 {
+    MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass);
     MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
 
+    base_bearer_class->load_connection_status = load_connection_status;
+    base_bearer_class->load_connection_status_finish = load_connection_status_finish;
+
     broadband_bearer_class->connect_3gpp = connect_3gpp;
     broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish;
     broadband_bearer_class->dial_3gpp = dial_3gpp;
diff --git a/plugins/fibocom/mm-broadband-modem-fibocom.c b/plugins/fibocom/mm-broadband-modem-fibocom.c
index 9a81943..9d65969 100644
--- a/plugins/fibocom/mm-broadband-modem-fibocom.c
+++ b/plugins/fibocom/mm-broadband-modem-fibocom.c
@@ -18,14 +18,22 @@
 #include "mm-broadband-modem-fibocom.h"
 #include "mm-broadband-bearer-fibocom-ecm.h"
 #include "mm-broadband-modem.h"
-#include "mm-iface-modem.h"
 #include "mm-base-modem-at.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-3gpp-profile-manager.h"
 #include "mm-log.h"
 
-static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_init                      (MMIfaceModem                   *iface);
+static void iface_modem_3gpp_init                 (MMIfaceModem3gpp               *iface);
+static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface);
+
+static MMIfaceModem3gppProfileManager *iface_modem_3gpp_profile_manager_parent;
 
 G_DEFINE_TYPE_EXTENDED (MMBroadbandModemFibocom, mm_broadband_modem_fibocom, MM_TYPE_BROADBAND_MODEM, 0,
-                        G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init))
+                        G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
+                        G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
+                        G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, iface_modem_3gpp_profile_manager_init))
 
 typedef enum {
     FEATURE_SUPPORT_UNKNOWN,
@@ -36,6 +44,8 @@
 struct _MMBroadbandModemFibocomPrivate {
     FeatureSupport  gtrndis_support;
     GRegex         *sim_ready_regex;
+    FeatureSupport  initial_eps_bearer_support;
+    gint            initial_eps_bearer_cid;
 };
 
 /*****************************************************************************/
@@ -164,12 +174,12 @@
 }
 
 /*****************************************************************************/
-/* Reset (Modem interface) */
+/* Reset / Power (Modem interface)                                           */
 
 static gboolean
-modem_reset_finish (MMIfaceModem *self,
-                    GAsyncResult *res,
-                    GError **error)
+modem_common_power_finish (MMIfaceModem *self,
+                           GAsyncResult *res,
+                           GError **error)
 {
     return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
 }
@@ -181,6 +191,32 @@
 {
     mm_base_modem_at_command (MM_BASE_MODEM (self),
                               "+CFUN=15",
+                              15,
+                              FALSE,
+                              callback,
+                              user_data);
+}
+
+static void
+modem_power_down (MMIfaceModem *self,
+                  GAsyncReadyCallback callback,
+                  gpointer user_data)
+{
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "+CFUN=4",
+                              15,
+                              FALSE,
+                              callback,
+                              user_data);
+}
+
+static void
+modem_power_off (MMIfaceModem *self,
+                 GAsyncReadyCallback callback,
+                 gpointer user_data)
+{
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "+CPWROFF",
                               3,
                               FALSE,
                               callback,
@@ -188,6 +224,430 @@
 }
 
 /*****************************************************************************/
+/* Load initial EPS bearer properties (as agreed with network)               */
+
+static MMBearerProperties *
+modem_3gpp_load_initial_eps_bearer_finish (MMIfaceModem3gpp  *self,
+                                           GAsyncResult      *res,
+                                           GError           **error)
+{
+    return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static void
+load_initial_eps_cgcontrdp_ready (MMBaseModem  *self,
+                                  GAsyncResult *res,
+                                  GTask        *task)
+{
+    GError                  *error = NULL;
+    const gchar             *response;
+    g_autofree gchar        *apn = NULL;
+    MMBearerProperties      *properties;
+
+    response = mm_base_modem_at_command_finish (self, res, &error);
+    if (!response || !mm_3gpp_parse_cgcontrdp_response (response, NULL, NULL, &apn, NULL, NULL, NULL, NULL, NULL, &error))
+        g_task_return_error (task, error);
+    else {
+        properties = mm_bearer_properties_new ();
+        mm_bearer_properties_set_apn (properties, apn);
+        g_task_return_pointer (task, properties, g_object_unref);
+    }
+
+    g_object_unref (task);
+}
+
+static void
+modem_3gpp_load_initial_eps_bearer (MMIfaceModem3gpp    *_self,
+                                    GAsyncReadyCallback  callback,
+                                    gpointer             user_data)
+{
+    MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self);
+    GTask                   *task;
+    g_autofree gchar        *cmd = NULL;
+
+    task = g_task_new (self, NULL, callback, user_data);
+
+    if (self->priv->initial_eps_bearer_support != FEATURE_SUPPORTED) {
+        g_task_return_new_error (task,
+                                 MM_CORE_ERROR,
+                                 MM_CORE_ERROR_UNSUPPORTED,
+                                 "Initial EPS bearer context ID unknown");
+        g_object_unref (task);
+        return;
+    }
+
+    g_assert (self->priv->initial_eps_bearer_cid >= 0);
+    cmd = g_strdup_printf ("+CGCONTRDP=%d", self->priv->initial_eps_bearer_cid);
+
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              cmd,
+                              3,
+                              FALSE,
+                              (GAsyncReadyCallback) load_initial_eps_cgcontrdp_ready,
+                              task);
+}
+
+/*****************************************************************************/
+/* Load initial EPS bearer settings (currently configured in modem)          */
+
+static MMBearerProperties *
+modem_3gpp_load_initial_eps_bearer_settings_finish (MMIfaceModem3gpp  *self,
+                                                    GAsyncResult      *res,
+                                                    GError           **error)
+{
+    return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static void
+load_initial_eps_bearer_get_profile_ready (MMIfaceModem3gppProfileManager *self,
+                                           GAsyncResult                   *res,
+                                           GTask                          *task)
+{
+    GError                   *error = NULL;
+    g_autoptr(MM3gppProfile)  profile = NULL;
+    MMBearerProperties       *properties;
+
+    profile = mm_iface_modem_3gpp_profile_manager_get_profile_finish (self, res, &error);
+    if (!profile) {
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    properties = mm_bearer_properties_new_from_profile (profile, &error);
+    if (!properties)
+        g_task_return_error (task, error);
+    else
+        g_task_return_pointer (task, properties, g_object_unref);
+    g_object_unref (task);
+}
+
+static void
+modem_3gpp_load_initial_eps_bearer_settings (MMIfaceModem3gpp    *_self,
+                                             GAsyncReadyCallback  callback,
+                                             gpointer             user_data)
+{
+    MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self);
+    MMPortSerialAt          *port;
+    MMKernelDevice          *device;
+    GTask                   *task;
+
+    /* Initial EPS bearer CID initialization run once only */
+    if (G_UNLIKELY (self->priv->initial_eps_bearer_support == FEATURE_SUPPORT_UNKNOWN)) {
+        /* There doesn't seem to be a programmatic way to find the initial EPS
+         * bearer's CID, so we'll use a udev variable. */
+        port = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+        device = mm_port_peek_kernel_device (MM_PORT (port));
+        if (mm_kernel_device_has_global_property (device, "ID_MM_FIBOCOM_INITIAL_EPS_CID")) {
+            self->priv->initial_eps_bearer_support = FEATURE_SUPPORTED;
+            self->priv->initial_eps_bearer_cid = mm_kernel_device_get_global_property_as_int (
+                device, "ID_MM_FIBOCOM_INITIAL_EPS_CID");
+        }
+        else
+            self->priv->initial_eps_bearer_support = FEATURE_NOT_SUPPORTED;
+    }
+
+    task = g_task_new (self, NULL, callback, user_data);
+
+    if (self->priv->initial_eps_bearer_support != FEATURE_SUPPORTED) {
+        g_task_return_new_error (task,
+                                 MM_CORE_ERROR,
+                                 MM_CORE_ERROR_UNSUPPORTED,
+                                 "Initial EPS bearer context ID unknown");
+        g_object_unref (task);
+        return;
+    }
+
+    g_assert (self->priv->initial_eps_bearer_cid >= 0);
+    mm_iface_modem_3gpp_profile_manager_get_profile (
+        MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self),
+        self->priv->initial_eps_bearer_cid,
+        (GAsyncReadyCallback) load_initial_eps_bearer_get_profile_ready,
+        task);
+}
+
+/*****************************************************************************/
+/* Set initial EPS bearer settings                                           */
+
+typedef enum {
+    SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LOAD_POWER_STATE = 0,
+    SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_DOWN,
+    SET_INITIAL_EPS_BEARER_SETTINGS_STEP_MODIFY_PROFILE,
+    SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_UP,
+    SET_INITIAL_EPS_BEARER_SETTINGS_STEP_FINISH,
+} SetInitialEpsStep;
+
+typedef struct {
+    MM3gppProfile     *profile;
+    SetInitialEpsStep  step;
+    MMModemPowerState  power_state;
+} SetInitialEpsContext;
+
+static void
+set_initial_eps_context_free (SetInitialEpsContext *ctx)
+{
+    g_object_unref (ctx->profile);
+    g_slice_free (SetInitialEpsContext, ctx);
+}
+
+static gboolean
+modem_3gpp_set_initial_eps_bearer_settings_finish (MMIfaceModem3gpp  *self,
+                                                   GAsyncResult      *res,
+                                                   GError           **error)
+{
+    return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void set_initial_eps_step (GTask *task);
+
+static void
+set_initial_eps_bearer_power_up_ready (MMBaseModem  *_self,
+                                       GAsyncResult *res,
+                                       GTask        *task)
+{
+    MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self);
+    SetInitialEpsContext    *ctx;
+    GError                  *error = NULL;
+
+    ctx = g_task_get_task_data (task);
+
+    if (!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up_finish (MM_IFACE_MODEM (self), res, &error)) {
+        g_prefix_error (&error, "Couldn't power up modem: ");
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    ctx->step++;
+    set_initial_eps_step (task);
+}
+
+static void
+set_initial_eps_bearer_modify_profile_ready (MMIfaceModem3gppProfileManager *self,
+                                             GAsyncResult                   *res,
+                                             GTask                          *task)
+{
+    GError                   *error = NULL;
+    SetInitialEpsContext     *ctx;
+    g_autoptr(MM3gppProfile)  stored = NULL;
+
+    ctx = g_task_get_task_data (task);
+
+    stored = mm_iface_modem_3gpp_profile_manager_set_profile_finish (self, res, &error);
+    if (!stored) {
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    ctx->step++;
+    set_initial_eps_step (task);
+}
+
+static void
+set_initial_eps_bearer_power_down_ready (MMBaseModem  *self,
+                                         GAsyncResult *res,
+                                         GTask        *task)
+{
+    SetInitialEpsContext *ctx;
+    GError               *error = NULL;
+
+    ctx = g_task_get_task_data (task);
+
+    if (!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down_finish (MM_IFACE_MODEM (self), res, &error)) {
+        g_prefix_error (&error, "Couldn't power down modem: ");
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    ctx->step++;
+    set_initial_eps_step (task);
+}
+
+static void
+set_initial_eps_bearer_load_power_state_ready (MMBaseModem  *self,
+                                               GAsyncResult *res,
+                                               GTask        *task)
+{
+    SetInitialEpsContext *ctx;
+    GError               *error = NULL;
+
+    ctx = g_task_get_task_data (task);
+
+    ctx->power_state = MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state_finish (MM_IFACE_MODEM (self), res, &error);
+    if (error) {
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    ctx->step++;
+    set_initial_eps_step (task);
+}
+
+static void
+set_initial_eps_step (GTask *task)
+{
+    MMBroadbandModemFibocom *self;
+    SetInitialEpsContext    *ctx;
+
+    self = g_task_get_source_object (task);
+    ctx  = g_task_get_task_data (task);
+
+    switch (ctx->step) {
+    case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LOAD_POWER_STATE:
+        mm_obj_dbg (self, "querying current power state...");
+        g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state);
+        g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state_finish);
+        MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state (
+            MM_IFACE_MODEM (self),
+            (GAsyncReadyCallback) set_initial_eps_bearer_load_power_state_ready,
+            task);
+        return;
+
+    case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_DOWN:
+        if (ctx->power_state == MM_MODEM_POWER_STATE_ON) {
+            mm_obj_dbg (self, "powering down before changing initial EPS bearer settings...");
+            g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down);
+            g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down_finish);
+            MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down (
+                MM_IFACE_MODEM (self),
+                (GAsyncReadyCallback) set_initial_eps_bearer_power_down_ready,
+                task);
+            return;
+        }
+        ctx->step++;
+        /* fall through */
+
+    case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_MODIFY_PROFILE:
+        mm_obj_dbg (self, "modifying initial EPS bearer settings profile...");
+        mm_iface_modem_3gpp_profile_manager_set_profile (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self),
+                                                         ctx->profile,
+                                                         "profile-id",
+                                                         TRUE,
+                                                         (GAsyncReadyCallback) set_initial_eps_bearer_modify_profile_ready,
+                                                         task);
+        return;
+
+    case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_UP:
+        if (ctx->power_state == MM_MODEM_POWER_STATE_ON) {
+            mm_obj_dbg (self, "powering up after changing initial EPS bearer settings...");
+            g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up);
+            g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up_finish);
+            MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up (
+                MM_IFACE_MODEM (self),
+                (GAsyncReadyCallback) set_initial_eps_bearer_power_up_ready,
+                task);
+            return;
+        }
+        ctx->step++;
+        /* fall through */
+
+    case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_FINISH:
+        g_task_return_boolean (task, TRUE);
+        g_object_unref (task);
+        return;
+
+    default:
+        g_assert_not_reached ();
+    }
+}
+
+static void
+modem_3gpp_set_initial_eps_bearer_settings (MMIfaceModem3gpp    *_self,
+                                            MMBearerProperties  *properties,
+                                            GAsyncReadyCallback  callback,
+                                            gpointer             user_data)
+{
+    MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self);
+    GTask                   *task;
+    MM3gppProfile           *profile;
+    MMBearerIpFamily         ip_family;
+    SetInitialEpsContext    *ctx;
+
+    task = g_task_new (self, NULL, callback, user_data);
+
+    if (self->priv->initial_eps_bearer_support != FEATURE_SUPPORTED) {
+        g_task_return_new_error (task,
+                                 MM_CORE_ERROR,
+                                 MM_CORE_ERROR_UNSUPPORTED,
+                                 "Initial EPS bearer context ID unknown");
+        g_object_unref (task);
+        return;
+    }
+
+    profile = mm_bearer_properties_peek_3gpp_profile (properties);
+    g_assert (self->priv->initial_eps_bearer_cid >= 0);
+    mm_3gpp_profile_set_profile_id (profile, self->priv->initial_eps_bearer_cid);
+    ip_family = mm_3gpp_profile_get_ip_type (profile);
+    if (ip_family == MM_BEARER_IP_FAMILY_NONE || ip_family == MM_BEARER_IP_FAMILY_ANY)
+        mm_3gpp_profile_set_ip_type (profile, MM_BEARER_IP_FAMILY_IPV4);
+
+    /* Setup context */
+    ctx = g_slice_new0 (SetInitialEpsContext);
+    ctx->profile = g_object_ref (profile);
+    ctx->step = SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LOAD_POWER_STATE;
+    g_task_set_task_data (task, ctx, (GDestroyNotify) set_initial_eps_context_free);
+
+    set_initial_eps_step (task);
+}
+
+/*****************************************************************************/
+/* Deactivate profile (3GPP profile management interface) */
+
+static gboolean
+modem_3gpp_profile_manager_deactivate_profile_finish (MMIfaceModem3gppProfileManager  *self,
+                                                      GAsyncResult                    *res,
+                                                      GError                         **error)
+{
+    return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+profile_manager_parent_deactivate_profile_ready (MMIfaceModem3gppProfileManager *self,
+                                                 GAsyncResult                   *res,
+                                                 GTask                          *task)
+{
+    GError *error = NULL;
+    if (iface_modem_3gpp_profile_manager_parent->deactivate_profile_finish(self, res, &error))
+        g_task_return_boolean (task, TRUE);
+    else
+        g_task_return_error (task, error);
+    g_object_unref (task);
+}
+
+static void
+modem_3gpp_profile_manager_deactivate_profile (MMIfaceModem3gppProfileManager *_self,
+                                               MM3gppProfile                  *profile,
+                                               GAsyncReadyCallback             callback,
+                                               gpointer                        user_data)
+{
+    MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self);
+    GTask                   *task;
+    gint                     profile_id;
+
+    task = g_task_new (self, NULL, callback, user_data);
+    profile_id = mm_3gpp_profile_get_profile_id (profile);
+
+    if (self->priv->initial_eps_bearer_support == FEATURE_SUPPORTED) {
+        g_assert (self->priv->initial_eps_bearer_cid >= 0);
+        if (self->priv->initial_eps_bearer_cid == profile_id) {
+            mm_obj_dbg (self, "skipping profile deactivation (initial EPS bearer)");
+            g_task_return_boolean (task, TRUE);
+            g_object_unref (task);
+            return;
+        }
+    }
+
+    iface_modem_3gpp_profile_manager_parent->deactivate_profile (
+        _self,
+        profile,
+        (GAsyncReadyCallback) profile_manager_parent_deactivate_profile_ready,
+        task);
+}
+
+/*****************************************************************************/
 
 static void
 setup_ports (MMBroadbandModem *_self)
@@ -240,9 +700,9 @@
                                               MMBroadbandModemFibocomPrivate);
 
     self->priv->gtrndis_support = FEATURE_SUPPORT_UNKNOWN;
-
     self->priv->sim_ready_regex = g_regex_new ("\\r\\n\\+SIM READY\\r\\n",
                                                G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+    self->priv->initial_eps_bearer_support = FEATURE_SUPPORT_UNKNOWN;
 }
 
 static void
@@ -261,7 +721,31 @@
     iface->create_bearer = modem_create_bearer;
     iface->create_bearer_finish = modem_create_bearer_finish;
     iface->reset = modem_reset;
-    iface->reset_finish = modem_reset_finish;
+    iface->reset_finish = modem_common_power_finish;
+    iface->modem_power_down = modem_power_down;
+    iface->modem_power_down_finish = modem_common_power_finish;
+    iface->modem_power_off = modem_power_off;
+    iface->modem_power_off_finish = modem_common_power_finish;
+}
+
+static void
+iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
+{
+    iface->load_initial_eps_bearer = modem_3gpp_load_initial_eps_bearer;
+    iface->load_initial_eps_bearer_finish = modem_3gpp_load_initial_eps_bearer_finish;
+    iface->load_initial_eps_bearer_settings = modem_3gpp_load_initial_eps_bearer_settings;
+    iface->load_initial_eps_bearer_settings_finish = modem_3gpp_load_initial_eps_bearer_settings_finish;
+    iface->set_initial_eps_bearer_settings = modem_3gpp_set_initial_eps_bearer_settings;
+    iface->set_initial_eps_bearer_settings_finish = modem_3gpp_set_initial_eps_bearer_settings_finish;
+}
+
+static void
+iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface)
+{
+    iface_modem_3gpp_profile_manager_parent = g_type_interface_peek_parent (iface);
+
+    iface->deactivate_profile = modem_3gpp_profile_manager_deactivate_profile;
+    iface->deactivate_profile_finish = modem_3gpp_profile_manager_deactivate_profile_finish;
 }
 
 static void
diff --git a/plugins/foxconn/mm-broadband-modem-mbim-foxconn.c b/plugins/foxconn/mm-broadband-modem-mbim-foxconn.c
index cfc0237..cec1c61 100644
--- a/plugins/foxconn/mm-broadband-modem-mbim-foxconn.c
+++ b/plugins/foxconn/mm-broadband-modem-mbim-foxconn.c
@@ -36,6 +36,7 @@
 #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
 # include "mm-iface-modem-firmware.h"
 # include "mm-shared-qmi.h"
+# include "mm-log.h"
 #endif
 
 static void iface_modem_location_init (MMIfaceModemLocation *iface);
@@ -96,6 +97,31 @@
     return (vendor_id == 0x105b || (vendor_id == 0x0489 && (product_id  == 0xe0da || product_id == 0xe0db)));
 }
 
+/*****************************************************************************/
+/* Need APPS version for the development of different functions when T77W968 support FASTBOOT and QMI PDC.
+ * Such as: T77W968.F1.0.0.5.2.GC.013.037 and T77W968.F1.0.0.5.2.GC.013.049, the MCFG version(T77W968.F1.0.0.5.2.GC.013) is same,
+ * but the APPS version(037 and 049) is different.
+ *
+ * For T77W968.F1.0.0.5.2.GC.013.049, before the change, "fwupdmgr get-devices" can obtain Current version is T77W968.F1.0.0.5.2.GC.013,
+ * it only include the MCFG version.
+ * After add need APPS version, it shows Current version is T77W968.F1.0.0.5.2.GC.013.049, including the MCFG+APPS version.
+ */
+
+static gboolean
+needs_fastboot_and_qmi_pdc_mcfg_apps_version (MMIfaceModemFirmware *self)
+{
+    guint vendor_id;
+    guint product_id;
+
+    /* T77W968(0x413c:0x81d7 ; 0x413c:0x81e0 ; 0x413c:0x81e4 ; 0x413c:0x81e6): supports FASTBOOT and QMI PDC,
+     * and requires MCFG+APPS version.
+     * else support FASTBOOT and QMI PDC, and require only MCFG version.
+     */
+    vendor_id = mm_base_modem_get_vendor_id (MM_BASE_MODEM (self));
+    product_id = mm_base_modem_get_product_id (MM_BASE_MODEM (self));
+    return (vendor_id == 0x413c && (product_id == 0x81d7 || product_id == 0x81e0 || product_id == 0x81e4 || product_id == 0x81e6));
+}
+
 static MMFirmwareUpdateSettings *
 create_update_settings (MMIfaceModemFirmware *self,
                         const gchar          *version_str)
@@ -164,15 +190,19 @@
 }
 
 static void
-firmware_load_update_settings (MMIfaceModemFirmware *self,
-                               GAsyncReadyCallback   callback,
-                               gpointer              user_data)
+mbim_port_allocate_qmi_client_ready (MMPortMbim     *mbim,
+                                     GAsyncResult   *res,
+                                     GTask          *task)
 {
-    GTask     *task;
-    QmiClient *fox_client = NULL;
-    QmiClient *dms_client = NULL;
+    MMIfaceModemFirmware *self;
+    QmiClient            *fox_client = NULL;
+    QmiClient            *dms_client = NULL;
+    g_autoptr(GError)     error = NULL;
 
-    task = g_task_new (self, NULL, callback, user_data);
+    self = g_task_get_source_object (task);
+
+    if (!mm_port_mbim_allocate_qmi_client_finish (mbim, res, &error))
+        mm_obj_dbg (self, "Allocate FOX client failed: %s", error->message);
 
     /* Try to get firmware version over fox service, if it failed to peek client, try dms service. */
     fox_client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_FOX, MM_PORT_QMI_FLAG_DEFAULT, NULL);
@@ -209,7 +239,7 @@
 
         input = qmi_message_dms_foxconn_get_firmware_version_input_new ();
         qmi_message_dms_foxconn_get_firmware_version_input_set_version_type (input,
-                                                                             (needs_qdu_and_mcfg_apps_version (self) ?
+                                                                             ((needs_qdu_and_mcfg_apps_version (self) || needs_fastboot_and_qmi_pdc_mcfg_apps_version (self)) ?
                                                                               QMI_DMS_FOXCONN_FIRMWARE_VERSION_TYPE_FIRMWARE_MCFG_APPS:
                                                                               QMI_DMS_FOXCONN_FIRMWARE_VERSION_TYPE_FIRMWARE_MCFG),
                                                                              NULL);
@@ -225,6 +255,24 @@
     g_assert_not_reached ();
 }
 
+static void
+firmware_load_update_settings (MMIfaceModemFirmware *self,
+                               GAsyncReadyCallback   callback,
+                               gpointer              user_data)
+{
+    GTask      *task;
+    MMPortMbim *mbim;
+
+    task = g_task_new (self, NULL, callback, user_data);
+
+    mbim = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self));
+    mm_port_mbim_allocate_qmi_client (mbim,
+                                      QMI_SERVICE_FOX,
+                                      NULL,
+                                      (GAsyncReadyCallback)mbim_port_allocate_qmi_client_ready,
+                                      task);
+}
+
 #endif
 
 /*****************************************************************************/
diff --git a/plugins/quectel/mm-shared-quectel.c b/plugins/quectel/mm-shared-quectel.c
index c383dc0..47d7cd3 100644
--- a/plugins/quectel/mm-shared-quectel.c
+++ b/plugins/quectel/mm-shared-quectel.c
@@ -56,6 +56,7 @@
     FeatureSupport         qgps_supported;
     GRegex                *qgpsurc_regex;
     GRegex                *qlwurc_regex;
+    GRegex                *rdy_regex;
 } Private;
 
 static void
@@ -63,6 +64,7 @@
 {
     g_regex_unref (priv->qgpsurc_regex);
     g_regex_unref (priv->qlwurc_regex);
+    g_regex_unref (priv->rdy_regex);
     g_slice_free (Private, priv);
 }
 
@@ -83,6 +85,11 @@
         priv->qgps_supported    = FEATURE_SUPPORT_UNKNOWN;
         priv->qgpsurc_regex     = g_regex_new ("\\r\\n\\+QGPSURC:.*", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
         priv->qlwurc_regex      = g_regex_new ("\\r\\n\\+QLWURC:.*", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+        priv->rdy_regex         = g_regex_new ("\\r\\nRDY", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+
+        g_assert (priv->qgpsurc_regex);
+        g_assert (priv->qlwurc_regex);
+        g_assert (priv->rdy_regex);
 
         g_assert (MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_broadband_modem_class);
         priv->broadband_modem_class_parent = MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_broadband_modem_class (self);
@@ -99,6 +106,23 @@
 }
 
 /*****************************************************************************/
+/* RDY unsolicited event handler */
+
+static void
+rdy_handler (MMPortSerialAt *port,
+             GMatchInfo *match_info,
+             MMBroadbandModem *self)
+{
+    /* The RDY URC indicates a modem reset that may or may not go hand-in-hand
+     * with USB re-enumeration. For the latter case, we must make sure to
+     * re-synchronize modem and ModemManager states by re-probing.
+     */
+    mm_obj_warn (self, "modem reset detected, triggering reprobe");
+    mm_base_modem_set_reprobe (MM_BASE_MODEM (self), TRUE);
+    mm_base_modem_set_valid (MM_BASE_MODEM (self), FALSE);
+}
+
+/*****************************************************************************/
 /* Setup ports (Broadband modem class) */
 
 void
@@ -134,6 +158,14 @@
             ports[i],
             priv->qlwurc_regex,
             NULL, NULL, NULL);
+
+        /* Handle RDY */
+        mm_port_serial_at_add_unsolicited_msg_handler (
+            ports[i],
+            priv->rdy_regex,
+            (MMPortSerialAtUnsolicitedMsgFn)rdy_handler,
+            self,
+            NULL);
     }
 }
 
diff --git a/plugins/telit/77-mm-telit-port-types.rules b/plugins/telit/77-mm-telit-port-types.rules
index b422984..b9439ff 100644
--- a/plugins/telit/77-mm-telit-port-types.rules
+++ b/plugins/telit/77-mm-telit-port-types.rules
@@ -122,20 +122,6 @@
 ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7011", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
 ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7011", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
 
-# LM940/960 use alternate settings for 3G band management
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{ID_MM_TELIT_BND_ALTERNATE}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{ID_MM_TELIT_BND_ALTERNATE}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1042", ENV{ID_MM_TELIT_BND_ALTERNATE}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1043", ENV{ID_MM_TELIT_BND_ALTERNATE}="1"
-
-# FN980 use alternate settings for 3G band management
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{ID_MM_TELIT_BND_ALTERNATE}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{ID_MM_TELIT_BND_ALTERNATE}="1"
-
-# LN920 use alternate settings for 3G band management
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{ID_MM_TELIT_BND_ALTERNATE}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{ID_MM_TELIT_BND_ALTERNATE}="1"
-
 # LM940/960 initial port delay
 ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{ID_MM_TELIT_PORT_DELAY}="1"
 ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{ID_MM_TELIT_PORT_DELAY}="1"
diff --git a/plugins/telit/mm-broadband-modem-telit.c b/plugins/telit/mm-broadband-modem-telit.c
index bbf3f76..3254eef 100644
--- a/plugins/telit/mm-broadband-modem-telit.c
+++ b/plugins/telit/mm-broadband-modem-telit.c
@@ -69,7 +69,6 @@
     MMModemLocationSource enabled_sources;
 };
 
-
 typedef struct {
     MMModemLocationSource source;
     guint gps_enable_step;
@@ -1070,11 +1069,11 @@
 /* Load access technologies (Modem interface) */
 
 static gboolean
-load_access_technologies_finish (MMIfaceModem *self,
-                                 GAsyncResult *res,
+load_access_technologies_finish (MMIfaceModem            *self,
+                                 GAsyncResult            *res,
                                  MMModemAccessTechnology *access_technologies,
-                                 guint *mask,
-                                 GError **error)
+                                 guint                   *mask,
+                                 GError                 **error)
 {
     GVariant *result;
 
@@ -1091,6 +1090,106 @@
 }
 
 static MMBaseModemAtResponseProcessorResult
+response_processor_cops_ignore_at_errors (MMBaseModem   *self,
+                                          gpointer       none,
+                                          const gchar   *command,
+                                          const gchar   *response,
+                                          gboolean       last_command,
+                                          const GError  *error,
+                                          GVariant     **result,
+                                          GError       **result_error)
+{
+    g_autoptr(GMatchInfo) match_info = NULL;
+    g_autoptr(GRegex) r = NULL;
+    guint actval = 0;
+    guint mode = 0;
+    guint vid;
+    guint pid;
+
+    *result = NULL;
+    *result_error = NULL;
+
+    if (error) {
+        /* Ignore AT errors (ie, ERROR or CMx ERROR) */
+        if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) {
+            *result_error = g_error_copy (error);
+            return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+        }
+        return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
+    }
+
+    vid = mm_base_modem_get_vendor_id (self);
+    pid = mm_base_modem_get_product_id (self);
+
+    if (!(vid == 0x1bc7 && (pid == 0x110a || pid == 0x110b))) {
+        /* AcT for non-LPWA modems would be checked by other command */
+        return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
+    }
+
+    r = g_regex_new ("\\+COPS:\\s*(\\d+),(\\d+),([^,]*)(?:,(\\d+))?(?:\\r\\n)?",
+                     0,
+                     0,
+                     NULL);
+    g_assert (r != NULL);
+
+    if (!g_regex_match (r, response, 0, &match_info)) {
+        g_set_error (result_error,
+                     MM_CORE_ERROR,
+                     MM_CORE_ERROR_FAILED,
+                     "Can't match +COPS? response: '%s'",
+                     response);
+        return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+    }
+
+    if (!mm_get_uint_from_match_info (match_info, 1, &mode)) {
+        g_set_error (result_error,
+                     MM_CORE_ERROR,
+                     MM_CORE_ERROR_FAILED,
+                     "Failed to parse mode in +COPS? response: '%s'",
+                     response);
+        return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+    }
+
+    if (mode == 2) {
+        g_set_error (result_error,
+                    MM_CORE_ERROR,
+                    MM_CORE_ERROR_FAILED,
+                    "Modem deregistered from the network: aborting AcT query");
+        return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+    }
+
+    if (!mm_get_uint_from_match_info (match_info, 4, &actval)) {
+        g_set_error (result_error,
+                     MM_CORE_ERROR,
+                     MM_CORE_ERROR_FAILED,
+                     "Failed to parse act in +COPS? response: '%s'",
+                     response);
+        return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+    }
+
+    switch (actval) {
+    case 0:
+        *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_GSM);
+        return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+    case 8:
+        *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_LTE_CAT_M);
+        return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+    case 9:
+        *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_LTE_NB_IOT);
+        return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+    default:
+        break;
+    }
+
+    g_set_error (result_error,
+                 MM_CORE_ERROR,
+                 MM_CORE_ERROR_FAILED,
+                 "Failed to map act in +COPS? response: '%s'",
+                 response);
+    return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+}
+
+static MMBaseModemAtResponseProcessorResult
 response_processor_psnt_ignore_at_errors (MMBaseModem   *self,
                                           gpointer       none,
                                           const gchar   *command,
@@ -1208,6 +1307,7 @@
 }
 
 static const MMBaseModemAtCommand access_tech_commands[] = {
+    { "+COPS?",    3, FALSE, response_processor_cops_ignore_at_errors },
     { "#PSNT?",    3, FALSE, response_processor_psnt_ignore_at_errors },
     { "+SERVICE?", 3, FALSE, response_processor_service_ignore_at_errors },
     { NULL }
diff --git a/plugins/telit/mm-modem-helpers-telit.c b/plugins/telit/mm-modem-helpers-telit.c
index 54cd6a0..c0df809 100644
--- a/plugins/telit/mm-modem-helpers-telit.c
+++ b/plugins/telit/mm-modem-helpers-telit.c
@@ -187,7 +187,7 @@
 #define B4G_FLAG(band) (((guint64) 1) << (band - MM_MODEM_BAND_TELIT_4G_FIRST))
 
 #define MM_MODEM_BAND_TELIT_EXT_4G_FIRST MM_MODEM_BAND_EUTRAN_65
-#define MM_MODEM_BAND_TELIT_EXT_4G_LAST  MM_MODEM_BAND_EUTRAN_71
+#define MM_MODEM_BAND_TELIT_EXT_4G_LAST  MM_MODEM_BAND_EUTRAN_85
 
 #define B4G_FLAG_EXT(band) (((guint64) 1) << (band - MM_MODEM_BAND_TELIT_EXT_4G_FIRST))
 
@@ -195,13 +195,9 @@
 /* Set current bands helpers */
 
 gchar *
-mm_telit_build_bnd_request (GArray    *bands_array,
-                            gboolean   modem_is_2g,
-                            gboolean   modem_is_3g,
-                            gboolean   modem_is_4g,
-                            gboolean   modem_alternate_3g_bands,
-                            gboolean   modem_ext_4g_bands,
-                            GError   **error)
+mm_telit_build_bnd_request (GArray                 *bands_array,
+                            MMTelitBNDParseConfig  *config,
+                            GError                **error)
 {
     guint32        mask2g = 0;
     guint64        mask3g = 0;
@@ -212,19 +208,9 @@
     gint64         flag3g = -1;
     gint64         flag4g = -1;
     gchar         *cmd;
-    const guint64 *telit_3g_to_mm_band_mask;
-    guint          telit_3g_to_mm_band_mask_n_elements;
-
-    initialize_telit_3g_to_mm_band_masks ();
-
-    /* Select correct 3G band mask */
-    if (modem_alternate_3g_bands) {
-        telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_alternate;
-        telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_alternate);
-    } else {
-        telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_default;
-        telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_default);
-    }
+    gboolean       modem_is_2g = config->modem_is_2g;
+    gboolean       modem_is_3g = config->modem_is_3g;
+    gboolean       modem_is_4g = config->modem_is_4g;
 
     for (i = 0; i < bands_array->len; i++) {
         MMModemBand band;
@@ -273,6 +259,19 @@
 
     /* Get 3G-specific telit value */
     if (mask3g) {
+        const guint64 *telit_3g_to_mm_band_mask;
+        guint          telit_3g_to_mm_band_mask_n_elements;
+
+        initialize_telit_3g_to_mm_band_masks ();
+
+        /* Select correct 3G band mask */
+        if (config->modem_alternate_3g_bands) {
+            telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_alternate;
+            telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_alternate);
+        } else {
+            telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_default;
+            telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_default);
+        }
         for (i = 0; i < telit_3g_to_mm_band_mask_n_elements; i++) {
             if (mask3g == telit_3g_to_mm_band_mask[i]) {
                 flag3g = i;
@@ -318,24 +317,24 @@
     else if (modem_is_2g && modem_is_3g && !modem_is_4g)
         cmd = g_strdup_printf ("#BND=%d,%" G_GINT64_FORMAT, flag2g, flag3g);
     else if (!modem_is_2g && !modem_is_3g && modem_is_4g) {
-        if (!modem_ext_4g_bands)
+        if (!config->modem_ext_4g_bands)
             cmd = g_strdup_printf ("#BND=0,0,%" G_GINT64_FORMAT, flag4g);
         else
             cmd = g_strdup_printf ("#BND=0,0,%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", mask4g, mask4gext);
     } else if (!modem_is_2g && modem_is_3g && modem_is_4g) {
-        if (!modem_ext_4g_bands)
+        if (!config->modem_ext_4g_bands)
             cmd = g_strdup_printf ("#BND=0,%" G_GINT64_FORMAT ",%" G_GINT64_FORMAT, flag3g, flag4g);
         else
             cmd = g_strdup_printf ("#BND=0,%" G_GINT64_FORMAT ",%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", flag3g, mask4g, mask4gext);
     } else if (modem_is_2g && !modem_is_3g && modem_is_4g) {
-        if (!modem_ext_4g_bands)
+        if (!config->modem_ext_4g_bands)
             cmd = g_strdup_printf ("#BND=%d,0,%" G_GINT64_FORMAT, flag2g, flag4g);
         else
             cmd = g_strdup_printf ("#BND=%d,0,%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", flag2g, mask4g, mask4gext);
     } else if (modem_is_2g && modem_is_3g && modem_is_4g) {
-        if (!modem_ext_4g_bands)
+        if (!config->modem_ext_4g_bands)
             cmd = g_strdup_printf ("#BND=%d,%" G_GINT64_FORMAT ",%" G_GINT64_FORMAT, flag2g, flag3g, flag4g);
-      else
+        else
             cmd = g_strdup_printf ("#BND=%d,%" G_GINT64_FORMAT ",%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", flag2g, flag3g, mask4g, mask4gext);
     } else
         g_assert_not_reached ();
@@ -668,15 +667,6 @@
     LOAD_BANDS_TYPE_CURRENT,
 } LoadBandsType;
 
-static gboolean
-bnd_response_has_ext_4g_bands (const gchar *response)
-{
-    g_auto(GStrv) tokens = NULL;
-
-    tokens = mm_split_string_groups (response);
-    return g_strv_length (tokens) == 4;
-}
-
 /* Regex tokens for #BND parsing */
 #define MM_SUPPORTED_BANDS_2G       "\\s*\\((?P<Bands2G>[0-9\\-,]*)\\)"
 #define MM_SUPPORTED_BANDS_3G       "(,\\s*\\((?P<Bands3G>[0-9\\-,]*)\\))?"
@@ -690,16 +680,11 @@
 #define MM_CURRENT_BANDS_4G_EXT     "(,\\s*(?P<Bands4GHex>[0-9A-F]+))?(,\\s*(?P<Bands4GExt>[0-9A-F]+))?"
 
 static GArray *
-common_parse_bnd_response (const gchar    *response,
-                           gboolean        modem_is_2g,
-                           gboolean        modem_is_3g,
-                           gboolean        modem_is_4g,
-                           gboolean        modem_alternate_3g_bands,
-                           gboolean        modem_has_hex_format_4g_bands,
-                           gboolean        modem_ext_4g_bands,
-                           LoadBandsType   load_type,
-                           gpointer        log_object,
-                           GError        **error)
+common_parse_bnd_response (const gchar            *response,
+                           MMTelitBNDParseConfig  *config,
+                           LoadBandsType           load_type,
+                           gpointer                log_object,
+                           GError                **error)
 {
     g_autoptr(GMatchInfo)  match_info = NULL;
     g_autoptr(GRegex)      r = NULL;
@@ -721,9 +706,9 @@
         [LOAD_BANDS_TYPE_CURRENT]   = "#BND:"MM_CURRENT_BANDS_2G MM_CURRENT_BANDS_3G MM_CURRENT_BANDS_4G_EXT,
     };
 
-    if (modem_ext_4g_bands)
+    if (config->modem_ext_4g_bands)
         load_bands_regex = load_bands_regex_4g_ext[load_type];
-    else if (modem_has_hex_format_4g_bands)
+    else if (config->modem_has_hex_format_4g_bands)
         load_bands_regex = load_bands_regex_4g_hex[load_type];
     else
         load_bands_regex = load_bands_regex_4g_dec[load_type];
@@ -745,16 +730,16 @@
 
     bands = g_array_new (TRUE, TRUE, sizeof (MMModemBand));
 
-    if (modem_is_2g && !telit_get_2g_mm_bands (match_info, log_object, &bands, &inner_error))
+    if (config->modem_is_2g && !telit_get_2g_mm_bands (match_info, log_object, &bands, &inner_error))
         goto out;
 
-    if (modem_is_3g && !telit_get_3g_mm_bands (match_info, log_object, modem_alternate_3g_bands, &bands, &inner_error))
+    if (config->modem_is_3g && !telit_get_3g_mm_bands (match_info, log_object, config->modem_alternate_3g_bands, &bands, &inner_error))
         goto out;
 
-    if (modem_is_4g) {
+    if (config->modem_is_4g) {
         gboolean ok;
 
-        ok = modem_ext_4g_bands?
+        ok = config->modem_ext_4g_bands?
             telit_get_ext_4g_mm_bands (match_info, &bands, &inner_error) :
             telit_get_4g_mm_bands (match_info, &bands, &inner_error);
         if (!ok)
@@ -771,43 +756,26 @@
 }
 
 GArray *
-mm_telit_parse_bnd_query_response (const gchar  *response,
-                                   gboolean      modem_is_2g,
-                                   gboolean      modem_is_3g,
-                                   gboolean      modem_is_4g,
-                                   gboolean      modem_alternate_3g_bands,
-                                   gboolean      modem_has_hex_format_4g_bands,
-                                   gboolean      modem_ext_4g_bands,
-                                   gpointer      log_object,
-                                   GError      **error)
+mm_telit_parse_bnd_query_response (const gchar            *response,
+                                   MMTelitBNDParseConfig  *config,
+                                   gpointer                log_object,
+                                   GError                **error)
 {
     return common_parse_bnd_response (response,
-                                      modem_is_2g, modem_is_3g, modem_is_4g,
-                                      modem_alternate_3g_bands,
-                                      modem_has_hex_format_4g_bands,
-                                      modem_ext_4g_bands,
+                                      config,
                                       LOAD_BANDS_TYPE_CURRENT,
                                       log_object,
                                       error);
 }
 
 GArray *
-mm_telit_parse_bnd_test_response (const gchar  *response,
-                                  gboolean      modem_is_2g,
-                                  gboolean      modem_is_3g,
-                                  gboolean      modem_is_4g,
-                                  gboolean      modem_alternate_3g_bands,
-                                  gboolean      modem_has_hex_format_4g_bands,
-                                  gboolean     *modem_ext_4g_bands,
-                                  gpointer      log_object,
-                                  GError      **error)
+mm_telit_parse_bnd_test_response (const gchar            *response,
+                                  MMTelitBNDParseConfig  *config,
+                                  gpointer                log_object,
+                                  GError                **error)
 {
-    *modem_ext_4g_bands = bnd_response_has_ext_4g_bands (response);
     return common_parse_bnd_response (response,
-                                      modem_is_2g, modem_is_3g, modem_is_4g,
-                                      modem_alternate_3g_bands,
-                                      modem_has_hex_format_4g_bands,
-                                      *modem_ext_4g_bands,
+                                      config,
                                       LOAD_BANDS_TYPE_SUPPORTED,
                                       log_object,
                                       error);
@@ -937,7 +905,8 @@
         {"25.", MM_TELIT_MODEL_LE910C1},
         {"32.", MM_TELIT_MODEL_LM960},
         {"38.", MM_TELIT_MODEL_FN980},
-        {"40.", MM_TELIT_MODEL_LN920}
+        {"40.", MM_TELIT_MODEL_LN920},
+        {"45.00", MM_TELIT_MODEL_FN990},
     };
 
     if (!revision)
@@ -950,3 +919,49 @@
 
     return MM_TELIT_MODEL_DEFAULT;
 }
+
+static MMTelitSwRevCmp lm9x0_software_revision_cmp (const gchar *revision_a,
+                                                    const gchar *revision_b)
+{
+    /* LM940 and LM960 share the same software revision format
+     * WW.XY.ABC[-ZZZZ], where WW is the chipset code and C the major version.
+     * If WW is the same, the other values X, Y, A and B are also the same, so
+     * we can limit the comparison to C only. ZZZZ is the minor version (it
+     * includes if version is beta, test, or alpha), but at this stage we are
+     * not interested in compare it. */
+    guint chipset_a, chipset_b;
+    guint major_a, major_b;
+    guint x, y, a, b;
+
+    g_return_val_if_fail (
+        sscanf (revision_a, "%2u.%1u%1u.%1u%1u%1u", &chipset_a, &x, &y, &a, &b, &major_a) == 6,
+        MM_TELIT_SW_REV_CMP_INVALID);
+    g_return_val_if_fail (
+        sscanf (revision_b, "%2u.%1u%1u.%1u%1u%1u", &chipset_b, &x, &y, &a, &b, &major_b) == 6,
+        MM_TELIT_SW_REV_CMP_INVALID);
+
+    if (chipset_a != chipset_b)
+        return MM_TELIT_SW_REV_CMP_INVALID;
+    if (major_a > major_b)
+        return MM_TELIT_SW_REV_CMP_NEWER;
+    if (major_a < major_b)
+        return MM_TELIT_SW_REV_CMP_OLDER;
+    return MM_TELIT_SW_REV_CMP_EQUAL;
+}
+
+MMTelitSwRevCmp mm_telit_software_revision_cmp (const gchar *revision_a,
+                                                const gchar *revision_b)
+{
+    MMTelitModel model_a;
+    MMTelitModel model_b;
+
+    model_a = mm_telit_model_from_revision (revision_a);
+    model_b = mm_telit_model_from_revision (revision_b);
+
+    if ((model_a == MM_TELIT_MODEL_LM940 || model_a == MM_TELIT_MODEL_LM960) &&
+        (model_b == MM_TELIT_MODEL_LM940 || model_b == MM_TELIT_MODEL_LM960)) {
+        return lm9x0_software_revision_cmp (revision_a, revision_b);
+    }
+
+    return MM_TELIT_SW_REV_CMP_UNSUPPORTED;
+}
diff --git a/plugins/telit/mm-modem-helpers-telit.h b/plugins/telit/mm-modem-helpers-telit.h
index 97eaf47..3844976 100644
--- a/plugins/telit/mm-modem-helpers-telit.h
+++ b/plugins/telit/mm-modem-helpers-telit.h
@@ -26,34 +26,38 @@
     MM_TELIT_MODEL_LM940,
     MM_TELIT_MODEL_LM960,
     MM_TELIT_MODEL_LN920,
+    MM_TELIT_MODEL_FN990,
 } MMTelitModel;
 
+typedef struct {
+    gboolean      modem_is_2g;
+    gboolean      modem_is_3g;
+    gboolean      modem_is_4g;
+    gboolean      modem_alternate_3g_bands;
+    gboolean      modem_has_hex_format_4g_bands;
+    gboolean      modem_ext_4g_bands;
+} MMTelitBNDParseConfig;
+
+typedef enum {
+    MM_TELIT_SW_REV_CMP_INVALID,
+    MM_TELIT_SW_REV_CMP_UNSUPPORTED,
+    MM_TELIT_SW_REV_CMP_OLDER,
+    MM_TELIT_SW_REV_CMP_EQUAL,
+    MM_TELIT_SW_REV_CMP_NEWER,
+} MMTelitSwRevCmp;
+
 /* #BND response parsers and request builder */
-GArray *mm_telit_parse_bnd_query_response (const gchar  *response,
-                                           gboolean      modem_is_2g,
-                                           gboolean      modem_is_3g,
-                                           gboolean      modem_is_4g,
-                                           gboolean      modem_alternate_3g_bands,
-                                           gboolean      modem_has_hex_format_4g_bands,
-                                           gboolean      modem_ext_4g_bands,
-                                           gpointer      log_object,
-                                           GError      **error);
-GArray *mm_telit_parse_bnd_test_response  (const gchar  *response,
-                                           gboolean      modem_is_2g,
-                                           gboolean      modem_is_3g,
-                                           gboolean      modem_is_4g,
-                                           gboolean      modem_alternate_3g_bands,
-                                           gboolean      modem_has_hex_format_4g_bands,
-                                           gboolean     *modem_ext_4g_bands,
-                                           gpointer      log_object,
-                                           GError      **error);
-gchar  *mm_telit_build_bnd_request        (GArray       *bands_array,
-                                           gboolean      modem_is_2g,
-                                           gboolean      modem_is_3g,
-                                           gboolean      modem_is_4g,
-                                           gboolean      modem_alternate_3g_bands,
-                                           gboolean      modem_ext_4g_bands,
-                                           GError      **error);
+GArray *mm_telit_parse_bnd_query_response (const gchar            *response,
+                                           MMTelitBNDParseConfig  *config,
+                                           gpointer                log_object,
+                                           GError                **error);
+GArray *mm_telit_parse_bnd_test_response  (const gchar            *response,
+                                           MMTelitBNDParseConfig  *config,
+                                           gpointer                log_object,
+                                           GError                **error);
+gchar  *mm_telit_build_bnd_request        (GArray                 *bands_array,
+                                           MMTelitBNDParseConfig  *config,
+                                           GError                **error);
 
 /* #QSS? response parser */
 typedef enum { /*< underscore_name=mm_telit_qss_status >*/
@@ -80,4 +84,7 @@
 
 MMTelitModel mm_telit_model_from_revision (const gchar *revision);
 
+MMTelitSwRevCmp mm_telit_software_revision_cmp (const gchar *reference,
+                                                const gchar *revision);
+
 #endif  /* MM_MODEM_HELPERS_TELIT_H */
diff --git a/plugins/telit/mm-shared-telit.c b/plugins/telit/mm-shared-telit.c
index e5f4413..09c122b 100644
--- a/plugins/telit/mm-shared-telit.c
+++ b/plugins/telit/mm-shared-telit.c
@@ -34,6 +34,8 @@
 /*****************************************************************************/
 /* Private data context */
 
+#define TELIT_LM940_EXT_LTE_BND_SW_REVISION "24.01.516"
+
 #define PRIVATE_TAG "shared-telit-private-tag"
 static GQuark private_quark;
 
@@ -58,10 +60,22 @@
 }
 
 static gboolean
-is_bnd_4g_format_hex (MMBaseModem *self,
-                      const gchar *revision)
+has_alternate_3g_bands (const gchar *revision)
 {
-    MMTelitModel model;    
+    MMTelitModel model;
+
+    model = mm_telit_model_from_revision (revision);
+    return (model == MM_TELIT_MODEL_FN980 ||
+            model == MM_TELIT_MODEL_FN990 ||
+            model == MM_TELIT_MODEL_LM940 ||
+            model == MM_TELIT_MODEL_LM960 ||
+            model == MM_TELIT_MODEL_LN920);
+}
+
+static gboolean
+is_bnd_4g_format_hex (const gchar *revision)
+{
+    MMTelitModel model;
 
     model = mm_telit_model_from_revision (revision);
 
@@ -72,22 +86,19 @@
             model == MM_TELIT_MODEL_LN920);
 }
 
-static void
-initialize_alternate_3g_band (MMSharedTelit *self,
-                              Private       *priv)
+static gboolean
+has_extended_4g_bands (const gchar *revision)
 {
-    MMPort         *primary;
-    MMKernelDevice *port;
+    MMTelitModel model;
 
-    primary = MM_PORT (mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)));
-    if (primary) {
-        port = mm_port_peek_kernel_device (primary);
+    model = mm_telit_model_from_revision (revision);
+    if (model == MM_TELIT_MODEL_LM940)
+        return mm_telit_software_revision_cmp (revision, TELIT_LM940_EXT_LTE_BND_SW_REVISION) >= MM_TELIT_SW_REV_CMP_EQUAL;
 
-        /* Lookup for the tag specifying that we're using the alternate 3G band mapping */
-        priv->alternate_3g_bands = mm_kernel_device_get_global_property_as_boolean (port, "ID_MM_TELIT_BND_ALTERNATE");
-        if (priv->alternate_3g_bands)
-            mm_obj_dbg (self, "telit modem using alternate 3G band mask setup");
-    }
+    return (model == MM_TELIT_MODEL_FN980 ||
+            model == MM_TELIT_MODEL_FN990 ||
+            model == MM_TELIT_MODEL_LM960 ||
+            model == MM_TELIT_MODEL_LN920);
 }
 
 static Private *
@@ -101,8 +112,6 @@
     priv = g_object_get_qdata (G_OBJECT (self), private_quark);
     if (!priv) {
         priv = g_slice_new0 (Private);
-        initialize_alternate_3g_band (self, priv);
-        /* ext_4g_bands field is initialized inside #BND=? response handler */
 
         if (MM_SHARED_TELIT_GET_INTERFACE (self)->peek_parent_modem_interface)
             priv->iface_modem_parent = MM_SHARED_TELIT_GET_INTERFACE (self)->peek_parent_modem_interface (self);
@@ -132,6 +141,23 @@
     priv = get_private (MM_SHARED_TELIT (self));
     g_clear_pointer (&priv->software_package_version, g_free);
     priv->software_package_version = g_strdup (revision);
+    priv->alternate_3g_bands = has_alternate_3g_bands (revision);
+    priv->ext_4g_bands = has_extended_4g_bands (revision);
+}
+
+void
+mm_shared_telit_get_bnd_parse_config (MMIfaceModem *self, MMTelitBNDParseConfig *config)
+{
+    Private *priv;
+
+    priv = get_private (MM_SHARED_TELIT (self));
+
+    config->modem_is_2g = mm_iface_modem_is_2g (self);
+    config->modem_is_3g = mm_iface_modem_is_3g (self);
+    config->modem_is_4g = mm_iface_modem_is_4g (self);
+    config->modem_alternate_3g_bands = priv->alternate_3g_bands;
+    config->modem_has_hex_format_4g_bands = is_bnd_4g_format_hex (priv->software_package_version);
+    config->modem_ext_4g_bands = priv->ext_4g_bands;
 }
 
 /*****************************************************************************/
@@ -241,16 +267,11 @@
         g_task_return_error (task, error);
     else {
         GArray *bands;
+        MMTelitBNDParseConfig config;
 
-        bands = mm_telit_parse_bnd_test_response (response,
-                                                  mm_iface_modem_is_2g (MM_IFACE_MODEM (self)),
-                                                  mm_iface_modem_is_3g (MM_IFACE_MODEM (self)),
-                                                  mm_iface_modem_is_4g (MM_IFACE_MODEM (self)),
-                                                  priv->alternate_3g_bands,
-                                                  is_bnd_4g_format_hex (self, priv->software_package_version),
-                                                  &priv->ext_4g_bands,
-                                                  self,
-                                                  &error);
+        mm_shared_telit_get_bnd_parse_config (MM_IFACE_MODEM (self), &config);
+
+        bands = mm_telit_parse_bnd_test_response (response, &config, self, &error);
         if (!bands)
             g_task_return_error (task, error);
         else {
@@ -338,25 +359,17 @@
 {
     const gchar *response;
     GError      *error = NULL;
-    Private     *priv;
-
-    priv = get_private (MM_SHARED_TELIT (self));
 
     response = mm_base_modem_at_command_finish (self, res, &error);
     if (!response)
         g_task_return_error (task, error);
     else {
         GArray *bands;
+        MMTelitBNDParseConfig config;
 
-        bands = mm_telit_parse_bnd_query_response (response,
-                                                   mm_iface_modem_is_2g (MM_IFACE_MODEM (self)),
-                                                   mm_iface_modem_is_3g (MM_IFACE_MODEM (self)),
-                                                   mm_iface_modem_is_4g (MM_IFACE_MODEM (self)),
-                                                   priv->alternate_3g_bands,
-                                                   is_bnd_4g_format_hex (self, priv->software_package_version),
-                                                   priv->ext_4g_bands,
-                                                   self,
-                                                   &error);
+        mm_shared_telit_get_bnd_parse_config (MM_IFACE_MODEM (self), &config);
+
+        bands = mm_telit_parse_bnd_query_response (response, &config, self, &error);
         if (!bands)
             g_task_return_error (task, error);
         else
@@ -454,15 +467,16 @@
 {
     GError  *error = NULL;
     gchar   *cmd;
-    Private *priv;
     GArray  *bands_array;
-
-    priv = get_private (MM_SHARED_TELIT (self));
+    MMTelitBNDParseConfig config;
 
     bands_array = g_task_get_task_data (task);
     g_assert (bands_array);
 
     if (bands_array->len == 1 && g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
+        Private *priv;
+
+        priv = get_private (MM_SHARED_TELIT (self));
         if (!priv->supported_bands) {
             g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
                                      "Couldn't build ANY band settings: unknown supported bands");
@@ -472,13 +486,8 @@
         bands_array = priv->supported_bands;
     }
 
-    cmd = mm_telit_build_bnd_request (bands_array,
-                                      mm_iface_modem_is_2g (self),
-                                      mm_iface_modem_is_3g (self),
-                                      mm_iface_modem_is_4g (self),
-                                      priv->alternate_3g_bands,
-                                      priv->ext_4g_bands,
-                                      &error);
+    mm_shared_telit_get_bnd_parse_config (self, &config);
+    cmd = mm_telit_build_bnd_request (bands_array, &config, &error);
     if (!cmd) {
         g_task_return_error (task, error);
         g_object_unref (task);
@@ -783,3 +792,4 @@
 
     return shared_telit_type;
 }
+
diff --git a/plugins/telit/mm-shared-telit.h b/plugins/telit/mm-shared-telit.h
index 6dfcb63..bf093ea 100644
--- a/plugins/telit/mm-shared-telit.h
+++ b/plugins/telit/mm-shared-telit.h
@@ -25,6 +25,7 @@
 #include "mm-broadband-modem.h"
 #include "mm-iface-modem.h"
 #include "mm-iface-modem-location.h"
+#include "mm-modem-helpers-telit.h"
 
 #define MM_TYPE_SHARED_TELIT                   (mm_shared_telit_get_type ())
 #define MM_SHARED_TELIT(obj)                   (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_TELIT, MMSharedTelit))
@@ -101,4 +102,6 @@
 void      mm_shared_telit_store_revision                (MMSharedTelit *self,
                                                          const gchar   *revision);
 
+void      mm_shared_telit_get_bnd_parse_config          (MMIfaceModem          *self,
+                                                         MMTelitBNDParseConfig *config);
 #endif  /* MM_SHARED_TELIT_H */
diff --git a/plugins/telit/tests/test-mm-modem-helpers-telit.c b/plugins/telit/tests/test-mm-modem-helpers-telit.c
index 1b96d08..e14ba6b 100644
--- a/plugins/telit/tests/test-mm-modem-helpers-telit.c
+++ b/plugins/telit/tests/test-mm-modem-helpers-telit.c
@@ -35,26 +35,21 @@
 
 typedef struct {
     const gchar *response;
-    gboolean     modem_is_2g;
-    gboolean     modem_is_3g;
-    gboolean     modem_is_4g;
-    gboolean     modem_alternate_3g_bands;
-    gboolean     modem_has_4g_bands_hex_format;
-    gboolean     modem_ext_4g_bands;
+    MMTelitBNDParseConfig config;
     guint        mm_bands_len;
     MMModemBand  mm_bands [MAX_BANDS_LIST_LEN];
 } BndResponseTest;
 
 static BndResponseTest supported_band_mapping_tests [] = {
     {
-        "#BND: (0-3)", TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, 4,
+        "#BND: (0-3)", {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE}, 4,
         { MM_MODEM_BAND_EGSM,
           MM_MODEM_BAND_DCS,
           MM_MODEM_BAND_PCS,
           MM_MODEM_BAND_G850 }
     },
     {
-        "#BND: (0-3),(0,2,5,6)", TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, 7,
+        "#BND: (0-3),(0,2,5,6)", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 7,
         { MM_MODEM_BAND_EGSM,
           MM_MODEM_BAND_DCS,
           MM_MODEM_BAND_PCS,
@@ -64,7 +59,7 @@
           MM_MODEM_BAND_UTRAN_8 }
     },
     {
-        "#BND: (0,3),(0,2,5,6)", TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, 7,
+        "#BND: (0,3),(0,2,5,6)", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 7,
         { MM_MODEM_BAND_EGSM,
           MM_MODEM_BAND_DCS,
           MM_MODEM_BAND_PCS,
@@ -74,7 +69,7 @@
           MM_MODEM_BAND_UTRAN_8 }
     },
     {
-        "#BND: (0,2),(0,2,5,6)", TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, 6,
+        "#BND: (0,2),(0,2,5,6)", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 6,
         { MM_MODEM_BAND_EGSM,
           MM_MODEM_BAND_DCS,
           MM_MODEM_BAND_G850,
@@ -83,7 +78,7 @@
           MM_MODEM_BAND_UTRAN_8 }
     },
     {
-        "#BND: (0,2),(0-4,5,6)", TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, 7,
+        "#BND: (0,2),(0-4,5,6)", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 7,
         { MM_MODEM_BAND_EGSM,
           MM_MODEM_BAND_DCS,
           MM_MODEM_BAND_G850,
@@ -93,7 +88,7 @@
           MM_MODEM_BAND_UTRAN_8 }
     },
     {
-        "#BND: (0-3),(0,2,5,6),(1-1)", TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, 8,
+        "#BND: (0-3),(0,2,5,6),(1-1)", {TRUE, TRUE, TRUE, FALSE, FALSE, FALSE}, 8,
         { MM_MODEM_BAND_EGSM,
           MM_MODEM_BAND_DCS,
           MM_MODEM_BAND_PCS,
@@ -104,7 +99,7 @@
           MM_MODEM_BAND_EUTRAN_1 }
     },
     {
-        "#BND: (0),(0),(1-3)", TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, 5,
+        "#BND: (0),(0),(1-3)", {TRUE, TRUE, TRUE, FALSE, FALSE, FALSE}, 5,
         { MM_MODEM_BAND_EGSM,
           MM_MODEM_BAND_DCS,
           MM_MODEM_BAND_UTRAN_1,
@@ -112,13 +107,13 @@
           MM_MODEM_BAND_EUTRAN_2 }
     },
     {
-        "#BND: (0),(0),(1-3)", FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, 2,
+        "#BND: (0),(0),(1-3)", {FALSE, FALSE, TRUE, FALSE, FALSE, FALSE}, 2,
         { MM_MODEM_BAND_EUTRAN_1,
           MM_MODEM_BAND_EUTRAN_2 }
     },
     /* 3G alternate band settings: default */
     {
-        "#BND: (0),(0,2,5,6,12,25)", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, 5,
+        "#BND: (0),(0,2,5,6,12,25)", {FALSE, TRUE, FALSE, FALSE, FALSE, FALSE}, 5,
         { MM_MODEM_BAND_UTRAN_1,
           MM_MODEM_BAND_UTRAN_5,
           MM_MODEM_BAND_UTRAN_8,
@@ -127,7 +122,7 @@
     },
     /* 3G alternate band settings: alternate */
     {
-        "#BND: (0),(0,2,5,6,12,13)", FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, 4,
+        "#BND: (0),(0,2,5,6,12,13)", {FALSE, TRUE, FALSE, TRUE, FALSE, FALSE}, 4,
         { MM_MODEM_BAND_UTRAN_1,
           MM_MODEM_BAND_UTRAN_3,
           MM_MODEM_BAND_UTRAN_5,
@@ -137,7 +132,7 @@
      * 168695967: 0xA0E189F: 0000 1010 0000 1110 0001 1000 1001 1111
      */
     {
-        "#BND: (0-5),(0),(1-168695967)", TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, 17,
+        "#BND: (0-5),(0),(1-168695967)", {TRUE, FALSE, TRUE, FALSE, FALSE, FALSE}, 17,
         { MM_MODEM_BAND_EGSM,
           MM_MODEM_BAND_DCS,
           MM_MODEM_BAND_PCS,
@@ -158,7 +153,7 @@
     },
     /* 4G ext band settings: devices such as LN920 */
     {
-        "#BND: (0),(0),(1003100185A),(42)", FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, 13,
+        "#BND: (0),(0),(1003100185A),(42)", {FALSE, TRUE, TRUE, FALSE, TRUE, TRUE}, 13,
         { MM_MODEM_BAND_UTRAN_1,
           MM_MODEM_BAND_EUTRAN_2,
           MM_MODEM_BAND_EUTRAN_4,
@@ -175,7 +170,7 @@
     },
     /* 4G band in hex format: devices such as LE910C1-EUX */
     {
-        "#BND: (0),(0,5,6,13,15,23),(80800C5)", TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, 11,
+        "#BND: (0),(0,5,6,13,15,23),(80800C5)", {TRUE, TRUE, TRUE, FALSE, TRUE, FALSE}, 11,
         {
             MM_MODEM_BAND_EGSM,
             MM_MODEM_BAND_DCS,
@@ -199,20 +194,13 @@
     for (i = 0; i < G_N_ELEMENTS (supported_band_mapping_tests); i++) {
         GError *error = NULL;
         GArray *bands = NULL;
-        gboolean modem_ext_4g_bands;
 
         bands = mm_telit_parse_bnd_test_response (supported_band_mapping_tests[i].response,
-                                                  supported_band_mapping_tests[i].modem_is_2g,
-                                                  supported_band_mapping_tests[i].modem_is_3g,
-                                                  supported_band_mapping_tests[i].modem_is_4g,
-                                                  supported_band_mapping_tests[i].modem_alternate_3g_bands,
-                                                  supported_band_mapping_tests[i].modem_has_4g_bands_hex_format,
-                                                  &modem_ext_4g_bands,
+                                                  &supported_band_mapping_tests[i].config,
                                                   NULL,
                                                   &error);
         g_assert_no_error (error);
         g_assert (bands);
-        g_assert (supported_band_mapping_tests[i].modem_ext_4g_bands == modem_ext_4g_bands);
 
         mm_test_helpers_compare_bands (bands,
                                        supported_band_mapping_tests[i].mm_bands,
@@ -223,18 +211,18 @@
 
 static BndResponseTest current_band_mapping_tests [] = {
     {
-        "#BND: 0", TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, 2,
+        "#BND: 0", {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE}, 2,
         { MM_MODEM_BAND_EGSM,
           MM_MODEM_BAND_DCS }
     },
     {
-        "#BND: 0,5", TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, 3,
+        "#BND: 0,5", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 3,
         { MM_MODEM_BAND_EGSM,
           MM_MODEM_BAND_DCS,
           MM_MODEM_BAND_UTRAN_8 }
     },
     {
-        "#BND: 1,3", TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, 5,
+        "#BND: 1,3", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 5,
         { MM_MODEM_BAND_EGSM,
           MM_MODEM_BAND_PCS,
           MM_MODEM_BAND_UTRAN_1,
@@ -242,38 +230,38 @@
           MM_MODEM_BAND_UTRAN_5 }
     },
     {
-        "#BND: 2,7", TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, 3,
+        "#BND: 2,7", {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, 3,
         { MM_MODEM_BAND_DCS,
           MM_MODEM_BAND_G850,
           MM_MODEM_BAND_UTRAN_4 }
     },
     {
-        "#BND: 3,0,1", TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, 4,
+        "#BND: 3,0,1", {TRUE, TRUE, TRUE, FALSE, FALSE, FALSE}, 4,
         { MM_MODEM_BAND_PCS,
           MM_MODEM_BAND_G850,
           MM_MODEM_BAND_UTRAN_1,
           MM_MODEM_BAND_EUTRAN_1 }
     },
     {
-        "#BND: 0,0,3", TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, 4,
+        "#BND: 0,0,3", {TRUE, FALSE, TRUE, FALSE, FALSE, FALSE}, 4,
         { MM_MODEM_BAND_EGSM,
           MM_MODEM_BAND_DCS,
           MM_MODEM_BAND_EUTRAN_1,
           MM_MODEM_BAND_EUTRAN_2 }
     },
     {
-        "#BND: 0,0,3", FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, 2,
+        "#BND: 0,0,3", {FALSE, FALSE, TRUE, FALSE, FALSE, FALSE}, 2,
         { MM_MODEM_BAND_EUTRAN_1,
           MM_MODEM_BAND_EUTRAN_2 }
     },
     /* 3G alternate band settings: default */
     {
-        "#BND: 0,12", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, 1,
+        "#BND: 0,12", {FALSE, TRUE, FALSE, FALSE, FALSE, FALSE}, 1,
         { MM_MODEM_BAND_UTRAN_6 }
     },
     /* 3G alternate band settings: alternate */
     {
-        "#BND: 0,12", FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, 4,
+        "#BND: 0,12", {FALSE, TRUE, FALSE, TRUE, FALSE, FALSE}, 4,
         { MM_MODEM_BAND_UTRAN_1,
           MM_MODEM_BAND_UTRAN_3,
           MM_MODEM_BAND_UTRAN_5,
@@ -283,7 +271,7 @@
      * 168695967: 0xA0E189F: 0000 1010 0000 1110 0001 1000 1001 1111
      */
     {
-        "#BND: 5,0,168695967", TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, 17,
+        "#BND: 5,0,168695967", {TRUE, FALSE, TRUE, FALSE, FALSE, FALSE}, 17,
         { MM_MODEM_BAND_EGSM,
           MM_MODEM_BAND_DCS,
           MM_MODEM_BAND_PCS,
@@ -304,7 +292,7 @@
     },
     /* 4G ext band settings: devices such as LN920 */
     {
-        "#BND: 0,0,1003100185A,42", FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, 13,
+        "#BND: 0,0,1003100185A,42", {FALSE, TRUE, TRUE, FALSE, FALSE, TRUE}, 13,
         { MM_MODEM_BAND_UTRAN_1,
           MM_MODEM_BAND_EUTRAN_2,
           MM_MODEM_BAND_EUTRAN_4,
@@ -331,12 +319,7 @@
         GArray *bands = NULL;
 
         bands = mm_telit_parse_bnd_query_response (current_band_mapping_tests[i].response,
-                                                   current_band_mapping_tests[i].modem_is_2g,
-                                                   current_band_mapping_tests[i].modem_is_3g,
-                                                   current_band_mapping_tests[i].modem_is_4g,
-                                                   current_band_mapping_tests[i].modem_alternate_3g_bands,
-                                                   supported_band_mapping_tests[i].modem_has_4g_bands_hex_format,
-                                                   current_band_mapping_tests[i].modem_ext_4g_bands,
+                                                   &current_band_mapping_tests[i].config,
                                                    NULL,
                                                    &error);
         g_assert_no_error (error);
@@ -362,12 +345,15 @@
 {
     gchar  *cmd;
     GError *error = NULL;
+    MMTelitBNDParseConfig config = {
+        .modem_is_2g = modem_is_2g,
+        .modem_is_3g = modem_is_3g,
+        .modem_is_4g = modem_is_4g,
+        .modem_alternate_3g_bands = modem_alternate_3g_bands,
+        .modem_ext_4g_bands = modem_ext_4g_bands
+    };
 
-    cmd = mm_telit_build_bnd_request (bands_array,
-                                      modem_is_2g, modem_is_3g, modem_is_4g,
-                                      modem_alternate_3g_bands,
-                                      modem_ext_4g_bands,
-                                      &error);
+    cmd = mm_telit_build_bnd_request (bands_array, &config, &error);
     g_assert_no_error (error);
     g_assert_cmpstr (cmd, ==, expected_cmd);
     g_free (cmd);
@@ -386,11 +372,14 @@
 {
     gchar  *cmd;
     GError *error = NULL;
-
-    cmd = mm_telit_build_bnd_request (bands_array,
-                                      modem_is_2g, modem_is_3g, modem_is_4g,
-                                      FALSE, FALSE,
-                                      &error);
+    MMTelitBNDParseConfig config = {
+        .modem_is_2g = modem_is_2g,
+        .modem_is_3g = modem_is_3g,
+        .modem_is_4g = modem_is_4g,
+        .modem_alternate_3g_bands = FALSE,
+        .modem_ext_4g_bands = FALSE,
+    };
+    cmd = mm_telit_build_bnd_request (bands_array, &config, &error);
     g_assert_error (error, MM_CORE_ERROR, (gint)expected_error);
     g_assert (!cmd);
 }
@@ -657,6 +646,35 @@
     }
 }
 
+static void
+test_telit_compare_software_revision_string (void)
+{
+    struct {
+        const char *revision_a;
+        const char *revision_b;
+        MMTelitSwRevCmp expected;
+    } tt [] = {
+        {"24.01.514", "24.01.514", MM_TELIT_SW_REV_CMP_EQUAL},
+        {"24.01.514", "24.01.513", MM_TELIT_SW_REV_CMP_NEWER},
+        {"24.01.513", "24.01.514", MM_TELIT_SW_REV_CMP_OLDER},
+        {"32.00.013", "24.01.514", MM_TELIT_SW_REV_CMP_INVALID},
+        {"32.00.014", "32.00.014", MM_TELIT_SW_REV_CMP_EQUAL},
+        {"32.00.014", "32.00.013", MM_TELIT_SW_REV_CMP_NEWER},
+        {"32.00.013", "32.00.014", MM_TELIT_SW_REV_CMP_OLDER},
+        {"38.00.000", "38.00.000", MM_TELIT_SW_REV_CMP_UNSUPPORTED},
+        /* LM9x0 Minor version (e.g. beta, test, alpha) value is currently
+         * ignored because not required by any implemented feature. */
+        {"24.01.516-B123", "24.01.516-B134", MM_TELIT_SW_REV_CMP_EQUAL},
+    };
+    guint i;
+
+    for (i = 0; i < G_N_ELEMENTS (tt); i++) {
+        g_assert_cmpint (tt[i].expected,
+                         ==,
+                         mm_telit_software_revision_cmp (tt[i].revision_a, tt[i].revision_b));
+    }
+}
+
 /******************************************************************************/
 
 int main (int argc, char **argv)
@@ -672,5 +690,6 @@
     g_test_add_func ("/MM/telit/bands/current/set_bands/4g", test_telit_get_4g_bnd_flag);
     g_test_add_func ("/MM/telit/qss/query", test_telit_parse_qss_query);
     g_test_add_func ("/MM/telit/swpkv/parse_response", test_telit_parse_swpkgv_response);
+    g_test_add_func ("/MM/telit/revision/compare", test_telit_compare_software_revision_string);
     return g_test_run ();
 }
diff --git a/po/de.po b/po/de.po
index b626725..7877cb8 100644
--- a/po/de.po
+++ b/po/de.po
@@ -3,21 +3,22 @@
 # This file is distributed under the same license as the Modem Manager package.
 #
 # Mario Blättermann <mario.blaettermann@gmail.com>, 2013.
+# Jürgen Benvenuti <gastornis@posteo.org>, 2022.
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: ModemManager\n"
 "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
 "ModemManager/issues\n"
-"POT-Creation-Date: 2022-07-16 14:32+0200\n"
-"PO-Revision-Date: 2014-01-06 21:23+0100\n"
-"Last-Translator: Mario Blättermann <mario.blaettermann@gmail.com>\n"
-"Language-Team: German <debian-l10n-german@lists.debian.org>\n"
+"POT-Creation-Date: 2022-09-09 15:25+0000\n"
+"PO-Revision-Date: 2022-09-10 21:20+0200\n"
+"Last-Translator: Jürgen Benvenuti <gastornis@posteo.org>\n"
+"Language-Team: German <gnome-de@gnome.org>\n"
 "Language: de\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 1.5.4\n"
+"X-Generator: Poedit 3.1.1\n"
 
 #: data/org.freedesktop.ModemManager1.policy.in.in:13
 msgid "Control the Modem Manager daemon"
@@ -54,7 +55,6 @@
 msgstr "Textnachrichten senden, speichern, bearbeiten und löschen"
 
 #: data/org.freedesktop.ModemManager1.policy.in.in:41
-#, fuzzy
 msgid ""
 "System policy prevents sending or manipulating this device's text messages."
 msgstr ""
@@ -64,34 +64,32 @@
 #: data/org.freedesktop.ModemManager1.policy.in.in:49
 msgid "Accept incoming voice calls or start outgoing voice calls."
 msgstr ""
+"Eingehende Sprachanrufe annehmen oder ausgehende Sprachanrufe beginnen."
 
 #: data/org.freedesktop.ModemManager1.policy.in.in:50
-#, fuzzy
 msgid "System policy prevents voice calls."
-msgstr "Die Systemrichtlinien verhindern die Steuerung von ModemManager."
+msgstr "Die Systemrichtlinien verhindern Sprachanrufe."
 
 #: data/org.freedesktop.ModemManager1.policy.in.in:58
 msgid "Query network time and timezone information"
-msgstr ""
+msgstr "Netzwerkzeit und Zeitzonen-Information abfragen"
 
 #: data/org.freedesktop.ModemManager1.policy.in.in:59
-#, fuzzy
 msgid "System policy prevents querying network time information."
 msgstr ""
-"Die Systemrichtlinien verhindern die Abfrage der Netzwerkinformationen und -"
-"dienste."
+"Die Systemrichtlinien verhindern die Abfrage der Netzwerkzeit-Information."
 
 #: data/org.freedesktop.ModemManager1.policy.in.in:67
 msgid "Enable and view geographic location and positioning information"
 msgstr ""
-"Informationen zum geografischen Standort und Positionierung aktivieren und "
-"anzeigen"
+"Informationen zum geografischen Standort und zur Positionierung aktivieren "
+"und anzeigen"
 
 #: data/org.freedesktop.ModemManager1.policy.in.in:68
 msgid ""
 "System policy prevents enabling or viewing geographic location information."
 msgstr ""
-"Die Systemrichtlinien verhindern das Aktivieren oder Ändern der "
+"Die Systemrichtlinien verhindern das Aktivieren oder Anzeigen der "
 "Informationen zum geografischen Standort."
 
 #: data/org.freedesktop.ModemManager1.policy.in.in:76
@@ -118,4 +116,4 @@
 
 #: src/mm-sleep-monitor-systemd.c:125
 msgid "ModemManager needs to reset devices"
-msgstr ""
+msgstr "ModemManager muss Geräte zurücksetzen"
diff --git a/src/mm-base-sim.c b/src/mm-base-sim.c
index be25e86..5fb83dc 100644
--- a/src/mm-base-sim.c
+++ b/src/mm-base-sim.c
@@ -12,7 +12,8 @@
  *
  * Copyright (C) 2008 - 2009 Novell, Inc.
  * Copyright (C) 2009 - 2011 Red Hat, Inc.
- * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2011 - 2022 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2011 - 2022 Google, Inc.
  */
 
 #include <config.h>
@@ -2287,6 +2288,90 @@
 }
 
 /*****************************************************************************/
+/* GID1 and GID2 */
+
+static GByteArray *
+parse_gid (const gchar  *response,
+           GError      **error)
+{
+    guint             sw1 = 0;
+    guint             sw2 = 0;
+    g_autofree gchar *hex = NULL;
+
+    if (!mm_3gpp_parse_crsm_response (response, &sw1, &sw2, &hex, error))
+        return NULL;
+
+    if ((sw1 == 0x90 && sw2 == 0x00) ||
+        (sw1 == 0x91) ||
+        (sw1 == 0x92) ||
+        (sw1 == 0x9f)) {
+        guint8 *bin = NULL;
+        gsize   binlen = 0;
+
+        /* Convert hex string to binary */
+        bin = mm_utils_hexstr2bin (hex, -1, &binlen, error);
+        if (!bin) {
+            g_prefix_error (error, "SIM returned malformed response '%s': ", hex);
+            return NULL;
+        }
+
+        /* return as bytearray */
+        return g_byte_array_new_take (bin, binlen);
+    }
+
+    g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+                 "SIM failed to handle CRSM request (sw1 %d sw2 %d)", sw1, sw2);
+    return NULL;
+}
+
+static GByteArray *
+common_load_gid_finish (MMBaseSim     *self,
+                        GAsyncResult  *res,
+                        GError       **error)
+{
+    g_autofree gchar *result = NULL;
+
+    result = g_task_propagate_pointer (G_TASK (res), error);
+    if (!result)
+        return NULL;
+
+    return parse_gid (result, error);
+}
+
+STR_REPLY_READY_FN (load_gid1)
+STR_REPLY_READY_FN (load_gid2)
+
+static void
+load_gid1 (MMBaseSim           *self,
+           GAsyncReadyCallback  callback,
+           gpointer             user_data)
+{
+    /* READ BINARY of EFgid1 */
+    mm_base_modem_at_command (
+        self->priv->modem,
+        "+CRSM=176,28478,0,0,15",
+        10,
+        FALSE,
+        (GAsyncReadyCallback)load_gid1_command_ready,
+        g_task_new (self, NULL, callback, user_data));
+}
+
+static void
+load_gid2 (MMBaseSim           *self,
+           GAsyncReadyCallback  callback,
+           gpointer             user_data)
+{
+    /* READ BINARY of EFgid2 */
+    mm_base_modem_at_command (
+        self->priv->modem,
+        "+CRSM=176,28479,0,0,15",
+        10,
+        FALSE,
+        (GAsyncReadyCallback)load_gid2_command_ready,
+        g_task_new (self, NULL, callback, user_data));
+}
+
+/*****************************************************************************/
 
 MMBaseSim *
 mm_base_sim_new_initialized (MMBaseModem *modem,
@@ -3080,6 +3165,10 @@
     klass->load_emergency_numbers_finish = load_emergency_numbers_finish;
     klass->load_preferred_networks = load_preferred_networks;
     klass->load_preferred_networks_finish = load_preferred_networks_finish;
+    klass->load_gid1 = load_gid1;
+    klass->load_gid1_finish = common_load_gid_finish;
+    klass->load_gid2 = load_gid2;
+    klass->load_gid2_finish = common_load_gid_finish;
     klass->set_preferred_networks = set_preferred_networks;
     klass->set_preferred_networks_finish = set_preferred_networks_finish;
     klass->send_pin = send_pin;
diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c
index 33ba2c5..644aaee 100644
--- a/src/mm-broadband-modem-mbim.c
+++ b/src/mm-broadband-modem-mbim.c
@@ -2945,7 +2945,6 @@
     QMI_SERVICE_PDS,
     QMI_SERVICE_LOC,
     QMI_SERVICE_PDC,
-    QMI_SERVICE_FOX,
     QMI_SERVICE_UIM,
 };
 
@@ -8235,9 +8234,18 @@
 static gboolean
 sar_enable_finish (MMIfaceModemSar *self,
                    GAsyncResult    *res,
+                   guint           *out_sar_power_level,
                    GError         **error)
 {
-     return g_task_propagate_boolean (G_TASK (res), error);
+    guint level;
+
+    if (!g_task_propagate_boolean (G_TASK (res), error))
+        return FALSE;
+
+    level = GPOINTER_TO_UINT (g_task_get_task_data (G_TASK (res)));
+    if (out_sar_power_level)
+        *out_sar_power_level = level;
+    return TRUE;
 }
 
 static void
@@ -8287,6 +8295,8 @@
 #endif
     config_state->backoff_index = mm_iface_modem_sar_get_power_level (_self);
 
+    g_task_set_task_data (task, GUINT_TO_POINTER (config_state->backoff_index), NULL);
+
     message = mbim_message_ms_sar_config_set_new (MBIM_SAR_CONTROL_MODE_OS,
                                                   enable ? MBIM_SAR_BACKOFF_STATE_ENABLED : MBIM_SAR_BACKOFF_STATE_DISABLED,
                                                   1, (const MbimSarConfigState **)&config_state, NULL);
@@ -8342,17 +8352,6 @@
     if (!peek_device (self, &device, callback, user_data))
         return;
 
-    if (!mm_iface_modem_get_sar_state (_self)) {
-        g_task_report_new_error (self,
-                                 callback,
-                                 user_data,
-                                 sar_set_power_level,
-                                 MM_CORE_ERROR,
-                                 MM_CORE_ERROR_WRONG_STATE,
-                                 "Couldn't set power level of SAR, because the SAR is disabled");
-        return;
-    }
-
     /*
      * the value 0xFFFFFFFF means all antennas
      * the backoff index set to the input power level
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c
index 0eb03b3..6281c16 100644
--- a/src/mm-broadband-modem-qmi.c
+++ b/src/mm-broadband-modem-qmi.c
@@ -62,8 +62,8 @@
 static void iface_modem_location_init (MMIfaceModemLocation *iface);
 static void iface_modem_oma_init (MMIfaceModemOma *iface);
 static void iface_modem_firmware_init (MMIfaceModemFirmware *iface);
-static void iface_modem_signal_init (MMIfaceModemSignal *iface);
 static void iface_modem_sar_init (MMIfaceModemSar *iface);
+static void iface_modem_signal_init (MMIfaceModemSignal *iface);
 static void shared_qmi_init (MMSharedQmi *iface);
 
 static MMIfaceModemLocation  *iface_modem_location_parent;
@@ -79,8 +79,8 @@
                         G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init)
                         G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init)
                         G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
-                        G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIGNAL, iface_modem_signal_init)
                         G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SAR, iface_modem_sar_init)
+                        G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIGNAL, iface_modem_signal_init)
                         G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_OMA, iface_modem_oma_init)
                         G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init)
                         G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_QMI, shared_qmi_init))
@@ -183,9 +183,6 @@
     gboolean profile_manager_unsolicited_events_setup;
     guint refresh_indication_id;
 
-    /* SAR Operation */
-    gboolean is_sar_supported;
-
     /* PS registration helpers when using NAS System Info and DSD
      * (not applicable when using NAS Serving System) */
     gboolean dsd_supported;
@@ -8302,24 +8299,26 @@
 
 
 /*****************************************************************************/
-
 /* Check support (SAR interface) */
 
+/* SAR level 0 is assumed DISABLED, and any other level is assumed ENABLED */
+#define QMI_SAR_ENABLE_POWER_INDEX   QMI_SAR_RF_STATE_1
+#define QMI_SAR_DISABLED_POWER_INDEX QMI_SAR_RF_STATE_0
+
 static gboolean
-sar_check_support_finish (MMIfaceModemSar *self,
-                          GAsyncResult    *res,
-                          GError         **error)
+sar_check_support_finish (MMIfaceModemSar  *self,
+                          GAsyncResult     *res,
+                          GError          **error)
 {
     return g_task_propagate_boolean (G_TASK (res), error);
 }
 
 static void
-sar_check_support (MMIfaceModemSar    *_self,
-                   GAsyncReadyCallback callback,
-                   gpointer            user_data)
+sar_check_support (MMIfaceModemSar     *self,
+                   GAsyncReadyCallback  callback,
+                   gpointer             user_data)
 {
-    MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
-    GTask               *task;
+    GTask *task;
 
     task = g_task_new (self, NULL, callback, user_data);
 
@@ -8328,15 +8327,19 @@
                                     QMI_SERVICE_SAR,
                                     MM_PORT_QMI_FLAG_DEFAULT,
                                     NULL)) {
-        self->priv->is_sar_supported = FALSE;
-    } else
-        self->priv->is_sar_supported = TRUE;
+        mm_obj_dbg (self, "SAR capabilities not supported");
+        g_task_return_boolean (task, FALSE);
+    } else {
+        mm_obj_dbg (self, "SAR capabilities supported");
+        g_task_return_boolean (task, TRUE);
+    }
 
-    mm_obj_dbg (self, "SAR capabilities %s", self->priv->is_sar_supported ? "supported" : "not supported");
-    g_task_return_boolean (task, self->priv->is_sar_supported);
     g_object_unref (task);
 }
 
+/*****************************************************************************/
+/* Load SAR state (SAR interface) */
+
 static gboolean
 sar_load_state_finish (MMIfaceModemSar *self,
                        GAsyncResult    *res,
@@ -8358,33 +8361,69 @@
 }
 
 static void
-sar_load_state (MMIfaceModemSar *_self,
-                GAsyncReadyCallback callback,
-                gpointer user_data)
+sar_load_state_ready (QmiClientSar *client,
+                      GAsyncResult *res,
+                      GTask        *task)
 {
-    MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
-    GTask               *task;
+    g_autoptr(QmiMessageSarRfGetStateOutput)  output = NULL;
+    GError                                   *error = NULL;
+    QmiSarRfState                             rf_state;
 
-    task = g_task_new (self, NULL, callback, user_data);
+    output = qmi_client_sar_rf_get_state_finish (client, res, &error);
+    if (output &&
+        qmi_message_sar_rf_get_state_output_get_result (output, &error) &&
+        qmi_message_sar_rf_get_state_output_get_state (output, &rf_state, &error)) {
+        if (rf_state == QMI_SAR_DISABLED_POWER_INDEX)
+            g_task_return_boolean (task, FALSE);
+        else
+            g_task_return_boolean (task, TRUE);
+    } else
+        g_task_return_error (task, error);
 
-    mm_obj_dbg (self, "SAR enabled %s", self->priv->is_sar_supported ? "yes" : "no");
-    g_task_return_boolean (task, self->priv->is_sar_supported);
     g_object_unref (task);
 }
 
+static void
+sar_load_state (MMIfaceModemSar     *self,
+                GAsyncReadyCallback  callback,
+                gpointer             user_data)
+{
+    GTask     *task;
+    QmiClient *client = NULL;
+
+    if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+                                      QMI_SERVICE_SAR, &client,
+                                      callback, user_data))
+        return;
+
+    task = g_task_new (self, NULL, callback, user_data);
+
+    qmi_client_sar_rf_get_state (
+        QMI_CLIENT_SAR (client),
+        NULL,
+        5,
+        NULL,
+        (GAsyncReadyCallback)sar_load_state_ready,
+        task);
+}
+
+/*****************************************************************************/
+/* Load SAR power level (SAR interface) */
+
 static gboolean
-sar_load_power_level_finish (MMIfaceModemSar *self,
-                             GAsyncResult    *res,
-                             guint           *out_power_level,
+sar_load_power_level_finish (MMIfaceModemSar  *self,
+                             GAsyncResult     *res,
+                             guint            *out_power_level,
                              GError          **error)
 {
     gssize result;
 
-    result = g_task_propagate_int(G_TASK (res), error);
+    result = g_task_propagate_int (G_TASK (res), error);
     if (result < 0)
         return FALSE;
 
-    *out_power_level = (guint) result;
+    if (out_power_level)
+        *out_power_level = (guint) result;
     return TRUE;
 }
 
@@ -8393,29 +8432,28 @@
                             GAsyncResult *res,
                             GTask        *task)
 {
-    g_autoptr(QmiMessageSarRfGetStateOutput) output = NULL;
-    GError                                  *error = NULL;
-    QmiSarRfState                            rf_state;
+    g_autoptr(QmiMessageSarRfGetStateOutput)  output = NULL;
+    GError                                   *error = NULL;
+    QmiSarRfState                             rf_state;
 
     output = qmi_client_sar_rf_get_state_finish (client, res, &error);
     if (output &&
         qmi_message_sar_rf_get_state_output_get_result (output, &error) &&
-        qmi_message_sar_rf_get_state_output_get_state (output, &rf_state, &error)) {
-            g_task_return_int (task, rf_state);
-    } else
+        qmi_message_sar_rf_get_state_output_get_state (output, &rf_state, &error))
+        g_task_return_int (task, rf_state);
+    else
         g_task_return_error (task, error);
 
     g_object_unref (task);
 }
 
 static void
-sar_load_power_level (MMIfaceModemSar    *_self,
-                      GAsyncReadyCallback callback,
-                      gpointer            user_data)
+sar_load_power_level (MMIfaceModemSar     *self,
+                      GAsyncReadyCallback  callback,
+                      gpointer             user_data)
 {
-    MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
-    GTask               *task;
-    QmiClient           *client = NULL;
+    GTask     *task;
+    QmiClient *client = NULL;
 
     if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
                                       QMI_SERVICE_SAR, &client,
@@ -8433,12 +8471,24 @@
         task);
 }
 
+/*****************************************************************************/
+/* Enable/Disable SAR (SAR interface) */
+
 static gboolean
 sar_enable_finish (MMIfaceModemSar *self,
                    GAsyncResult    *res,
+                   guint           *out_sar_power_level,
                    GError         **error)
 {
-     return g_task_propagate_boolean (G_TASK (res), error);
+    QmiSarRfState level;
+
+    if (!g_task_propagate_boolean (G_TASK (res), error))
+        return FALSE;
+
+    level = GPOINTER_TO_UINT (g_task_get_task_data (G_TASK (res)));
+    if (out_sar_power_level)
+        *out_sar_power_level = level;
+    return TRUE;
 }
 
 static void
@@ -8446,31 +8496,28 @@
                   GAsyncResult *res,
                   GTask        *task)
 {
-    g_autoptr(QmiMessageSarRfSetStateOutput) output = NULL;
-    GError                                  *error = NULL;
+    g_autoptr(QmiMessageSarRfSetStateOutput)  output = NULL;
+    GError                                   *error = NULL;
 
     output = qmi_client_sar_rf_set_state_finish (client, res, &error);
-    if (output &&
-        qmi_message_sar_rf_set_state_output_get_result (output, &error)) {
-            g_task_return_boolean (task, TRUE);
-    } else
+    if (output && qmi_message_sar_rf_set_state_output_get_result (output, &error))
+        g_task_return_boolean (task, TRUE);
+    else
         g_task_return_error (task, error);
 
     g_object_unref (task);
 }
 
-
 static void
-sar_enable (MMIfaceModemSar    *_self,
-            gboolean            enable,
-            GAsyncReadyCallback callback,
-            gpointer            user_data)
+sar_enable (MMIfaceModemSar     *self,
+            gboolean             enable,
+            GAsyncReadyCallback  callback,
+            gpointer             user_data)
 {
-    MMBroadbandModemQmi                    *self = MM_BROADBAND_MODEM_QMI (_self);
-    g_autoptr(QmiMessageSarRfSetStateInput) input = NULL;
-    GTask                                  *task;
-    QmiClient                              *client = NULL;
-    GError                                 *error = NULL;
+    g_autoptr(QmiMessageSarRfSetStateInput)  input = NULL;
+    GTask                                   *task;
+    QmiClient                               *client = NULL;
+    QmiSarRfState                            level;
 
     if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
                                       QMI_SERVICE_SAR, &client,
@@ -8479,10 +8526,18 @@
 
     task = g_task_new (self, NULL, callback, user_data);
     input = qmi_message_sar_rf_set_state_input_new ();
-    qmi_message_sar_rf_set_state_input_set_state (
-                input,
-                0,
-                &error);
+
+    /* When enabling, try to set the last valid known power level used, instead
+     * of defaulting to level 1 */
+    if (enable) {
+        level = mm_iface_modem_sar_get_power_level (self);
+        if (level == QMI_SAR_DISABLED_POWER_INDEX)
+            level = QMI_SAR_ENABLE_POWER_INDEX;
+    } else
+        level = QMI_SAR_DISABLED_POWER_INDEX;
+
+    qmi_message_sar_rf_set_state_input_set_state (input, level, NULL);
+    g_task_set_task_data (task, GUINT_TO_POINTER (level), NULL);
 
     qmi_client_sar_rf_set_state (
         QMI_CLIENT_SAR (client),
@@ -8493,6 +8548,8 @@
         task);
 }
 
+/*****************************************************************************/
+/* Set SAR power level (SAR interface) */
 
 static gboolean
 sar_set_power_level_finish (MMIfaceModemSar *self,
@@ -8511,27 +8568,23 @@
     GError                                  *error = NULL;
 
     output = qmi_client_sar_rf_set_state_finish (client, res, &error);
-    if (output &&
-        qmi_message_sar_rf_set_state_output_get_result (output, &error)) {
-            g_task_return_boolean (task, TRUE);
-    } else
+    if (output && qmi_message_sar_rf_set_state_output_get_result (output, &error))
+        g_task_return_boolean (task, TRUE);
+    else
         g_task_return_error (task, error);
 
     g_object_unref (task);
 }
 
-
 static void
-sar_set_power_level (MMIfaceModemSar    *_self,
+sar_set_power_level (MMIfaceModemSar    *self,
                      guint               power_level,
                      GAsyncReadyCallback callback,
                      gpointer            user_data)
 {
-    MMBroadbandModemQmi                    *self = MM_BROADBAND_MODEM_QMI (_self);
-    g_autoptr(QmiMessageSarRfSetStateInput) input = NULL;
-    GTask                                  *task;
-    QmiClient                              *client = NULL;
-    GError                                 *error = NULL;
+    g_autoptr(QmiMessageSarRfSetStateInput)  input = NULL;
+    GTask                                   *task;
+    QmiClient                               *client = NULL;
 
     if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
                                       QMI_SERVICE_SAR, &client,
@@ -8539,11 +8592,17 @@
         return;
 
     task = g_task_new (self, NULL, callback, user_data);
+
+    if (power_level == QMI_SAR_DISABLED_POWER_INDEX) {
+        g_task_return_new_error (task,
+                                 MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+                                 "Unsupported power level");
+        g_object_unref (task);
+        return;
+    }
+
     input = qmi_message_sar_rf_set_state_input_new ();
-    qmi_message_sar_rf_set_state_input_set_state (
-                input,
-                power_level,
-                &error);
+    qmi_message_sar_rf_set_state_input_set_state (input, power_level, NULL);
     qmi_client_sar_rf_set_state (
         QMI_CLIENT_SAR (client),
         input,
@@ -13109,8 +13168,8 @@
     QMI_SERVICE_LOC,
     QMI_SERVICE_PDC,
     QMI_SERVICE_VOICE,
-    QMI_SERVICE_SAR,
     QMI_SERVICE_DSD,
+    QMI_SERVICE_SAR,
 };
 
 typedef struct {
@@ -13825,15 +13884,6 @@
 }
 
 static void
-iface_modem_signal_init (MMIfaceModemSignal *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;
-}
-
-static void
 iface_modem_sar_init (MMIfaceModemSar *iface)
 {
     iface->check_support = sar_check_support;
@@ -13849,6 +13899,15 @@
 }
 
 static void
+iface_modem_signal_init (MMIfaceModemSignal *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;
+}
+
+static void
 iface_modem_oma_init (MMIfaceModemOma *iface)
 {
     iface->check_support = oma_check_support;
diff --git a/src/mm-iface-modem-sar.c b/src/mm-iface-modem-sar.c
index 2e92af1..63105d6 100644
--- a/src/mm-iface-modem-sar.c
+++ b/src/mm-iface-modem-sar.c
@@ -35,24 +35,6 @@
 {
 }
 
-gboolean
-mm_iface_modem_get_sar_state (MMIfaceModemSar *self)
-{
-    MmGdbusModemSar *skeleton = NULL;
-    gboolean         state;
-
-    g_object_get (self,
-                  MM_IFACE_MODEM_SAR_DBUS_SKELETON, &skeleton,
-                  NULL);
-
-    if (!skeleton)
-        return FALSE;
-
-    state  = mm_gdbus_modem_sar_get_state (skeleton);
-    g_object_unref (skeleton);
-    return state;
-}
-
 guint
 mm_iface_modem_sar_get_power_level (MMIfaceModemSar *self)
 {
@@ -96,12 +78,15 @@
               HandleEnableContext *ctx)
 {
     GError *error = NULL;
+    guint   power_level = 0;
 
-    if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->enable_finish (self, res, &error))
+    if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->enable_finish (self, res, &power_level, &error))
         g_dbus_method_invocation_take_error (ctx->invocation, error);
     else {
         /* Update current features in the interface */
         mm_gdbus_modem_sar_set_state (ctx->skeleton, ctx->enable);
+        if (ctx->enable)
+            mm_gdbus_modem_sar_set_power_level (ctx->skeleton, power_level);
         mm_gdbus_modem_sar_complete_enable (ctx->skeleton, ctx->invocation);
     }
 
@@ -132,6 +117,12 @@
         return;
     }
 
+    if (mm_gdbus_modem_sar_get_state (ctx->skeleton) == ctx->enable) {
+        mm_gdbus_modem_sar_complete_enable (ctx->skeleton, ctx->invocation);
+        handle_enable_context_free (ctx);
+        return;
+    }
+
     mm_obj_dbg (self, "%s SAR...", ctx->enable ? "Enabling" : "Disabling");
 
     MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->enable (ctx->self,
@@ -188,7 +179,7 @@
 {
     GError *error = NULL;
 
-    if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->enable_finish (self, res, &error)) {
+    if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->set_power_level_finish (self, res, &error)) {
         g_dbus_method_invocation_take_error (ctx->invocation, error);
     } else {
         mm_gdbus_modem_sar_set_power_level (ctx->skeleton, ctx->power_level);
@@ -222,6 +213,21 @@
         return;
     }
 
+    if (!mm_gdbus_modem_sar_get_state (ctx->skeleton)) {
+        g_dbus_method_invocation_return_error (ctx->invocation,
+                                               MM_CORE_ERROR,
+                                               MM_CORE_ERROR_WRONG_STATE,
+                                               "Cannot set SAR power level: SAR is disabled");
+        handle_set_power_level_context_free (ctx);
+        return;
+    }
+
+    if (mm_gdbus_modem_sar_get_power_level (ctx->skeleton) == ctx->power_level) {
+        mm_gdbus_modem_sar_complete_set_power_level (ctx->skeleton, ctx->invocation);
+        handle_set_power_level_context_free (ctx);
+        return;
+    }
+
     mm_obj_dbg (self, "Set SAR power level to: '%d'", ctx->power_level);
     MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->set_power_level (
         ctx->self,
diff --git a/src/mm-iface-modem-sar.h b/src/mm-iface-modem-sar.h
index 2dba700..e6518f1 100644
--- a/src/mm-iface-modem-sar.h
+++ b/src/mm-iface-modem-sar.h
@@ -48,6 +48,7 @@
                      gpointer            user_data);
     gboolean (* enable_finish) (MMIfaceModemSar *self,
                                 GAsyncResult    *res,
+                                guint           *out_sar_power_level,
                                 GError         **error);
     /* Get SAR state (async) */
     void (* load_state) (MMIfaceModemSar    *self,
@@ -96,7 +97,6 @@
 void     mm_iface_modem_sar_bind_simple_status (MMIfaceModemSar*self,
                                                 MMSimpleStatus *status);
 
-gboolean mm_iface_modem_get_sar_state              (MMIfaceModemSar *self);
-guint    mm_iface_modem_sar_get_power_level        (MMIfaceModemSar *self);
+guint    mm_iface_modem_sar_get_power_level    (MMIfaceModemSar *self);
 
 #endif /* MM_IFACE_MODEM_SAR_H */
diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c
index c9d1619..911c139 100644
--- a/src/mm-iface-modem.c
+++ b/src/mm-iface-modem.c
@@ -3758,16 +3758,16 @@
             g_error_matches (error,
                              MM_MOBILE_EQUIPMENT_ERROR,
                              MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) {
-            /* SIM errors are only critical in 3GPP-only devices */
-            if (!mm_iface_modem_is_cdma (self)) {
+            /* SIM errors are only critical in 3GPP-capable devices */
+            if (mm_iface_modem_is_3gpp (self)) {
                 ctx->saved_error = error;
                 ctx->step = UPDATE_LOCK_INFO_CONTEXT_STEP_LAST;
                 update_lock_info_context_step (task);
                 return;
             }
 
-            /* For mixed 3GPP+3GPP2 devices, skip SIM errors */
-            mm_obj_dbg (self, "skipping SIM error in 3GPP2-capable device, assuming no lock is needed");
+            /* For non 3GPP-capable devices, skip SIM errors */
+            mm_obj_dbg (self, "skipping SIM error in non 3GPP-capable device, assuming no lock is needed");
             g_error_free (error);
             ctx->lock = MM_MODEM_LOCK_NONE;
         } else {
@@ -4778,48 +4778,6 @@
     }
 
 static void
-current_capabilities_internal_load_unlock_required_ready (MMIfaceModem *self,
-                                                          GAsyncResult *res,
-                                                          GTask *task)
-{
-    InitializationContext *ctx;
-    GError *error = NULL;
-
-    ctx = g_task_get_task_data (task);
-
-    internal_load_unlock_required_finish (self, res, &error);
-    if (error) {
-        /* These SIM errors indicate that there is NO valid SIM available. So,
-         * remove all 3GPP caps from the current capabilities */
-        if (g_error_matches (error,
-                             MM_MOBILE_EQUIPMENT_ERROR,
-                             MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) ||
-            g_error_matches (error,
-                             MM_MOBILE_EQUIPMENT_ERROR,
-                             MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) ||
-            g_error_matches (error,
-                             MM_MOBILE_EQUIPMENT_ERROR,
-                             MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) {
-            MMModemCapability caps;
-
-            mm_obj_dbg (self, "multimode device without SIM, no 3GPP capabilities");
-            caps = mm_gdbus_modem_get_current_capabilities (ctx->skeleton);
-            caps &= ~MM_MODEM_CAPABILITY_3GPP;
-
-            /* CDMA-EVDO must still be around */
-            g_assert (caps & MM_MODEM_CAPABILITY_CDMA_EVDO);
-            mm_gdbus_modem_set_current_capabilities (ctx->skeleton, caps);
-        }
-
-        g_error_free (error);
-    }
-
-    /* Keep on */
-    ctx->step++;
-    interface_initialization_step (task);
-}
-
-static void
 load_current_capabilities_ready (MMIfaceModem *self,
                                  GAsyncResult *res,
                                  GTask *task)
@@ -4871,22 +4829,8 @@
                       NULL);
     }
 
-    /* Update current caps right away, even if we may fix them during the
-     * multimode device check. No big deal in updating them twice, as we're not
-     * exposed in DBus yet. */
     mm_gdbus_modem_set_current_capabilities (ctx->skeleton, caps);
 
-    /* If the device is a multimode device (3GPP+3GPP2) check whether we have a
-     * SIM or not. */
-    if ((caps & MM_MODEM_CAPABILITY_CDMA_EVDO) && (caps & MM_MODEM_CAPABILITY_3GPP)) {
-        mm_obj_dbg (self, "checking if multimode device has a SIM...");
-        internal_load_unlock_required (
-            self,
-            (GAsyncReadyCallback)current_capabilities_internal_load_unlock_required_ready,
-            task);
-        return;
-    }
-
     ctx->step++;
     interface_initialization_step (task);
 }
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index b339812..93d9fda 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -460,7 +460,7 @@
     /* Example:
      * <CR><LF>RING<CR><LF>
      */
-    return g_regex_new ("\\r\\nRING\\r\\n",
+    return g_regex_new ("\\r\\nRING(?:\\r)?\\r\\n",
                         G_REGEX_RAW | G_REGEX_OPTIMIZE,
                         0,
                         NULL);
diff --git a/src/mm-sim-mbim.c b/src/mm-sim-mbim.c
index 10f29f0..68c35dc 100644
--- a/src/mm-sim-mbim.c
+++ b/src/mm-sim-mbim.c
@@ -961,7 +961,7 @@
 }
 
 static GByteArray *
-common_read_binary_finish (MMBaseSim     *self,
+common_read_binary_finish (MMSimMbim     *self,
                            GAsyncResult  *res,
                            GError       **error)
 {
@@ -1076,14 +1076,56 @@
 }
 
 /*****************************************************************************/
-/* Read GID1 and GID2 */
+/* Read GID1 */
 
 static GByteArray *
 load_gid1_finish (MMBaseSim     *self,
                   GAsyncResult  *res,
                   GError       **error)
 {
-    return common_read_binary_finish (self, res, error);
+    return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+parent_load_gid1_ready (MMBaseSim    *self,
+                        GAsyncResult *res,
+                        GTask        *task)
+{
+    g_autoptr(GError)  error = NULL;
+    GByteArray        *value;
+
+    value = MM_BASE_SIM_CLASS(mm_sim_mbim_parent_class)->load_gid1_finish (self, res, &error);
+    if (value) {
+        g_task_return_pointer (task, value, (GDestroyNotify)g_byte_array_unref);
+    } else {
+        mm_obj_dbg (self, "failed reading GID1 using AT: %s", error->message);
+        g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+                                 "Failed reading GID1 from SIM card");
+    }
+
+    g_object_unref (task);
+}
+
+static void
+common_read_binary_gid1_ready (MMSimMbim    *self,
+                               GAsyncResult *res,
+                               GTask        *task)
+{
+    g_autoptr(GError)  error = NULL;
+    GByteArray        *value;
+
+    value = common_read_binary_finish (self, res, &error);
+    if (value) {
+        g_task_return_pointer (task, value, (GDestroyNotify)g_byte_array_unref);
+        g_object_unref (task);
+        return;
+    }
+
+    /* Fallback to parent implementation if possible */
+    mm_obj_dbg (self, "failed reading GID1 using MBIM: %s", error->message);
+    MM_BASE_SIM_CLASS(mm_sim_mbim_parent_class)->load_gid1 (MM_BASE_SIM (self),
+                                                            (GAsyncReadyCallback)parent_load_gid1_ready,
+                                                            task);
 }
 
 static void
@@ -1091,17 +1133,69 @@
            GAsyncReadyCallback  callback,
            gpointer             user_data)
 {
-    const guint8 file_path[] = { 0x7F, 0xFF, 0x6F, 0x3E };
+    GTask        *task;
+    const guint8  file_path[] = { 0x7F, 0xFF, 0x6F, 0x3E };
 
-    common_read_binary (MM_SIM_MBIM (self), file_path, G_N_ELEMENTS (file_path), callback, user_data);
+    task = g_task_new (self, NULL, callback, user_data);
+
+    common_read_binary (MM_SIM_MBIM (self),
+                        file_path,
+                        G_N_ELEMENTS (file_path),
+                        (GAsyncReadyCallback)common_read_binary_gid1_ready,
+                        task);
 }
 
+/*****************************************************************************/
+/* Read GID2 */
+
 static GByteArray *
 load_gid2_finish (MMBaseSim     *self,
                   GAsyncResult  *res,
                   GError       **error)
 {
-    return common_read_binary_finish (self, res, error);
+    return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+parent_load_gid2_ready (MMBaseSim    *self,
+                        GAsyncResult *res,
+                        GTask        *task)
+{
+    g_autoptr(GError)  error = NULL;
+    GByteArray        *value;
+
+    value = MM_BASE_SIM_CLASS(mm_sim_mbim_parent_class)->load_gid2_finish (self, res, &error);
+    if (value) {
+        g_task_return_pointer (task, value, (GDestroyNotify)g_byte_array_unref);
+    } else {
+        mm_obj_dbg (self, "failed reading GID2 using AT: %s", error->message);
+        g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+                                 "Failed reading GID2 from SIM card");
+    }
+
+    g_object_unref (task);
+}
+
+static void
+common_read_binary_gid2_ready (MMSimMbim    *self,
+                               GAsyncResult *res,
+                               GTask        *task)
+{
+    g_autoptr(GError)  error = NULL;
+    GByteArray        *value;
+
+    value = common_read_binary_finish (self, res, &error);
+    if (value) {
+        g_task_return_pointer (task, value, (GDestroyNotify)g_byte_array_unref);
+        g_object_unref (task);
+        return;
+    }
+
+    /* Fallback to parent implementation if possible */
+    mm_obj_dbg (self, "failed reading GID2 using MBIM: %s", error->message);
+    MM_BASE_SIM_CLASS(mm_sim_mbim_parent_class)->load_gid2 (MM_BASE_SIM (self),
+                                                            (GAsyncReadyCallback)parent_load_gid2_ready,
+                                                            task);
 }
 
 static void
@@ -1109,9 +1203,16 @@
            GAsyncReadyCallback  callback,
            gpointer             user_data)
 {
-    const guint8 file_path[] = { 0x7F, 0xFF, 0x6F, 0x3F };
+    GTask        *task;
+    const guint8  file_path[] = { 0x7F, 0xFF, 0x6F, 0x3F };
 
-    common_read_binary (MM_SIM_MBIM (self), file_path, G_N_ELEMENTS (file_path), callback, user_data);
+    task = g_task_new (self, NULL, callback, user_data);
+
+    common_read_binary (MM_SIM_MBIM (self),
+                        file_path,
+                        G_N_ELEMENTS (file_path),
+                        (GAsyncReadyCallback)common_read_binary_gid2_ready,
+                        task);
 }
 
 /*****************************************************************************/
diff --git a/src/mm-sms-part-3gpp.c b/src/mm-sms-part-3gpp.c
index 07d3ef8..8a36cab 100644
--- a/src/mm-sms-part-3gpp.c
+++ b/src/mm-sms-part-3gpp.c
@@ -422,23 +422,22 @@
         PDU_SIZE_CHECK (1, "cannot read SMSC address length");
         smsc_addr_size_bytes = pdu[offset++];
         if (smsc_addr_size_bytes > 0) {
-             PDU_SIZE_CHECK (offset + smsc_addr_size_bytes, "cannot read SMSC address");
-             /* SMSC may not be given in DELIVER PDUs */
-             address = sms_decode_address (&pdu[1], 2 * (smsc_addr_size_bytes - 1), error);
-             if (!address) {
-                     g_prefix_error (error, "Couldn't read SMSC address: ");
-                     mm_sms_part_free (sms_part);
-                     return NULL;
-             }
-             mm_sms_part_take_smsc (sms_part, g_steal_pointer (&address));
-             mm_obj_dbg (log_object, "  SMSC address parsed: '%s'", mm_sms_part_get_smsc (sms_part));
-             offset += smsc_addr_size_bytes;
+            PDU_SIZE_CHECK (offset + smsc_addr_size_bytes, "cannot read SMSC address");
+            /* SMSC may not be given in DELIVER PDUs */
+            address = sms_decode_address (&pdu[1], 2 * (smsc_addr_size_bytes - 1), error);
+            if (!address) {
+                g_prefix_error (error, "Couldn't read SMSC address: ");
+                mm_sms_part_free (sms_part);
+                return NULL;
+            }
+            mm_sms_part_take_smsc (sms_part, g_steal_pointer (&address));
+            mm_obj_dbg (log_object, "  SMSC address parsed: '%s'", mm_sms_part_get_smsc (sms_part));
+            offset += smsc_addr_size_bytes;
         } else
-              mm_obj_dbg (log_object, "  no SMSC address given");
+            mm_obj_dbg (log_object, "  no SMSC address given");
     } else
         mm_obj_dbg (log_object, "  This is a transfer-route message");
 
-
     /* ---------------------------------------------------------------------- */
     /* TP-MTI (1 byte) */
     PDU_SIZE_CHECK (offset + 1, "cannot read TP-MTI");
@@ -492,7 +491,6 @@
         offset++;
     }
 
-
     /* ---------------------------------------------------------------------- */
     /* TP-DA or TP-OA or TP-RA
      * First byte represents the number of DIGITS in the number.
@@ -519,6 +517,7 @@
 
     if (pdu_type == SMS_TP_MTI_SMS_DELIVER) {
         gchar *str = NULL;
+
         PDU_SIZE_CHECK (offset + 9,
                         "cannot read PID/DCS/Timestamp"); /* 1+1+7=9 */
 
@@ -534,8 +533,7 @@
             mm_sms_part_free (sms_part);
             return NULL;
         }
-        mm_sms_part_take_timestamp (sms_part,
-                                    str);
+        mm_sms_part_take_timestamp (sms_part, str);
         offset += 7;
 
         tp_user_data_len_offset = offset;
@@ -577,9 +575,9 @@
         }
 
         tp_user_data_len_offset = offset;
-    }
-    else if (pdu_type == SMS_TP_MTI_SMS_STATUS_REPORT) {
+    } else if (pdu_type == SMS_TP_MTI_SMS_STATUS_REPORT) {
         gchar *str = NULL;
+
         /* We have 2 timestamps in status report PDUs:
          *  first, the timestamp for when the PDU was received in the SMSC
          *  second, the timestamp for when the PDU was forwarded by the SMSC