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

Change-Id: If60714a84f9c174757c8e274dd663210abe2a072
diff --git a/cli/mmcli-bearer.c b/cli/mmcli-bearer.c
index cd9ecca..8b05313 100644
--- a/cli/mmcli-bearer.c
+++ b/cli/mmcli-bearer.c
@@ -265,13 +265,13 @@
         if (val > 0)
             g_print ("                     |    Bytes received: '%" G_GUINT64_FORMAT "'\n", val);
         else
-            g_print ("                     |    Bytes received: 'N/A'\n");
+            g_print ("                     |    Bytes received: 'n/a'\n");
 
         val = mm_bearer_stats_get_tx_bytes (stats);
         if (val > 0)
             g_print ("                     | Bytes transmitted: '%" G_GUINT64_FORMAT "'\n", val);
         else
-            g_print ("                     | Bytes transmitted: 'N/A'\n");
+            g_print ("                     | Bytes transmitted: 'n/a'\n");
     }
 
     g_clear_object (&stats);
diff --git a/cli/mmcli-modem.c b/cli/mmcli-modem.c
index f78b0d1..8967b1d 100644
--- a/cli/mmcli-modem.c
+++ b/cli/mmcli-modem.c
@@ -461,7 +461,7 @@
     /* If available, 3GPP related stuff */
     if (ctx->modem_3gpp) {
         gchar *facility_locks;
-        GList *pco_list, *l;
+        GList *pco_list;
 
         facility_locks = (mm_modem_3gpp_facility_build_string_from_mask (
                               mm_modem_3gpp_get_enabled_facility_locks (ctx->modem_3gpp)));
@@ -473,8 +473,7 @@
                  "           |  operator name: '%s'\n"
                  "           |   subscription: '%s'\n"
                  "           |   registration: '%s'\n"
-                 "           |    EPS UE mode: '%s'\n"
-                 "           |            PCO:\n",
+                 "           |    EPS UE mode: '%s'\n",
                  VALIDATE_UNKNOWN (mm_modem_3gpp_get_imei (ctx->modem_3gpp)),
                  facility_locks,
                  VALIDATE_UNKNOWN (mm_modem_3gpp_get_operator_code (ctx->modem_3gpp)),
@@ -486,25 +485,31 @@
                  mm_modem_3gpp_eps_ue_mode_operation_get_string (
                      mm_modem_3gpp_get_eps_ue_mode_operation (ctx->modem_3gpp)));
 
-        for (l = pco_list; l; l = g_list_next (l)) {
-            MMPco *pco = MM_PCO (l->data);
-            gchar *pco_data_hex = NULL;
-            const guint8 *pco_data;
-            gsize pco_data_size;
+        if (pco_list) {
+            GList *l;
 
-            pco_data = mm_pco_get_data (pco, &pco_data_size);
-            if (pco_data)
-                pco_data_hex = mm_utils_bin2hexstr (pco_data, pco_data_size);
+            g_print ("           |            PCO:\n");
+            for (l = pco_list; l; l = g_list_next (l)) {
+                MMPco *pco = MM_PCO (l->data);
+                gchar *pco_data_hex = NULL;
+                const guint8 *pco_data;
+                gsize pco_data_size;
 
-            g_print ("           |                 %u: (%s) '%s'\n",
-                     mm_pco_get_session_id (pco),
-                     mm_pco_is_complete (pco) ? "complete" : "partial",
-                     pco_data_hex ? pco_data_hex : "");
-            g_free (pco_data_hex);
-        }
+                pco_data = mm_pco_get_data (pco, &pco_data_size);
+                if (pco_data)
+                    pco_data_hex = mm_utils_bin2hexstr (pco_data, pco_data_size);
+
+                g_print ("           |                 %u: (%s) '%s'\n",
+                         mm_pco_get_session_id (pco),
+                         mm_pco_is_complete (pco) ? "complete" : "partial",
+                         pco_data_hex ? pco_data_hex : "");
+                g_free (pco_data_hex);
+            }
+            mm_pco_list_free (pco_list);
+        } else
+            g_print ("           |            PCO: 'n/a'\n");
 
         g_free (facility_locks);
-        mm_pco_list_free (pco_list);
     }
 
     /* If available, CDMA related stuff */
diff --git a/docs/reference/api/ModemManager-docs.xml b/docs/reference/api/ModemManager-docs.xml
index 57e3338..156b6ff 100644
--- a/docs/reference/api/ModemManager-docs.xml
+++ b/docs/reference/api/ModemManager-docs.xml
@@ -49,6 +49,7 @@
       <year>2015</year>
       <year>2016</year>
       <year>2017</year>
+      <year>2018</year>
       <holder>The ModemManager Authors</holder>
     </copyright>
 
@@ -84,6 +85,11 @@
     <xi:include href="xml/mm-errors.xml"/>
   </part>
 
+  <part id="ref-udev">
+    <title>Common udev tag definitions</title>
+    <xi:include href="xml/mm-tags.xml"/>
+  </part>
+
   <!-- Part 2, DBus reference -->
   <xi:include href="xml/ModemManager-dbus-reference.xml"/>
 
diff --git a/docs/reference/api/ModemManager-sections.txt b/docs/reference/api/ModemManager-sections.txt
index 5ec728d..6da6a33 100644
--- a/docs/reference/api/ModemManager-sections.txt
+++ b/docs/reference/api/ModemManager-sections.txt
@@ -140,3 +140,22 @@
 <SUBSECTION Private>
 MMModemBandDeprecated
 </SECTION>
+
+<SECTION>
+<FILE>mm-tags</FILE>
+<TITLE>Common udev tags</TITLE>
+ID_MM_CANDIDATE
+ID_MM_PHYSDEV_UID
+ID_MM_PORT_IGNORE
+ID_MM_DEVICE_PROCESS
+ID_MM_DEVICE_IGNORE
+ID_MM_DEVICE_MANUAL_SCAN_ONLY
+ID_MM_PLATFORM_DRIVER_PROBE
+ID_MM_PORT_TYPE_AT_PPP
+ID_MM_PORT_TYPE_AT_PRIMARY
+ID_MM_PORT_TYPE_AT_SECONDARY
+ID_MM_PORT_TYPE_GPS
+ID_MM_PORT_TYPE_QCDM
+ID_MM_TTY_BAUDRATE
+ID_MM_TTY_FLOW_CONTROL
+</SECTION>
diff --git a/include/Makefile.am b/include/Makefile.am
index 237ee6b..c0f3117 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -19,4 +19,5 @@
 	ModemManager-names.h
 
 EXTRA_DIST = \
+	ModemManager-tags.h \
 	ModemManager-version.h.in
