api,modem: new 'CarrierConfigurationRevision' property

Which reports the version of the currently active carrier
configuration.

We also update the firmware 'version' reported in the firmware
settings so that carrier-specific upgrades can be performed (e.g. when
the firmware stays the same but the MCFG is updated).
diff --git a/cli/mmcli-modem.c b/cli/mmcli-modem.c
index 5d3c271..e6c4a62 100644
--- a/cli/mmcli-modem.c
+++ b/cli/mmcli-modem.c
@@ -351,7 +351,8 @@
     mmcli_output_string           (MMC_F_HARDWARE_MANUFACTURER,           mm_modem_get_manufacturer (ctx->modem));
     mmcli_output_string           (MMC_F_HARDWARE_MODEL,                  mm_modem_get_model (ctx->modem));
     mmcli_output_string           (MMC_F_HARDWARE_REVISION,               mm_modem_get_revision (ctx->modem));
-    mmcli_output_string           (MMC_F_HARDWARE_CARRIER_CONFIGURATION,  mm_modem_get_carrier_configuration (ctx->modem));
+    mmcli_output_string           (MMC_F_HARDWARE_CARRIER_CONF,           mm_modem_get_carrier_configuration (ctx->modem));
+    mmcli_output_string           (MMC_F_HARDWARE_CARRIER_CONF_REV,       mm_modem_get_carrier_configuration_revision (ctx->modem));
     mmcli_output_string           (MMC_F_HARDWARE_HW_REVISION,            mm_modem_get_hardware_revision (ctx->modem));
     mmcli_output_string_multiline (MMC_F_HARDWARE_SUPPORTED_CAPABILITIES, supported_capabilities_string);
     mmcli_output_string_multiline (MMC_F_HARDWARE_CURRENT_CAPABILITIES,   current_capabilities_string);
diff --git a/cli/mmcli-output.c b/cli/mmcli-output.c
index 33819fe..0242460 100644
--- a/cli/mmcli-output.c
+++ b/cli/mmcli-output.c
@@ -96,8 +96,9 @@
     [MMC_F_GENERAL_DEVICE_ID]                 = { "modem.generic.device-identifier",                 "device id",                MMC_S_MODEM_GENERAL,           },
     [MMC_F_HARDWARE_MANUFACTURER]             = { "modem.generic.manufacturer",                      "manufacturer",             MMC_S_MODEM_HARDWARE,          },
     [MMC_F_HARDWARE_MODEL]                    = { "modem.generic.model",                             "model",                    MMC_S_MODEM_HARDWARE,          },
-    [MMC_F_HARDWARE_REVISION]                 = { "modem.generic.revision",                          "revision",                 MMC_S_MODEM_HARDWARE,          },
-    [MMC_F_HARDWARE_CARRIER_CONFIGURATION]    = { "modem.generic.carrier-configuration",             "carrier config",           MMC_S_MODEM_HARDWARE,          },
+    [MMC_F_HARDWARE_REVISION]                 = { "modem.generic.revision",                          "firmware revision",        MMC_S_MODEM_HARDWARE,          },
+    [MMC_F_HARDWARE_CARRIER_CONF]             = { "modem.generic.carrier-configuration",             "carrier config",           MMC_S_MODEM_HARDWARE,          },
+    [MMC_F_HARDWARE_CARRIER_CONF_REV]         = { "modem.generic.carrier-configuration-revision",    "carrier config revision",  MMC_S_MODEM_HARDWARE,          },
     [MMC_F_HARDWARE_HW_REVISION]              = { "modem.generic.hardware-revision",                 "h/w revision",             MMC_S_MODEM_HARDWARE,          },
     [MMC_F_HARDWARE_SUPPORTED_CAPABILITIES]   = { "modem.generic.supported-capabilities",            "supported",                MMC_S_MODEM_HARDWARE,          },
     [MMC_F_HARDWARE_CURRENT_CAPABILITIES]     = { "modem.generic.current-capabilities",              "current",                  MMC_S_MODEM_HARDWARE,          },