diff --git a/include/ModemManager-tags.h b/include/ModemManager-tags.h
new file mode 100644
index 0000000..4be1e79
--- /dev/null
+++ b/include/ModemManager-tags.h
@@ -0,0 +1,210 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+/*
+ * NOTE! this file is NOT part of the installed ModemManager API.
+ *
+ * We keep this file under include/ because we want to build and
+ * expose the associated documentation.
+ */
+
+#ifndef MM_TAGS_H
+#define MM_TAGS_H
+
+/**
+ * SECTION:mm-tags
+ * @short_description: generic udev tags supported
+ *
+ * This section defines generic udev tags that are used by ModemManager,
+ * associated to full devices or to specific ports in a given device.
+ */
+
+/**
+ * ID_MM_CANDIDATE:
+ *
+ * This is a port-specific tag added automatically when all other
+ * ModemManager related tags have already been set.
+ */
+#define ID_MM_CANDIDATE "ID_MM_CANDIDATE"
+
+/**
+ * ID_MM_PHYSDEV_UID:
+ *
+ * This is a device-specific tag that allows users to 'name' modem
+ * devices with a predefined 'unique ID' string.
+ *
+ * When this tag is given per-port, the daemon will consider that all
+ * ports with the same UID value are associated to the same device.
+ * This is useful for e.g. modems that expose multiple RS232 ports
+ * connected to the system via different platform ports (or USB to
+ * RS232 adapters).
+ *
+ * This UID is exposed in
+ * the '<link linkend="gdbus-property-org-freedesktop-ModemManager1-Modem.Device">Device</link>'
+ * property and can then be used in mmcli calls to refer unequivocally
+ * to a specific device, regardless of its modem index, e.g.:
+ *  $ mmcli --modem=UID ...
+ */
+#define ID_MM_PHYSDEV_UID "ID_MM_PHYSDEV_UID"
+
+/**
+ * ID_MM_DEVICE_PROCESS:
+ *
+ * This is a device-specific tag that allows explicitly requesting the
+ * processing of all ports exposed by the device. This tag is usually
+ * used by users when the daemon runs with WHITELIST-ONLY filter policy
+ * type, and is associated to the MM_FILTER_RULE_EXPLICIT_WHITELIST rule.
+ *
+ * This tag may also be specified in specific ports, e.g. when the modem
+ * exposes a single platform port without any parent device.
+ */
+#define ID_MM_DEVICE_PROCESS "ID_MM_DEVICE_PROCESS"
+
+/**
+ * ID_MM_PORT_IGNORE:
+ *
+ * This is a port-specific tag that allows explicitly ignoring a given port
+ * in a device.
+ *
+ * This tag applies to all types of ports.
+ */
+#define ID_MM_PORT_IGNORE "ID_MM_PORT_IGNORE"
+
+/**
+ * ID_MM_DEVICE_IGNORE:
+ *
+ * This is a device-specific tag that allows explicitly blacklisting
+ * devices that expose TTY devices so that they are never probed.
+ *
+ * This tag is used when the daemon runs with DEFAULT or PARANOID
+ * filter policy type, and is associated to the MM_FILTER_RULE_TTY_BLACKLIST
+ * rule.
+ *
+ * This tag is ignored when the STRICT filter policy is used.
+ */
+#define ID_MM_DEVICE_IGNORE "ID_MM_DEVICE_IGNORE"
+
+/**
+ * ID_MM_DEVICE_MANUAL_SCAN_ONLY:
+ *
+ * This is a device-specific tag that allows explicitly greylisting
+ * devices that expose TTY devices so that they are never probed
+ * automatically. Instead, an explicit manual scan request may be sent
+ * to the daemon so that the TTY ports exposed by the device are
+ * probed.
+ *
+ * This tag is used when the daemon runs with DEFAULT or PARANOID
+ * filter policy type, and is associated to the MM_FILTER_RULE_TTY_MANUAL_SCAN_ONLY
+ * rule.
+ *
+ * This tag is ignored when the STRICT filter policy is used.
+ */
+#define ID_MM_DEVICE_MANUAL_SCAN_ONLY "ID_MM_DEVICE_MANUAL_SCAN_ONLY"
+
+/**
+ * ID_MM_PLATFORM_DRIVER_PROBE:
+ *
+ * This is a port-specific tag applied to platform ports so that they
+ * are probed automatically by the daemon. Platform ports that don't
+ * have this tag will never probed. This tag is a bit redundant, as
+ * the user could also use ID_MM_DEVICE_PROCESS for the same purpose.
+ *
+ * This tag is associated to the MM_FILTER_RULE_TTY_PLATFORM_DRIVER
+ * rule, which is only meaningful when the daemon runs with the
+ * DEFAULT filter policy type, as that is the only one that would
+ * allow probing all ports not explicitly forbidden before the last
+ * MM_FILTER_RULE_TTY_DEFAULT_ALLOWED rule.
+ */
+#define ID_MM_PLATFORM_DRIVER_PROBE "ID_MM_PLATFORM_DRIVER_PROBE"
+
+/**
+ * ID_MM_PORT_TYPE_AT_PRIMARY:
+ *
+ * This is a port-specific tag applied to TTYs that we know in advance
+ * are AT ports to be used as primary control ports.
+ *
+ * This tag will also prevent QCDM probing on the port.
+ */
+#define ID_MM_PORT_TYPE_AT_PRIMARY "ID_MM_PORT_TYPE_AT_PRIMARY"
+
+/**
+ * ID_MM_PORT_TYPE_AT_SECONDARY:
+ *
+ * This is a port-specific tag applied to TTYs that we know in advance
+ * are AT ports to be used as secondary control ports.
+ *
+ * This tag will also prevent QCDM probing on the port.
+ */
+#define ID_MM_PORT_TYPE_AT_SECONDARY "ID_MM_PORT_TYPE_AT_SECONDARY"
+
+/**
+ * ID_MM_PORT_TYPE_AT_PPP:
+ *
+ * This is a port-specific tag applied to TTYs that we know in advance
+ * are AT ports to be used as data ports exclusively.
+ *
+ * This tag will also prevent QCDM probing on the port.
+ */
+#define ID_MM_PORT_TYPE_AT_PPP "ID_MM_PORT_TYPE_AT_PPP"
+
+/**
+ * ID_MM_PORT_TYPE_QCDM:
+ *
+ * This is a port-specific tag applied to TTYs that we know in advance
+ * are QCDM ports.
+ *
+ * The only purpose of this tag is to prevent AT probing in the port.
+ */
+#define ID_MM_PORT_TYPE_QCDM "ID_MM_PORT_TYPE_QCDM"
+
+/**
+ * ID_MM_PORT_TYPE_GPS:
+ *
+ * This is a port-specific tag applied to TTYs that we know in advance
+ * are GPS data ports where we expect to receive NMEA traces.
+ *
+ * This tag also prevents AT and QCDM probing in the port.
+ */
+#define ID_MM_PORT_TYPE_GPS "ID_MM_PORT_TYPE_GPS"
+
+/**
+ * ID_MM_TTY_BAUDRATE:
+ *
+ * This is a port-specific tag applied to TTYs that require a specific
+ * baudrate to work. USB modems will usually allow auto-bauding
+ * configuration, so this tag is really only meaningful to true RS232
+ * devices.
+ *
+ * The value of the tag should be the number of bauds per second to
+ * use when talking to the port, e.g. "115200". If not given, the
+ * default of 57600bps is assumed.
+ */
+#define ID_MM_TTY_BAUDRATE "ID_MM_TTY_BAUDRATE"
+
+/**
+ * ID_MM_TTY_FLOW_CONTROL:
+ *
+ * This is a port-specific tag applied to TTYs that require a specific
+ * flow control mechanism to work not only in data mode but also in
+ * control mode.
+ *
+ * The value of the tag should be either 'none', 'xon-xoff' or
+ * 'rts-cts', and must be a flow control value supported by the device
+ * where it's configured. If not given, it is assumed that the TTYs
+ * don't require any specific flow control setting in command mode.
+ */
+#define ID_MM_TTY_FLOW_CONTROL "ID_MM_TTY_FLOW_CONTROL"
+
+#endif /* MM_TAGS_H */
diff --git a/libmm-glib/mm-common-helpers.c b/libmm-glib/mm-common-helpers.c
index 3470f64..67a310f 100644
--- a/libmm-glib/mm-common-helpers.c
+++ b/libmm-glib/mm-common-helpers.c
@@ -25,6 +25,66 @@
 #include "mm-errors-types.h"
 #include "mm-common-helpers.h"
 
+static gint
+_enum_from_string (GType type,
+                   const gchar *str,
+                   gint error_value,
+                   GError **error)
+{
+    GEnumClass *enum_class;
+    gint value;
+    guint i;
+
+    enum_class = G_ENUM_CLASS (g_type_class_ref (type));
+
+    for (i = 0; enum_class->values[i].value_nick; i++) {
+        if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick)) {
+            value = enum_class->values[i].value;
+            g_type_class_unref (enum_class);
+            return value;
+        }
+    }
+
+    g_set_error (error,
+                 MM_CORE_ERROR,
+                 MM_CORE_ERROR_INVALID_ARGS,
+                 "Couldn't match '%s' with a valid %s value",
+                 str,
+                 g_type_name (type));
+    g_type_class_unref (enum_class);
+    return error_value;
+}
+
+static guint
+_flags_from_string (GType type,
+                    const gchar *str,
+                    guint error_value,
+                    GError **error)
+{
+    GFlagsClass *flags_class;
+    guint value;
+    guint i;
+
+    flags_class = G_FLAGS_CLASS (g_type_class_ref (type));
+
+    for (i = 0; flags_class->values[i].value_nick; i++) {
+        if (!g_ascii_strcasecmp (str, flags_class->values[i].value_nick)) {
+            value = flags_class->values[i].value;
+            g_type_class_unref (flags_class);
+            return value;
+        }
+    }
+
+    g_set_error (error,
+                 MM_CORE_ERROR,
+                 MM_CORE_ERROR_INVALID_ARGS,
+                 "Couldn't match '%s' with a valid %s value",
+                 str,
+                 g_type_name (type));
+    g_type_class_unref (flags_class);
+    return error_value;
+}
+
 gchar *
 mm_common_build_capabilities_string (const MMModemCapability *capabilities,
                                      guint n_capabilities)
@@ -788,22 +848,10 @@
 mm_common_get_eps_ue_mode_operation_from_string (const gchar  *str,
                                                  GError      **error)
 {
-    GEnumClass *enum_class;
-    guint       i;
-
-    enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_MODEM_3GPP_EPS_UE_MODE_OPERATION));
-
-    for (i = 0; enum_class->values[i].value_nick; i++) {
-        if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
-            return enum_class->values[i].value;
-    }
-
-    g_set_error (error,
-                 MM_CORE_ERROR,
-                 MM_CORE_ERROR_INVALID_ARGS,
-                 "Couldn't match '%s' with a valid MMModem3gppEpsUeModeOperation value",
-                 str);
-    return MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN;
+    return _enum_from_string (MM_TYPE_MODEM_3GPP_EPS_UE_MODE_OPERATION,
+                              str,
+                              MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN,
+                              error);
 }
 
 GArray *
@@ -907,44 +955,20 @@
 mm_common_get_rm_protocol_from_string (const gchar *str,
                                        GError **error)
 {
-    GEnumClass *enum_class;
-    guint i;
-
-    enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_MODEM_CDMA_RM_PROTOCOL));
-
-    for (i = 0; enum_class->values[i].value_nick; i++) {
-        if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
-            return enum_class->values[i].value;
-    }
-
-    g_set_error (error,
-                 MM_CORE_ERROR,
-                 MM_CORE_ERROR_INVALID_ARGS,
-                 "Couldn't match '%s' with a valid MMModemCdmaRmProtocol value",
-                 str);
-    return MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN;
+    return _enum_from_string (MM_TYPE_MODEM_CDMA_RM_PROTOCOL,
+                              str,
+                              MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN,
+                              error);
 }
 
 MMBearerIpFamily
 mm_common_get_ip_type_from_string (const gchar *str,
                                    GError **error)
 {
-    GFlagsClass *flags_class;
-    guint i;
-
-    flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_BEARER_IP_FAMILY));
-
-    for (i = 0; flags_class->values[i].value_nick; i++) {
-        if (!g_ascii_strcasecmp (str, flags_class->values[i].value_nick))
-            return flags_class->values[i].value;
-    }
-
-    g_set_error (error,
-                 MM_CORE_ERROR,
-                 MM_CORE_ERROR_INVALID_ARGS,
-                 "Couldn't match '%s' with a valid MMBearerIpFamily value",
-                 str);
-    return MM_BEARER_IP_FAMILY_NONE;
+    return _flags_from_string (MM_TYPE_BEARER_IP_FAMILY,
+                               str,
+                               MM_BEARER_IP_FAMILY_NONE,
+                               error);
 }
 
 MMBearerAllowedAuth