diff --git a/cli/mmcli-output.h b/cli/mmcli-output.h
index 75533b0..7793f79 100644
--- a/cli/mmcli-output.h
+++ b/cli/mmcli-output.h
@@ -91,7 +91,8 @@
     MMC_F_HARDWARE_MANUFACTURER,
     MMC_F_HARDWARE_MODEL,
     MMC_F_HARDWARE_REVISION,
-    MMC_F_HARDWARE_CARRIER_CONFIGURATION,
+    MMC_F_HARDWARE_CARRIER_CONF,
+    MMC_F_HARDWARE_CARRIER_CONF_REV,
     MMC_F_HARDWARE_HW_REVISION,
     MMC_F_HARDWARE_SUPPORTED_CAPABILITIES,
     MMC_F_HARDWARE_CURRENT_CAPABILITIES,
diff --git a/docs/reference/libmm-glib/libmm-glib-sections.txt b/docs/reference/libmm-glib/libmm-glib-sections.txt
index 4055868..583b494 100644
--- a/docs/reference/libmm-glib/libmm-glib-sections.txt
+++ b/docs/reference/libmm-glib/libmm-glib-sections.txt
@@ -139,6 +139,8 @@
 mm_modem_dup_revision
 mm_modem_get_carrier_configuration
 mm_modem_dup_carrier_configuration
+mm_modem_get_carrier_configuration_revision
+mm_modem_dup_carrier_configuration_revision
 mm_modem_get_hardware_revision
 mm_modem_dup_hardware_revision
 mm_modem_get_drivers
@@ -2026,6 +2028,8 @@
 mm_gdbus_modem_dup_revision
 mm_gdbus_modem_get_carrier_configuration
 mm_gdbus_modem_dup_carrier_configuration
+mm_gdbus_modem_get_carrier_configuration_revision
+mm_gdbus_modem_dup_carrier_configuration_revision
 mm_gdbus_modem_get_hardware_revision
 mm_gdbus_modem_dup_hardware_revision
 mm_gdbus_modem_get_signal_quality
@@ -2098,6 +2102,7 @@
 mm_gdbus_modem_set_ports
 mm_gdbus_modem_set_revision
 mm_gdbus_modem_set_carrier_configuration
+mm_gdbus_modem_set_carrier_configuration_revision
 mm_gdbus_modem_set_hardware_revision
 mm_gdbus_modem_set_signal_quality
 mm_gdbus_modem_set_sim
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.xml b/introspection/org.freedesktop.ModemManager1.Modem.xml
index f647d2f..0fc5741 100644
--- a/introspection/org.freedesktop.ModemManager1.Modem.xml
+++ b/introspection/org.freedesktop.ModemManager1.Modem.xml
@@ -304,6 +304,13 @@
     <property name="CarrierConfiguration" type="s" access="read" />
 
     <!--
+        CarrierConfigurationRevision:
+
+        The revision identification of the carrier-specific configuration (MCFG) in use by the modem.
+    -->
+    <property name="CarrierConfigurationRevision" type="s" access="read" />
+
+    <!--
         HardwareRevision:
 
         The revision identification of the hardware, as reported by the modem.