@@ -1001,132 +1025,60 @@
 mm_common_get_sms_storage_from_string (const gchar *str,
                                        GError **error)
 {
-    GEnumClass *enum_class;
-    guint i;
-
-    enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_SMS_STORAGE));
-
-    for (i = 0; enum_class->values[i].value_nick; i++) {
-        if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
-            return enum_class->values[i].value;
-    }
-
-    g_set_error (error,
-                 MM_CORE_ERROR,
-                 MM_CORE_ERROR_INVALID_ARGS,
-                 "Couldn't match '%s' with a valid MMSmsStorage value",
-                 str);
-    return MM_SMS_STORAGE_UNKNOWN;
+    return _enum_from_string (MM_TYPE_SMS_STORAGE,
+                              str,
+                              MM_SMS_STORAGE_UNKNOWN,
+                              error);
 }
 
 MMSmsCdmaTeleserviceId
 mm_common_get_sms_cdma_teleservice_id_from_string (const gchar *str,
                                                    GError **error)
 {
-    GEnumClass *enum_class;
-    guint i;
-
-    enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_SMS_CDMA_TELESERVICE_ID));
-
-    for (i = 0; enum_class->values[i].value_nick; i++) {
-        if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
-            return enum_class->values[i].value;
-    }
-
-    g_set_error (error,
-                 MM_CORE_ERROR,
-                 MM_CORE_ERROR_INVALID_ARGS,
-                 "Couldn't match '%s' with a valid MMSmsCdmaTeleserviceId value",
-                 str);
-    return MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN;
+    return _enum_from_string (MM_TYPE_SMS_CDMA_TELESERVICE_ID,
+                              str,
+                              MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN,
+                              error);
 }
 
 MMSmsCdmaServiceCategory
 mm_common_get_sms_cdma_service_category_from_string (const gchar *str,
                                                      GError **error)
 {
-    GEnumClass *enum_class;
-    guint i;
-
-    enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_SMS_CDMA_SERVICE_CATEGORY));
-
-    for (i = 0; enum_class->values[i].value_nick; i++) {
-        if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
-            return enum_class->values[i].value;
-    }
-
-    g_set_error (error,
-                 MM_CORE_ERROR,
-                 MM_CORE_ERROR_INVALID_ARGS,
-                 "Couldn't match '%s' with a valid MMSmsCdmaServiceCategory value",
-                 str);
-    return MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN;
+    return _enum_from_string (MM_TYPE_SMS_CDMA_SERVICE_CATEGORY,
+                              str,
+                              MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN,
+                              error);
 }
 
 MMCallDirection
 mm_common_get_call_direction_from_string (const gchar *str,
                                           GError **error)
 {
-    GEnumClass *enum_class;
-    guint i;
-
-    enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_CALL_DIRECTION));
-
-    for (i = 0; enum_class->values[i].value_nick; i++) {
-        if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
-            return enum_class->values[i].value;
-    }
-
-    g_set_error (error,
-                 MM_CORE_ERROR,
-                 MM_CORE_ERROR_INVALID_ARGS,
-                 "Couldn't match '%s' with a valid MMCallDirection value",
-                 str);
-    return MM_CALL_DIRECTION_UNKNOWN;
+    return _enum_from_string (MM_TYPE_CALL_DIRECTION,
+                              str,
+                              MM_CALL_DIRECTION_UNKNOWN,
+                              error);
 }
 
 MMCallState
 mm_common_get_call_state_from_string (const gchar *str,
                                       GError **error)
 {
-    GEnumClass *enum_class;
-    guint i;
-
-    enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_CALL_STATE));
-
-    for (i = 0; enum_class->values[i].value_nick; i++) {
-        if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
-            return enum_class->values[i].value;
-    }
-
-    g_set_error (error,
-                 MM_CORE_ERROR,
-                 MM_CORE_ERROR_INVALID_ARGS,
-                 "Couldn't match '%s' with a valid MMCallState value",
-                 str);
-    return MM_CALL_STATE_UNKNOWN;
+    return _enum_from_string (MM_TYPE_CALL_STATE,
+                              str,
+                              MM_CALL_STATE_UNKNOWN,
+                              error);
 }
 
 MMCallStateReason
 mm_common_get_call_state_reason_from_string (const gchar *str,
                                              GError **error)
 {
-    GEnumClass *enum_class;
-    guint i;
-
-    enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_CALL_STATE_REASON));
-
-    for (i = 0; enum_class->values[i].value_nick; i++) {
-        if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
-            return enum_class->values[i].value;
-    }
-
-    g_set_error (error,
-                 MM_CORE_ERROR,
-                 MM_CORE_ERROR_INVALID_ARGS,
-                 "Couldn't match '%s' with a valid MMCallStateReason value",
-                 str);
-    return MM_CALL_STATE_REASON_UNKNOWN;
+    return _enum_from_string (MM_TYPE_CALL_STATE_REASON,
+                              str,
+                              MM_CALL_STATE_REASON_UNKNOWN,
+                              error);
 }
 
 MMOmaFeature
@@ -1183,22 +1135,10 @@
 mm_common_get_oma_session_type_from_string (const gchar *str,
                                             GError **error)
 {
-    GEnumClass *enum_class;
-    guint i;
-
-    enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_OMA_SESSION_TYPE));
-
-    for (i = 0; enum_class->values[i].value_nick; i++) {
-        if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
-            return enum_class->values[i].value;
-    }
-
-    g_set_error (error,
-                 MM_CORE_ERROR,
-                 MM_CORE_ERROR_INVALID_ARGS,
-                 "Couldn't match '%s' with a valid MMOmaSessionType value",
-                 str);
-    return MM_OMA_SESSION_TYPE_UNKNOWN;
+    return _enum_from_string (MM_TYPE_OMA_SESSION_TYPE,
+                              str,
+                              MM_OMA_SESSION_TYPE_UNKNOWN,
+                              error);
 }
 
 GVariant *
diff --git a/libmm-glib/mm-location-3gpp.c b/libmm-glib/mm-location-3gpp.c
index a4cd777..d525db7 100644
--- a/libmm-glib/mm-location-3gpp.c
+++ b/libmm-glib/mm-location-3gpp.c
@@ -42,6 +42,11 @@
     gulong location_area_code;
     gulong cell_id;
     gulong tracking_area_code;
+
+    /* We use 0 as default MNC when unknown, and that is a bit problematic if
+     * the network operator has actually a 0 MNC (e.g. China Mobile, 46000).
+     * We need to explicitly track whether MNC is set or not. */
+    gboolean mobile_network_code_set;
 };
 
 /*****************************************************************************/
@@ -84,6 +89,10 @@
  *
  * Gets the Mobile Network Code of the 3GPP network.
  *
+ * Note that 0 may actually be a valid MNC. In general, the MNC should be
+ * considered valid just if the reported MCC is valid, as MCC should never
+ * be 0.
+ *
  * Returns: the MNC, or 0 if unknown.
  */
 guint
@@ -101,9 +110,10 @@
     g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), FALSE);
 
     /* If no change in the location info, don't do anything */
-    if (self->priv->mobile_network_code == mobile_network_code)
+    if (self->priv->mobile_network_code_set && (self->priv->mobile_network_code == mobile_network_code))
         return FALSE;
 
+    self->priv->mobile_network_code_set = TRUE;
     self->priv->mobile_network_code = mobile_network_code;
     return TRUE;
 }
@@ -206,6 +216,30 @@
 
 /*****************************************************************************/
 
+gboolean
+mm_location_3gpp_reset (MMLocation3gpp *self)
+{
+    g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), FALSE);
+
+    if (self->priv->mobile_country_code == 0 &&
+        !self->priv->mobile_network_code_set &&
+        self->priv->mobile_network_code == 0 &&
+        self->priv->location_area_code == 0 &&
+        self->priv->tracking_area_code == 0 &&
+        self->priv->cell_id == 0)
+        return FALSE;
+
+    self->priv->mobile_country_code = 0;
+    self->priv->mobile_network_code_set = FALSE;
+    self->priv->mobile_network_code = 0;
+    self->priv->location_area_code = 0;
+    self->priv->tracking_area_code = 0;
+    self->priv->cell_id = 0;
+    return TRUE;
+}
+
+/*****************************************************************************/
+
 GVariant *
 mm_location_3gpp_get_string_variant (MMLocation3gpp *self)
 {
@@ -214,7 +248,7 @@
     g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), NULL);
 
     if (self->priv->mobile_country_code &&
-        self->priv->mobile_network_code &&
+        self->priv->mobile_network_code_set &&  /* MNC 0 is actually valid! */
         (self->priv->location_area_code || self->priv->tracking_area_code) &&
         self->priv->cell_id) {
         gchar *str;
diff --git a/libmm-glib/mm-location-3gpp.h b/libmm-glib/mm-location-3gpp.h
index cde055c..abcb4b8 100644
--- a/libmm-glib/mm-location-3gpp.h
+++ b/libmm-glib/mm-location-3gpp.h
@@ -84,6 +84,7 @@
                                                    gulong cell_id);
 gboolean mm_location_3gpp_set_tracking_area_code  (MMLocation3gpp *self,
                                                    gulong tracking_area_code);
+gboolean mm_location_3gpp_reset                   (MMLocation3gpp *self);
 
 #endif
 
diff --git a/src/kerneldevice/mm-kernel-device-generic.c b/src/kerneldevice/mm-kernel-device-generic.c
index 34987e9..9d956bc 100644
--- a/src/kerneldevice/mm-kernel-device-generic.c
+++ b/src/kerneldevice/mm-kernel-device-generic.c
@@ -21,6 +21,8 @@
 #define _LIBMM_INSIDE_MM
 #include <libmm-glib.h>
 
+#include <ModemManager-tags.h>
+
 #include "mm-kernel-device-generic.h"
 #include "mm-kernel-device-generic-rules.h"
 #include "mm-log.h"
@@ -486,7 +488,7 @@
         return uid;
 
     /* Try to load from properties set */
-    if ((uid = mm_kernel_device_get_property (self, "ID_MM_PHYSDEV_UID")) != NULL)
+    if ((uid = mm_kernel_device_get_property (self, ID_MM_PHYSDEV_UID)) != NULL)
         return uid;
 
     /* Use physical device path, if any */
diff --git a/src/kerneldevice/mm-kernel-device-udev.c b/src/kerneldevice/mm-kernel-device-udev.c
index 730de98..da668f3 100644
--- a/src/kerneldevice/mm-kernel-device-udev.c
+++ b/src/kerneldevice/mm-kernel-device-udev.c
@@ -18,6 +18,8 @@
 #define _LIBMM_INSIDE_MM
 #include <libmm-glib.h>
 
+#include <ModemManager-tags.h>
+
 #include "mm-kernel-device-udev.h"
 #include "mm-log.h"
 
@@ -358,7 +360,7 @@
     }
 
     /* Try to load from properties set on the physical device */
-    if ((uid = mm_kernel_device_get_global_property (MM_KERNEL_DEVICE (self), "ID_MM_PHYSDEV_UID")) != NULL)
+    if ((uid = mm_kernel_device_get_global_property (MM_KERNEL_DEVICE (self), ID_MM_PHYSDEV_UID)) != NULL)
         return uid;
 
     /* Use physical device sysfs path, if any */
diff --git a/src/mm-base-manager.c b/src/mm-base-manager.c
index 3c09089..be92b5e 100644
--- a/src/mm-base-manager.c
+++ b/src/mm-base-manager.c
@@ -30,6 +30,8 @@
 #include "mm-kernel-device-generic.h"
 
 #include <ModemManager.h>
+#include <ModemManager-tags.h>
+
 #include <mm-errors-types.h>
 #include <mm-gdbus-manager.h>
 #include <mm-gdbus-test.h>
@@ -303,8 +305,8 @@
      *
      * This udev tag applies to each port in a device. In other words, the flag
      * may be set in some ports, but not in others */
-    if (!mm_kernel_device_get_property_as_boolean (port, "ID_MM_CANDIDATE")) {
-        /* This could mean that device changed, losing its ID_MM_CANDIDATE
+    if (!mm_kernel_device_get_property_as_boolean (port, ID_MM_CANDIDATE)) {
+        /* This could mean that device changed, losing its candidate
          * flags (such as Bluetooth RFCOMM devices upon disconnect.
          * Try to forget it. */
         device_removed (manager, port);
diff --git a/src/mm-base-modem.c b/src/mm-base-modem.c
index c0ebb7f..8ac52d1 100644
--- a/src/mm-base-modem.c
+++ b/src/mm-base-modem.c
@@ -23,6 +23,8 @@
 #include <string.h>
 
 #include <ModemManager.h>
+#include <ModemManager-tags.h>
+
 #include <mm-errors-types.h>
 #include <mm-gdbus-modem.h>
 
@@ -215,13 +217,13 @@
                                                    mm_serial_parser_v1_destroy);
             /* Prefer plugin-provided flags to the generic ones */
             if (at_pflags == MM_PORT_SERIAL_AT_FLAG_NONE) {
-                if (mm_kernel_device_get_property_as_boolean (kernel_device, "ID_MM_PORT_TYPE_AT_PRIMARY")) {
+                if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_PRIMARY)) {
                     mm_dbg ("AT port '%s/%s' flagged as primary", subsys, name);
                     at_pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
-                } else if (mm_kernel_device_get_property_as_boolean (kernel_device, "ID_MM_PORT_TYPE_AT_SECONDARY")) {
+                } else if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_SECONDARY)) {
                     mm_dbg ("AT port '%s/%s' flagged as secondary", subsys, name);
                     at_pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
-                } else if (mm_kernel_device_get_property_as_boolean (kernel_device, "ID_MM_PORT_TYPE_AT_PPP")) {
+                } else if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_PPP)) {
                     mm_dbg ("AT port '%s/%s' flagged as PPP", subsys, name);
                     at_pflags = MM_PORT_SERIAL_AT_FLAG_PPP;
                 }
@@ -249,12 +251,12 @@
                               self);
 
         /* For serial ports, optionally use a specific baudrate and flow control */
-        if (mm_kernel_device_has_property (kernel_device, "ID_MM_TTY_BAUDRATE"))
+        if (mm_kernel_device_has_property (kernel_device, ID_MM_TTY_BAUDRATE))
             g_object_set (port,
-                          MM_PORT_SERIAL_BAUD, mm_kernel_device_get_property_as_int (kernel_device, "ID_MM_TTY_BAUDRATE"),
+                          MM_PORT_SERIAL_BAUD, mm_kernel_device_get_property_as_int (kernel_device, ID_MM_TTY_BAUDRATE),
                           NULL);
 
-        flow_control_tag = mm_kernel_device_get_property (kernel_device, "ID_MM_TTY_FLOW_CONTROL");
+        flow_control_tag = mm_kernel_device_get_property (kernel_device, ID_MM_TTY_FLOW_CONTROL);
         if (flow_control_tag) {
             MMFlowControl flow_control;
             GError *inner_error = NULL;
@@ -377,7 +379,7 @@
     }
     g_clear_error (&error);
 
-    g_list_free_full (disable_tasks, (GDestroyNotify)g_object_unref);
+    g_list_free_full (disable_tasks, g_object_unref);
 }
 
 void
@@ -437,7 +439,7 @@
     }
     g_clear_error (&error);
 
-    g_list_free_full (enable_tasks, (GDestroyNotify)g_object_unref);
+    g_list_free_full (enable_tasks, g_object_unref);
 }
 
 void
diff --git a/src/mm-filter.c b/src/mm-filter.c
index cf877b7..e65a628 100644
--- a/src/mm-filter.c
+++ b/src/mm-filter.c
@@ -16,6 +16,9 @@
 #include <config.h>
 #include <string.h>
 
+#include <ModemManager.h>
+#include <ModemManager-tags.h>
+
 #include "mm-daemon-enums-types.h"
 #include "mm-filter.h"
 #include "mm-log.h"
@@ -51,8 +54,8 @@
      * allow specifying this flag per-port instead of for the full device, e.g.
      * for platform tty ports where there's only one port anyway. */
     if ((self->priv->enabled_rules & MM_FILTER_RULE_EXPLICIT_WHITELIST) &&
-        (mm_kernel_device_get_global_property_as_boolean (port, "ID_MM_DEVICE_PROCESS") ||
-         mm_kernel_device_get_property_as_boolean (port, "ID_MM_DEVICE_PROCESS"))) {
+        (mm_kernel_device_get_global_property_as_boolean (port, ID_MM_DEVICE_PROCESS) ||
+         mm_kernel_device_get_property_as_boolean (port, ID_MM_DEVICE_PROCESS))) {
         mm_dbg ("[filter] (%s/%s) port allowed: device is whitelisted", subsystem, name);
         return TRUE;
     }
@@ -89,7 +92,7 @@
 
         /* Ignore blacklisted tty devices. */
         if ((self->priv->enabled_rules & MM_FILTER_RULE_TTY_BLACKLIST) &&
-            (mm_kernel_device_get_global_property_as_boolean (port, "ID_MM_DEVICE_IGNORE"))) {
+            (mm_kernel_device_get_global_property_as_boolean (port, ID_MM_DEVICE_IGNORE))) {
             mm_dbg ("[filter] (%s/%s): port filtered: device is blacklisted", subsystem, name);
             return FALSE;
         }