diff --git a/libmm-glib/mm-modem.c b/libmm-glib/mm-modem.c
index d7d5954..9aab453 100644
--- a/libmm-glib/mm-modem.c
+++ b/libmm-glib/mm-modem.c
@@ -544,6 +544,47 @@
 /*****************************************************************************/
 
 /**
+ * mm_modem_get_carrier_configuration_revision:
+ * @self: A #MMModem.
+ *
+ * Gets the carrier-specific configuration revision in use, as reported by this #MMModem.
+ *
+ * <warning>The returned value is only valid until the property changes so
+ * it is only safe to use this function on the thread where
+ * @self was constructed. Use mm_modem_dup_carrier_configuration() if on another
+ * thread.</warning>
+ *
+ * Returns: (transfer none): The carrier configuration revision, or %NULL if none available. Do not free the returned value, it belongs to @self.
+ */
+const gchar *
+mm_modem_get_carrier_configuration_revision (MMModem *self)
+{
+    g_return_val_if_fail (MM_IS_MODEM (self), NULL);
+
+    RETURN_NON_EMPTY_CONSTANT_STRING (
+        mm_gdbus_modem_get_carrier_configuration_revision (MM_GDBUS_MODEM (self)));
+}
+
+/**
+ * mm_modem_dup_carrier_configuration_revision:
+ * @self: A #MMModem.
+ *
+ * Gets a copy of the carrier-specific configuration revision in use, as reported by this #MMModem.
+ *
+ * Returns: (transfer full): The carrier configuration revision, or %NULL if none available. The returned value should be freed with g_free().
+ */
+gchar *
+mm_modem_dup_carrier_configuration_revision (MMModem *self)
+{
+    g_return_val_if_fail (MM_IS_MODEM (self), NULL);
+
+    RETURN_NON_EMPTY_STRING (
+        mm_gdbus_modem_dup_carrier_configuration_revision (MM_GDBUS_MODEM (self)));
+}
+
+/*****************************************************************************/
+
+/**
  * mm_modem_get_hardware_revision:
  * @self: A #MMModem.
  *
diff --git a/libmm-glib/mm-modem.h b/libmm-glib/mm-modem.h
index 95719d6..e494ced 100644
--- a/libmm-glib/mm-modem.h
+++ b/libmm-glib/mm-modem.h
@@ -103,8 +103,10 @@
 const gchar       *mm_modem_get_revision             (MMModem *self);
 gchar             *mm_modem_dup_revision             (MMModem *self);
 
-const gchar       *mm_modem_get_carrier_configuration (MMModem *self);
-gchar             *mm_modem_dup_carrier_configuration (MMModem *self);
+const gchar       *mm_modem_get_carrier_configuration          (MMModem *self);
+gchar             *mm_modem_dup_carrier_configuration          (MMModem *self);
+const gchar       *mm_modem_get_carrier_configuration_revision (MMModem *self);
+gchar             *mm_modem_dup_carrier_configuration_revision (MMModem *self);
 
 const gchar       *mm_modem_get_hardware_revision    (MMModem *self);
 gchar             *mm_modem_dup_hardware_revision    (MMModem *self);
diff --git a/src/mm-iface-modem-firmware.c b/src/mm-iface-modem-firmware.c
index 1775a1c..ecfa3b5 100644
--- a/src/mm-iface-modem-firmware.c
+++ b/src/mm-iface-modem-firmware.c
@@ -287,16 +287,27 @@
                      MMFirmwareUpdateSettings  *update_settings,
                      GError                   **error)
 {
-    const gchar *revision;
+    const gchar *firmware_revision;
+    const gchar *carrier_revision;
+    gchar       *combined;
 
-    revision = mm_iface_modem_get_revision (MM_IFACE_MODEM (self));
-    if (!revision) {
+    firmware_revision = mm_iface_modem_get_revision (MM_IFACE_MODEM (self));
+    if (!firmware_revision) {
         g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
                      "Unknown revision");
         return FALSE;
     }
 
-    mm_firmware_update_settings_set_version (update_settings, revision);
+    mm_iface_modem_get_carrier_config (MM_IFACE_MODEM (self), NULL, &carrier_revision);
+
+    if (!carrier_revision) {
+        mm_firmware_update_settings_set_version (update_settings, firmware_revision);
+        return TRUE;
+    }
+
+    combined = g_strdup_printf ("%s - %s", firmware_revision, carrier_revision);
+    mm_firmware_update_settings_set_version (update_settings, combined);
+    g_free (combined);
     return TRUE;
 }
 
@@ -335,8 +346,7 @@
         return FALSE;
     }
 
-    /* carrier = g_ascii_strup (mm_iface_modem_get_carrier_config (MM_IFACE_MODEM (self)), -1);     */
-    aux = mm_iface_modem_get_carrier_config (MM_IFACE_MODEM (self));
+    mm_iface_modem_get_carrier_config (MM_IFACE_MODEM (self), &aux, NULL);
 
     ids = g_ptr_array_new_with_free_func ((GDestroyNotify)g_free);
     if (aux) {
diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c
index 292923d..66db38a 100644
--- a/src/mm-iface-modem.c
+++ b/src/mm-iface-modem.c
@@ -4363,17 +4363,19 @@
 {
     InitializationContext *ctx;
     GError                *error = NULL;
-    gchar                 *carrier_configuration;
+    gchar                 *name = NULL;
+    gchar                 *revision = NULL;
 
     ctx = g_task_get_task_data (task);
 
-    carrier_configuration = MM_IFACE_MODEM_GET_INTERFACE (self)->load_carrier_config_finish (self, res, &error);
-    if (!carrier_configuration) {
+    if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_carrier_config_finish (self, res, &name, &revision, &error)) {
         mm_warn ("couldn't load carrier config: '%s'", error->message);
         g_error_free (error);
     } else {
-        mm_gdbus_modem_set_carrier_configuration (ctx->skeleton, carrier_configuration);
-        g_free (carrier_configuration);
+        mm_gdbus_modem_set_carrier_configuration          (ctx->skeleton, name);
+        mm_gdbus_modem_set_carrier_configuration_revision (ctx->skeleton, revision);
+        g_free (name);
+        g_free (revision);
     }
 
     /* Go on to next step */
@@ -5452,22 +5454,25 @@
     return revision;
 }
 
-const gchar *
-mm_iface_modem_get_carrier_config (MMIfaceModem *self)
+gboolean
+mm_iface_modem_get_carrier_config (MMIfaceModem  *self,
+                                   const gchar  **name,
+                                   const gchar  **revision)
 {
-    const gchar *carrier_config = NULL;
     MmGdbusModem *skeleton;
 
     g_object_get (self,
                   MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
                   NULL);
+    if (!skeleton)
+        return FALSE;
 
-    if (skeleton) {
-        carrier_config = mm_gdbus_modem_get_carrier_configuration (skeleton);
-        g_object_unref (skeleton);
-    }
-
-    return carrier_config;
+    if (name)
+        *name = mm_gdbus_modem_get_carrier_configuration (skeleton);
+    if (revision)
+        *revision = mm_gdbus_modem_get_carrier_configuration_revision (skeleton);
+    g_object_unref (skeleton);
+    return TRUE;
 }
 
 /*****************************************************************************/
diff --git a/src/mm-iface-modem.h b/src/mm-iface-modem.h
index 82236dd..51cb02b 100644
--- a/src/mm-iface-modem.h
+++ b/src/mm-iface-modem.h
@@ -362,8 +362,10 @@
     void     (* load_carrier_config)        (MMIfaceModem         *self,
                                              GAsyncReadyCallback   callback,
                                              gpointer              user_data);
-    gchar *  (* load_carrier_config_finish) (MMIfaceModem         *self,
+    gboolean (* load_carrier_config_finish) (MMIfaceModem         *self,
                                              GAsyncResult         *res,
+                                             gchar               **carrier_config_name,
+                                             gchar               **carrier_config_revision,
                                              GError              **error);
 
     /* Setup carrier config based on IMSI */
@@ -400,9 +402,11 @@
 gboolean mm_iface_modem_is_4g_only (MMIfaceModem *self);
 
 /* Helpers to query properties */
-const gchar *mm_iface_modem_get_model          (MMIfaceModem *self);
-const gchar *mm_iface_modem_get_revision       (MMIfaceModem *self);
-const gchar *mm_iface_modem_get_carrier_config (MMIfaceModem *self);
+const gchar *mm_iface_modem_get_model          (MMIfaceModem  *self);
+const gchar *mm_iface_modem_get_revision       (MMIfaceModem  *self);
+gboolean     mm_iface_modem_get_carrier_config (MMIfaceModem  *self,
+                                                const gchar  **name,
+                                                const gchar  **revision);
 
 /* Initialize Modem interface (async) */
 void     mm_iface_modem_initialize        (MMIfaceModem *self,
diff --git a/src/mm-shared-qmi.c b/src/mm-shared-qmi.c
index c48b543..989e066 100644
--- a/src/mm-shared-qmi.c
+++ b/src/mm-shared-qmi.c
@@ -2419,7 +2419,6 @@
     GArray       *config_list;
     guint         configs_loaded;
     gint          config_active_i;
-    gchar        *config_active;
 
     guint         token;
     guint         timeout_id;
@@ -2458,17 +2457,31 @@
 
     if (ctx->config_list)
         g_array_unref (ctx->config_list);
-    g_free (ctx->config_active);
     g_clear_object (&ctx->client);
     g_slice_free (LoadCarrierConfigContext, ctx);
 }
 
-gchar *
+gboolean
 mm_shared_qmi_load_carrier_config_finish (MMIfaceModem  *self,
                                           GAsyncResult  *res,
+                                          gchar        **carrier_config_name,
+                                          gchar        **carrier_config_revision,
                                           GError       **error)
 {
-    return g_task_propagate_pointer (G_TASK (res), error);
+    Private    *priv;
+    ConfigInfo *config;
+    gssize      i;
+
+    i = g_task_propagate_int (G_TASK (res), error);
+    if (i < 0)
+        return FALSE;
+
+    priv = get_private (MM_SHARED_QMI (self));
+    config = &g_array_index (priv->config_list, ConfigInfo, i);
+
+    *carrier_config_name = g_strdup (config->description);
+    *carrier_config_revision = g_strdup_printf ("%08X", config->version);
+    return TRUE;
 }
 
 static void load_carrier_config_step (GTask *task);
@@ -2543,7 +2556,6 @@
         if ((config->id->len == active_id->len) &&
             !memcmp (config->id->data, active_id->data, active_id->len)) {
             ctx->config_active_i = i;
-            ctx->config_active = g_strdup (config->description);
             break;
         }
     }
@@ -2812,8 +2824,7 @@
         priv->config_list = g_array_ref (ctx->config_list);
         priv->config_active_i = ctx->config_active_i;
 
-        g_assert (ctx->config_active);
-        g_task_return_pointer (task, g_strdup (ctx->config_active), g_free);
+        g_task_return_int (task, ctx->config_active_i);
         g_object_unref (task);
         break;
     }
diff --git a/src/mm-shared-qmi.h b/src/mm-shared-qmi.h
index 43564b1..093896e 100644
--- a/src/mm-shared-qmi.h
+++ b/src/mm-shared-qmi.h
@@ -150,8 +150,10 @@
 void               mm_shared_qmi_load_carrier_config                (MMIfaceModem         *self,
                                                                      GAsyncReadyCallback   callback,
                                                                      gpointer              user_data);
-gchar             *mm_shared_qmi_load_carrier_config_finish         (MMIfaceModem         *self,
+gboolean           mm_shared_qmi_load_carrier_config_finish         (MMIfaceModem         *self,
                                                                      GAsyncResult         *res,
+                                                                     gchar               **carrier_config_name,
+                                                                     gchar               **carrier_config_revision,
                                                                      GError              **error);
 void               mm_shared_qmi_setup_carrier_config               (MMIfaceModem         *self,
                                                                      const gchar          *imsi,