@@ -97,7 +100,7 @@
         /* Is the device in the manual-only greylist? If so, return if this is an
          * automatic scan. */
         if ((self->priv->enabled_rules & MM_FILTER_RULE_TTY_MANUAL_SCAN_ONLY) &&
-            (!manual_scan && mm_kernel_device_get_global_property_as_boolean (port, "ID_MM_DEVICE_MANUAL_SCAN_ONLY"))) {
+            (!manual_scan && mm_kernel_device_get_global_property_as_boolean (port, ID_MM_DEVICE_MANUAL_SCAN_ONLY))) {
             mm_dbg ("[filter] (%s/%s): port filtered: device probed only in manual scan", subsystem, name);
             return FALSE;
         }
@@ -111,7 +114,7 @@
              !g_strcmp0 (physdev_subsystem, "pci") ||
              !g_strcmp0 (physdev_subsystem, "pnp") ||
              !g_strcmp0 (physdev_subsystem, "sdio"))) {
-            if (!mm_kernel_device_get_global_property_as_boolean (port, "ID_MM_PLATFORM_DRIVER_PROBE")) {
+            if (!mm_kernel_device_get_global_property_as_boolean (port, ID_MM_PLATFORM_DRIVER_PROBE)) {
                 mm_dbg ("[filter] (%s/%s): port filtered: port's parent platform driver is not whitelisted", subsystem, name);
                 return FALSE;
             }
diff --git a/src/mm-iface-modem-location.c b/src/mm-iface-modem-location.c
index 88fd2bf..2ccb643 100644
--- a/src/mm-iface-modem-location.c
+++ b/src/mm-iface-modem-location.c
@@ -383,15 +383,8 @@
         return;
 
     if (mm_gdbus_modem_location_get_enabled (skeleton) & MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI) {
-        guint changed = 0;
-
         g_assert (ctx->location_3gpp != NULL);
-        changed += mm_location_3gpp_set_location_area_code (ctx->location_3gpp, 0);
-        changed += mm_location_3gpp_set_tracking_area_code (ctx->location_3gpp, 0);
-        changed += mm_location_3gpp_set_cell_id (ctx->location_3gpp, 0);
-        changed += mm_location_3gpp_set_mobile_country_code (ctx->location_3gpp, 0);
-        changed += mm_location_3gpp_set_mobile_network_code (ctx->location_3gpp, 0);
-        if (changed)
+        if (mm_location_3gpp_reset (ctx->location_3gpp))
             notify_3gpp_location_update (self, skeleton, ctx->location_3gpp);
     }
 
diff --git a/src/mm-iface-modem-messaging.c b/src/mm-iface-modem-messaging.c
index 7051290..8b3ac8c 100644
--- a/src/mm-iface-modem-messaging.c
+++ b/src/mm-iface-modem-messaging.c
@@ -896,6 +896,27 @@
     return default_storages_preference[i];
 }
 
+static MMSmsStorage
+get_single_default_sms_storage (MMIfaceModemMessaging *self)
+{
+    StorageContext *storage_ctx;
+
+    storage_ctx = get_storage_context (self);
+
+    /* If there is one single storage supported for storing and receiving, just
+     * use that one. */
+    if (storage_ctx->supported_mem2 && storage_ctx->supported_mem2->len == 1 &&
+        storage_ctx->supported_mem3 && storage_ctx->supported_mem3->len == 1) {
+        MMSmsStorage storing_default;
+
+        storing_default = g_array_index (storage_ctx->supported_mem2, MMSmsStorage, 0);
+        if (storing_default == g_array_index (storage_ctx->supported_mem3, MMSmsStorage, 0))
+            return storing_default;
+    }
+
+    return MM_SMS_STORAGE_UNKNOWN;
+}
+
 static void
 interface_enabling_step (GTask *task)
 {
@@ -949,32 +970,35 @@
         /* Fall down to next step */
         ctx->step++;
 
-    case ENABLING_STEP_STORAGE_DEFAULTS:
-        /* Set storage defaults */
-        if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage &&
-            MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage_finish) {
-            MMSmsStorage default_storage;
+    case ENABLING_STEP_STORAGE_DEFAULTS: {
+        MMSmsStorage default_storage;
 
+        /* Is there only one single storage supported? if so, we don't care if
+         * setting default storage is implemented or not. */
+        default_storage = get_single_default_sms_storage (self);
+        if (default_storage == MM_SMS_STORAGE_UNKNOWN)
             default_storage = get_best_initial_default_sms_storage (self);
 
-            /* Already bound to the 'default-storage' property in the skeleton */
-            g_object_set (self,
-                          MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE, default_storage,
-                          NULL);
+        /* Already bound to the 'default-storage' property in the skeleton */
+        g_object_set (self,
+                      MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE, default_storage,
+                      NULL);
 
-            if (default_storage != MM_SMS_STORAGE_UNKNOWN) {
-                MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage (
-                    self,
-                    default_storage,
-                    (GAsyncReadyCallback)set_default_storage_ready,
-                    task);
-                return;
-            }
-
+        if (default_storage == MM_SMS_STORAGE_UNKNOWN)
             mm_info ("Cannot set default storage, none of the suggested ones supported");
+        else if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage &&
+                 MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage_finish) {
+            MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage (
+                self,
+                default_storage,
+                (GAsyncReadyCallback)set_default_storage_ready,
+                task);
+            return;
         }
+
         /* Fall down to next step */
         ctx->step++;
+    }
 
     case ENABLING_STEP_LOAD_INITIAL_SMS_PARTS:
         /* Allow loading the initial list of SMS parts */
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index 35465d9..a1c7e9a 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -652,15 +652,21 @@
                              GError      **error)
 {
     GFlagsClass *flags_class;
+    guint value;
     guint i;
 
     flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_FLOW_CONTROL));
 
     for (i = 0; flags_class->values[i].value_nick; i++) {
-        if (!g_ascii_strcasecmp (str, flags_class->values[i].value_nick))
-            return flags_class->values[i].value;
+        if (!g_ascii_strcasecmp (str, flags_class->values[i].value_nick)) {
+            value = flags_class->values[i].value;
+            g_type_class_unref (flags_class);
+            return value;
+        }
     }
 
+    g_type_class_unref (flags_class);
+
     g_set_error (error,
                  MM_CORE_ERROR,
                  MM_CORE_ERROR_INVALID_ARGS,
diff --git a/src/mm-port-probe.c b/src/mm-port-probe.c
index fd66d20..9913001 100644
--- a/src/mm-port-probe.c
+++ b/src/mm-port-probe.c
@@ -22,6 +22,8 @@
 #include <string.h>
 
 #include <ModemManager.h>
+#include <ModemManager-tags.h>
+
 #include <mm-errors-types.h>
 
 #include "mm-port-probe.h"
@@ -637,12 +639,12 @@
 {
     const gchar *flow_control_tag;
 
-    if (mm_kernel_device_has_property (self->priv->port, "ID_MM_TTY_BAUDRATE"))
+    if (mm_kernel_device_has_property (self->priv->port, ID_MM_TTY_BAUDRATE))
         g_object_set (serial,
-                      MM_PORT_SERIAL_BAUD, mm_kernel_device_get_property_as_int (self->priv->port, "ID_MM_TTY_BAUDRATE"),
+                      MM_PORT_SERIAL_BAUD, mm_kernel_device_get_property_as_int (self->priv->port, ID_MM_TTY_BAUDRATE),
                       NULL);
 
-    flow_control_tag = mm_kernel_device_get_property (self->priv->port, "ID_MM_TTY_FLOW_CONTROL");
+    flow_control_tag = mm_kernel_device_get_property (self->priv->port, ID_MM_TTY_FLOW_CONTROL);
     if (flow_control_tag) {
         MMFlowControl flow_control;
         GError *error = NULL;
@@ -1896,12 +1898,12 @@
     case PROP_PORT:
         /* construct only */
         self->priv->port = g_value_dup_object (value);
-        self->priv->is_ignored = mm_kernel_device_get_property_as_boolean (self->priv->port, "ID_MM_PORT_IGNORE");
-        self->priv->is_gps = mm_kernel_device_get_property_as_boolean (self->priv->port, "ID_MM_PORT_TYPE_GPS");
-        self->priv->maybe_at_primary = mm_kernel_device_get_property_as_boolean (self->priv->port, "ID_MM_PORT_TYPE_AT_PRIMARY");
-        self->priv->maybe_at_secondary = mm_kernel_device_get_property_as_boolean (self->priv->port, "ID_MM_PORT_TYPE_AT_SECONDARY");
-        self->priv->maybe_at_ppp = mm_kernel_device_get_property_as_boolean (self->priv->port, "ID_MM_PORT_TYPE_AT_PPP");
-        self->priv->maybe_qcdm = mm_kernel_device_get_property_as_boolean (self->priv->port, "ID_MM_PORT_TYPE_QCDM");
+        self->priv->is_ignored = mm_kernel_device_get_property_as_boolean (self->priv->port, ID_MM_PORT_IGNORE);
+        self->priv->is_gps = mm_kernel_device_get_property_as_boolean (self->priv->port, ID_MM_PORT_TYPE_GPS);
+        self->priv->maybe_at_primary = mm_kernel_device_get_property_as_boolean (self->priv->port, ID_MM_PORT_TYPE_AT_PRIMARY);
+        self->priv->maybe_at_secondary = mm_kernel_device_get_property_as_boolean (self->priv->port, ID_MM_PORT_TYPE_AT_SECONDARY);
+        self->priv->maybe_at_ppp = mm_kernel_device_get_property_as_boolean (self->priv->port, ID_MM_PORT_TYPE_AT_PPP);
+        self->priv->maybe_qcdm = mm_kernel_device_get_property_as_boolean (self->priv->port, ID_MM_PORT_TYPE_QCDM);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
diff --git a/src/mm-port-serial.c b/src/mm-port-serial.c
index 38d4e47..41a5fc2 100644
--- a/src/mm-port-serial.c
+++ b/src/mm-port-serial.c
@@ -111,8 +111,8 @@
 
     guint connected_id;
 
-    gpointer flash_ctx;
-    gpointer reopen_ctx;
+    GTask *flash_task;
+    GTask *reopen_task;
 };
 
 /*****************************************************************************/
@@ -1166,7 +1166,7 @@
         return FALSE;
     }
 
-    if (self->priv->reopen_ctx) {
+    if (self->priv->reopen_task) {
         g_set_error (error,
                      MM_SERIAL_ERROR,
                      MM_SERIAL_ERROR_OPEN_FAILED,
@@ -1516,20 +1516,15 @@
 /* Reopen */
 
 typedef struct {
-    MMPortSerial *self;
-    GSimpleAsyncResult *result;
     guint initial_open_count;
     guint reopen_id;
 } ReopenContext;
 
 static void
-reopen_context_complete_and_free (ReopenContext *ctx)
+reopen_context_free (ReopenContext *ctx)
 {
     if (ctx->reopen_id)
         g_source_remove (ctx->reopen_id);
-    g_simple_async_result_complete_in_idle (ctx->result);
-    g_object_unref (ctx->result);
-    g_object_unref (ctx->self);
     g_slice_free (ReopenContext, ctx);
 }
 
@@ -1538,54 +1533,56 @@
                               GAsyncResult *res,
                               GError **error)
 {
-    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+    return g_task_propagate_boolean (G_TASK (res), error);
 }
 
 static void
 port_serial_reopen_cancel (MMPortSerial *self)
 {
-    ReopenContext *ctx;
+    GTask *task;
 
-    if (!self->priv->reopen_ctx)
+    if (!self->priv->reopen_task)
         return;
 
-    /* Recover context */
-    ctx = (ReopenContext *)self->priv->reopen_ctx;
-    self->priv->reopen_ctx = NULL;
+    /* Recover task */
+    task = self->priv->reopen_task;
+    self->priv->reopen_task = NULL;
 
-    g_simple_async_result_set_error (ctx->result,
-                                     MM_CORE_ERROR,
-                                     MM_CORE_ERROR_CANCELLED,
-                                     "Reopen cancelled");
-    reopen_context_complete_and_free (ctx);
+    g_task_return_new_error (task,
+                             MM_CORE_ERROR,
+                             MM_CORE_ERROR_CANCELLED,
+                             "Reopen cancelled");
+    g_object_unref (task);
 }
 
 static gboolean
 reopen_do (MMPortSerial *self)
 {
+    GTask *task;
     ReopenContext *ctx;
     GError *error = NULL;
     guint i;
 
-    /* Recover context */
-    g_assert (self->priv->reopen_ctx != NULL);
-    ctx = (ReopenContext *)self->priv->reopen_ctx;
-    self->priv->reopen_ctx = NULL;
+    /* Recover task */
+    g_assert (self->priv->reopen_task != NULL);
+    task = self->priv->reopen_task;
+    self->priv->reopen_task = NULL;
 
+    ctx = g_task_get_task_data (task);
     ctx->reopen_id = 0;
 
     for (i = 0; i < ctx->initial_open_count; i++) {
-        if (!mm_port_serial_open (ctx->self, &error)) {
+        if (!mm_port_serial_open (self, &error)) {
             g_prefix_error (&error, "Couldn't reopen port (%u): ", i);
             break;
         }
     }
 
     if (error)
-        g_simple_async_result_take_error (ctx->result, error);
+        g_task_return_error (task, error);
     else
-        g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
-    reopen_context_complete_and_free (ctx);
+        g_task_return_boolean (task, TRUE);
+    g_object_unref (task);
 
     return G_SOURCE_REMOVE;
 }
@@ -1597,35 +1594,34 @@
                        gpointer user_data)
 {
     ReopenContext *ctx;
+    GTask *task;
     guint i;
 
     g_return_if_fail (MM_IS_PORT_SERIAL (self));
 
     /* Setup context */
     ctx = g_slice_new0 (ReopenContext);
-    ctx->self = g_object_ref (self);
-    ctx->result = g_simple_async_result_new (G_OBJECT (self),
-                                             callback,
-                                             user_data,
-                                             mm_port_serial_reopen);
     ctx->initial_open_count = self->priv->open_count;
 
+    task = g_task_new (self, NULL, callback, user_data);
+    g_task_set_task_data (task, ctx, (GDestroyNotify)reopen_context_free);
+
     if (self->priv->forced_close) {
-        g_simple_async_result_set_error (ctx->result,
-                                         MM_CORE_ERROR,
-                                         MM_CORE_ERROR_FAILED,
-                                         "Serial port has been forced close.");
-        reopen_context_complete_and_free (ctx);
+        g_task_return_new_error (task,
+                                 MM_CORE_ERROR,
+                                 MM_CORE_ERROR_FAILED,
+                                 "Serial port has been forced close.");
+        g_object_unref (task);
         return;
     }
 
     /* If already reopening, halt */
-    if (self->priv->reopen_ctx) {
-        g_simple_async_result_set_error (ctx->result,
-                                         MM_CORE_ERROR,
-                                         MM_CORE_ERROR_IN_PROGRESS,
-                                         "Modem is already being reopened");
-        reopen_context_complete_and_free (ctx);
+    if (self->priv->reopen_task) {
+        g_task_return_new_error (task,
+                                 MM_CORE_ERROR,
+                                 MM_CORE_ERROR_IN_PROGRESS,
+                                 "Modem is already being reopened");
+        g_object_unref (task);
         return;
     }
 
@@ -1642,7 +1638,7 @@
         ctx->reopen_id = g_idle_add ((GSourceFunc)reopen_do, self);
 
     /* Store context in private info */
-    self->priv->reopen_ctx = ctx;
+    self->priv->reopen_task = task;
 }
 
 static gboolean
@@ -1694,20 +1690,15 @@
 /* Flash */
 
 typedef struct {
-    GSimpleAsyncResult *result;
-    MMPortSerial *self;
     speed_t current_speed;
     guint flash_id;
 } FlashContext;
 
 static void
-flash_context_complete_and_free (FlashContext *ctx)
+flash_context_free (FlashContext *ctx)
 {
     if (ctx->flash_id)
         g_source_remove (ctx->flash_id);
-    g_simple_async_result_complete_in_idle (ctx->result);
-    g_object_unref (ctx->result);
-    g_object_unref (ctx->self);
     g_slice_free (FlashContext, ctx);
 }
 
@@ -1716,44 +1707,46 @@
                              GAsyncResult *res,
                              GError **error)
 {
-    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+    return g_task_propagate_boolean (G_TASK (res), error);
 }
 
 void
 mm_port_serial_flash_cancel (MMPortSerial *self)
 {
-    FlashContext *ctx;
+    GTask *task;
 
-    if (!self->priv->flash_ctx)
+    if (!self->priv->flash_task)
         return;
 
-    /* Recover context */
-    ctx = (FlashContext *)self->priv->flash_ctx;
-    self->priv->flash_ctx = NULL;
+    /* Recover task */
+    task = self->priv->flash_task;
+    self->priv->flash_task = NULL;
 
-    g_simple_async_result_set_error (ctx->result,
-                                     MM_CORE_ERROR,
-                                     MM_CORE_ERROR_CANCELLED,
-                                     "Flash cancelled");
-    flash_context_complete_and_free (ctx);
+    g_task_return_new_error (task,
+                             MM_CORE_ERROR,
+                             MM_CORE_ERROR_CANCELLED,
+                             "Flash cancelled");
+    g_object_unref (task);
 }
 
 static gboolean
 flash_do (MMPortSerial *self)
 {
+    GTask *task;
     FlashContext *ctx;
     GError *error = NULL;
 
-    /* Recover context */
-    g_assert (self->priv->flash_ctx != NULL);
-    ctx = (FlashContext *)self->priv->flash_ctx;
-    self->priv->flash_ctx = NULL;
+    /* Recover task */
+    g_assert (self->priv->flash_task != NULL);
+    task = self->priv->flash_task;
+    self->priv->flash_task = NULL;
 
+    ctx = g_task_get_task_data (task);
     ctx->flash_id = 0;
 
     if (self->priv->flash_ok && mm_port_get_subsys (MM_PORT (self)) == MM_PORT_SUBSYS_TTY) {
         if (ctx->current_speed) {
-            if (!set_speed (ctx->self, ctx->current_speed, &error))
+            if (!set_speed (self, ctx->current_speed, &error))
                 g_assert (error);
         } else {
             error = g_error_new_literal (MM_SERIAL_ERROR,
@@ -1763,10 +1756,10 @@
     }
 
     if (error)
-        g_simple_async_result_take_error (ctx->result, error);
+        g_task_return_error (task, error);
     else
-        g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
-    flash_context_complete_and_free (ctx);
+        g_task_return_boolean (task, TRUE);
+    g_object_unref (task);
 
     return G_SOURCE_REMOVE;
 }
@@ -1779,6 +1772,7 @@
                       gpointer user_data)
 {
     FlashContext *ctx;
+    GTask *task;
     GError *error = NULL;
     gboolean success;
 
@@ -1786,33 +1780,31 @@
 
     /* Setup context */
     ctx = g_slice_new0 (FlashContext);
-    ctx->self = g_object_ref (self);
-    ctx->result = g_simple_async_result_new (G_OBJECT (self),
-                                             callback,
-                                             user_data,
-                                             mm_port_serial_flash);
+
+    task = g_task_new (self, NULL, callback, user_data);
+    g_task_set_task_data (task, ctx, (GDestroyNotify)flash_context_free);
 
     if (!mm_port_serial_is_open (self)) {
-        g_simple_async_result_set_error (ctx->result,
-                                         MM_SERIAL_ERROR,
-                                         MM_SERIAL_ERROR_NOT_OPEN,
-                                         "The serial port is not open.");
-        flash_context_complete_and_free (ctx);
+        g_task_return_new_error (task,
+                                 MM_SERIAL_ERROR,
+                                 MM_SERIAL_ERROR_NOT_OPEN,
+                                 "The serial port is not open.");
+        g_object_unref (task);
         return;
     }
 
-    if (self->priv->flash_ctx) {
-        g_simple_async_result_set_error (ctx->result,
-                                         MM_CORE_ERROR,
-                                         MM_CORE_ERROR_IN_PROGRESS,
-                                         "Modem is already being flashed.");
-        flash_context_complete_and_free (ctx);
+    if (self->priv->flash_task) {
+        g_task_return_new_error (task,
+                                 MM_CORE_ERROR,
+                                 MM_CORE_ERROR_IN_PROGRESS,
+                                 "Modem is already being flashed.");
+        g_object_unref (task);
         return;
     }
 
     /* Flashing only in TTY */
     if (!self->priv->flash_ok || mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_TTY) {
-        self->priv->flash_ctx = ctx;
+        self->priv->flash_task = task;
         ctx->flash_id = g_idle_add ((GSourceFunc)flash_do, self);
         return;
     }
@@ -1820,21 +1812,21 @@
     /* Grab current speed so we can reset it after flashing */
     success = get_speed (self, &ctx->current_speed, &error);
     if (!success && !ignore_errors) {
-        g_simple_async_result_take_error (ctx->result, error);
-        flash_context_complete_and_free (ctx);
+        g_task_return_error (task, error);
+        g_object_unref (task);
         return;
     }
     g_clear_error (&error);
 
     success = set_speed (self, B0, &error);
     if (!success && !ignore_errors) {
-        g_simple_async_result_take_error (ctx->result, error);
-        flash_context_complete_and_free (ctx);
+        g_task_return_error (task, error);
+        g_object_unref (task);
         return;
     }
     g_clear_error (&error);
 
-    self->priv->flash_ctx = ctx;
+    self->priv->flash_task = task;
     ctx->flash_id = g_timeout_add (flash_time, (GSourceFunc)flash_do, self);
 }