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

Change-Id: I22368813ff52c203507d5a72cbed75d3769e2be5
diff --git a/libmm-common/mm-bearer-properties.c b/libmm-common/mm-bearer-properties.c
index 6475ba7..8eb665c 100644
--- a/libmm-common/mm-bearer-properties.c
+++ b/libmm-common/mm-bearer-properties.c
@@ -440,6 +440,22 @@
 
 /*****************************************************************************/
 
+gboolean
+mm_bearer_properties_cmp (MMBearerProperties *a,
+                          MMBearerProperties *b)
+{
+    return ((!g_strcmp0 (a->priv->apn, b->priv->apn)) &&
+            (!g_strcmp0 (a->priv->ip_type, b->priv->ip_type)) &&
+            (!g_strcmp0 (a->priv->number, b->priv->number)) &&
+            (!g_strcmp0 (a->priv->user, b->priv->user)) &&
+            (!g_strcmp0 (a->priv->password, b->priv->password)) &&
+            (a->priv->allow_roaming == b->priv->allow_roaming) &&
+            (a->priv->allow_roaming_set == b->priv->allow_roaming_set) &&
+            (a->priv->rm_protocol == b->priv->rm_protocol));
+}
+
+/*****************************************************************************/
+
 MMBearerProperties *
 mm_bearer_properties_new (void)
 {
@@ -457,6 +473,13 @@
     /* Some defaults */
     self->priv->allow_roaming = TRUE;
     self->priv->rm_protocol = MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN;
+
+    /* At some point in the future, this default should probably be changed
+     * to IPV4V6. However, presently support for this PDP type is rare. An
+     * even better approach would likely be to query which PDP types the
+     * modem supports (using AT+CGDCONT=?), and set the default accordingly
+     */
+    self->priv->ip_type = g_strdup ("IP");
 }
 
 static void
diff --git a/libmm-common/mm-bearer-properties.h b/libmm-common/mm-bearer-properties.h
index 407eb0e..3288c33 100644
--- a/libmm-common/mm-bearer-properties.h
+++ b/libmm-common/mm-bearer-properties.h
@@ -86,6 +86,9 @@
 
 GVariant *mm_bearer_properties_get_dictionary (MMBearerProperties *self);
 
+gboolean mm_bearer_properties_cmp (MMBearerProperties *a,
+                                   MMBearerProperties *b);
+
 G_END_DECLS
 
 #endif /* MM_BEARER_PROPERTIES_H */
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index da960a8..af642b4 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -49,7 +49,8 @@
 	libmm-plugin-hso.la \
 	libmm-plugin-anydata.la \
 	libmm-plugin-linktop.la \
-	libmm-plugin-simtech.la
+	libmm-plugin-simtech.la \
+	libmm-plugin-wavecom.la
 
 #pkglib_LTLIBRARIES = \
 #	libmm-plugin-generic.la \
@@ -150,15 +151,15 @@
 #libmm_plugin_sierra_la_LIBADD = \
 #        $(builddir)/libicera-utils.la
 
-## Wavecom (Sierra Airlink)
-#libmm_plugin_wavecom_la_SOURCES = \
-#	mm-plugin-wavecom.c \
-#	mm-plugin-wavecom.h \
-#	mm-modem-wavecom-gsm.c \
-#	mm-modem-wavecom-gsm.h
-#libmm_plugin_wavecom_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-#libmm_plugin_wavecom_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-#
+# Wavecom (Sierra Airlink)
+libmm_plugin_wavecom_la_SOURCES = \
+	wavecom/mm-plugin-wavecom.c \
+	wavecom/mm-plugin-wavecom.h \
+	wavecom/mm-broadband-modem-wavecom.c \
+	wavecom/mm-broadband-modem-wavecom.h
+libmm_plugin_wavecom_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
+libmm_plugin_wavecom_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
 ## Novatel
 #libmm_plugin_novatel_la_SOURCES = \
 #	mm-plugin-novatel.c \
diff --git a/plugins/cinterion/mm-broadband-modem-cinterion.c b/plugins/cinterion/mm-broadband-modem-cinterion.c
index a3d35e8..d4d1bcf 100644
--- a/plugins/cinterion/mm-broadband-modem-cinterion.c
+++ b/plugins/cinterion/mm-broadband-modem-cinterion.c
@@ -749,7 +749,7 @@
                     if (G_UNLIKELY (!bands_array))
                         bands_array = g_array_new (FALSE, FALSE, sizeof (MMModemBand));
 
-                    for (j = 0; j < 4 && bands_2g[i].mm_bands[j] != 0; j++)
+                    for (j = 0; j < bands_2g[i].n_mm_bands; j++)
                         g_array_append_val (bands_array, bands_2g[i].mm_bands[j]);
 
                     break;
diff --git a/plugins/gobi/mm-broadband-modem-gobi.c b/plugins/gobi/mm-broadband-modem-gobi.c
index 01568b4..6c71262 100644
--- a/plugins/gobi/mm-broadband-modem-gobi.c
+++ b/plugins/gobi/mm-broadband-modem-gobi.c
@@ -59,7 +59,7 @@
     p = strchr (p, ',');
     if (p) {
         /* We are reporting ALL 3GPP access technologies here */
-        *access_technologies = mm_3gpp_string_to_access_tech (p + 1);
+        *access_technologies = mm_string_to_access_tech (p + 1);
         *mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK;
         return TRUE;
     }
diff --git a/plugins/iridium/mm-bearer-iridium.c b/plugins/iridium/mm-bearer-iridium.c
index 1caa68e..93936c2 100644
--- a/plugins/iridium/mm-bearer-iridium.c
+++ b/plugins/iridium/mm-bearer-iridium.c
@@ -385,21 +385,9 @@
 
 /*****************************************************************************/
 
-static gboolean
-cmp_properties (MMBearer *self,
-                MMBearerProperties *properties)
-{
-    return (mm_bearer_properties_get_apn (properties) == NULL &&
-            mm_bearer_properties_get_ip_type (properties) == NULL &&
-            mm_bearer_properties_get_number (properties) == NULL &&
-            mm_bearer_properties_get_rm_protocol (properties) == MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN &&
-            mm_bearer_properties_get_allow_roaming (properties));
-}
-
-/*****************************************************************************/
-
 MMBearer *
-mm_bearer_iridium_new (MMBroadbandModemIridium *modem)
+mm_bearer_iridium_new (MMBroadbandModemIridium *modem,
+                       MMBearerProperties *config)
 {
     MMBearer *bearer;
 
@@ -408,6 +396,7 @@
      * g_object_get() here */
     bearer = g_object_new (MM_TYPE_BEARER_IRIDIUM,
                            MM_BEARER_MODEM, modem,
+                           MM_BEARER_CONFIG, config,
                            "ip-timeout", MM_BEARER_IRIDIUM_IP_TIMEOUT_DEFAULT,
                            NULL);
 
@@ -428,7 +417,6 @@
     MMBearerClass *bearer_class = MM_BEARER_CLASS (klass);
 
     /* Virtual methods */
-    bearer_class->cmp_properties = cmp_properties;
     bearer_class->connect = connect;
     bearer_class->connect_finish = connect_finish;
     bearer_class->disconnect = disconnect;
diff --git a/plugins/iridium/mm-bearer-iridium.h b/plugins/iridium/mm-bearer-iridium.h
index ccb3307..191de0f 100644
--- a/plugins/iridium/mm-bearer-iridium.h
+++ b/plugins/iridium/mm-bearer-iridium.h
@@ -48,6 +48,7 @@
 
 /* Iridium bearer creation implementation.
  * NOTE it is *not* a broadband bearer, so not async-initable */
-MMBearer *mm_bearer_iridium_new (MMBroadbandModemIridium *modem);
+MMBearer *mm_bearer_iridium_new (MMBroadbandModemIridium *modem,
+                                 MMBearerProperties *config);
 
 #endif /* MM_BEARER_IRIDIUM_H */
diff --git a/plugins/iridium/mm-broadband-modem-iridium.c b/plugins/iridium/mm-broadband-modem-iridium.c
index 4ea8f06..1235ed2 100644
--- a/plugins/iridium/mm-broadband-modem-iridium.c
+++ b/plugins/iridium/mm-broadband-modem-iridium.c
@@ -360,10 +360,9 @@
                                         callback,
                                         user_data,
                                         create_bearer);
-    /* We just create a MMBearerIridium
-     * Note that we do not need to use properties here */
     mm_dbg ("Creating Iridium bearer...");
-    bearer = mm_bearer_iridium_new (MM_BROADBAND_MODEM_IRIDIUM (self));
+    bearer = mm_bearer_iridium_new (MM_BROADBAND_MODEM_IRIDIUM (self),
+                                    properties);
     g_simple_async_result_set_op_res_gpointer (result,
                                                bearer,
                                                (GDestroyNotify)g_object_unref);
diff --git a/plugins/mm-modem-wavecom-gsm.c b/plugins/mm-modem-wavecom-gsm.c
deleted file mode 100644
index 8b65426..0000000
--- a/plugins/mm-modem-wavecom-gsm.c
+++ /dev/null
@@ -1,1174 +0,0 @@
-/* -*- 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) 2011 Ammonit Measurement GmbH
- * Author: Aleksander Morgado <aleksander@lanedo.com>
- */
-
-#include <config.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "mm-errors.h"
-#include "mm-modem-helpers.h"
-#include "mm-modem-wavecom-gsm.h"
-#include "mm-serial-parsers.h"
-#include "mm-log.h"
-
-static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class);
-
-G_DEFINE_TYPE_EXTENDED (MMModemWavecomGsm, mm_modem_wavecom_gsm, MM_TYPE_GENERIC_GSM, 0,
-                        G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init))
-
-/* Bit flags for mobile station classes supported by the modem */
-typedef enum {
-    WAVECOM_MS_CLASS_UNKNOWN = 0,
-    /* Class C in circuit switched only mode, CS */
-    WAVECOM_MS_CLASS_CC = 1 << 0,
-    /* Class C in GPRS only mode, PS */
-    WAVECOM_MS_CLASS_CG = 1 << 1,
-    /* Class B (either CS or PS, not both at the same time)
-     * This should be the default for GSM/GPRS modems */
-    WAVECOM_MS_CLASS_B  = 1 << 2,
-    /* Class A in 3G only mode */
-    WAVECOM_MS_CLASS_A  = 1 << 3
-} WavecomMSClass;
-
-#define WAVECOM_MS_CLASS_CC_IDSTR "\"CC\""
-#define WAVECOM_MS_CLASS_CG_IDSTR "\"CG\""
-#define WAVECOM_MS_CLASS_B_IDSTR  "\"B\""
-#define WAVECOM_MS_CLASS_A_IDSTR  "\"A\""
-
-/* Mask of all supported 2G bands */
-#define ALL_2G_BANDS          \
-    (MM_MODEM_GSM_BAND_EGSM | \
-     MM_MODEM_GSM_BAND_DCS |  \
-     MM_MODEM_GSM_BAND_PCS |  \
-     MM_MODEM_GSM_BAND_G850)
-
-/* Mask of all supported 3G bands */
-#define ALL_3G_BANDS            \
-    (MM_MODEM_GSM_BAND_U2100 |  \
-     MM_MODEM_GSM_BAND_U1800 |  \
-     MM_MODEM_GSM_BAND_U17IV |  \
-     MM_MODEM_GSM_BAND_U800 |   \
-     MM_MODEM_GSM_BAND_U850 |   \
-     MM_MODEM_GSM_BAND_U900 |   \
-     MM_MODEM_GSM_BAND_U17IX |  \
-     MM_MODEM_GSM_BAND_U1900 |  \
-     MM_MODEM_GSM_BAND_U2600)
-
-#define MM_MODEM_WAVECOM_GSM_GET_PRIVATE(o)                             \
-    (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_WAVECOM_GSM, MMModemWavecomGsmPrivate))
-
-typedef struct {
-    /* Bitmask for supported MS classes */
-    guint8 supported_ms_classes;
-    /* Current MS class */
-    WavecomMSClass current_ms_class;
-    /* Current allowed mode, only for 3G devices */
-    MMModemGsmAllowedMode allowed_mode;
-    /* Bitmask for currently active bands */
-    guint32 current_bands;
-} MMModemWavecomGsmPrivate;
-
-/* Setup relationship between 2G bands in the modem (identified by a
- * single digit in ASCII) and the bitmask in ModemManager. */
-typedef struct {
-    gchar   wavecom_band;
-    guint32 mm_band_mask;
-} WavecomBand2G;
-static const WavecomBand2G bands_2g[] = {
-    { '0', MM_MODEM_GSM_BAND_G850 },
-    { '1', MM_MODEM_GSM_BAND_EGSM },
-    { '2', MM_MODEM_GSM_BAND_DCS  },
-    { '3', MM_MODEM_GSM_BAND_PCS  },
-    { '4', (MM_MODEM_GSM_BAND_G850 | MM_MODEM_GSM_BAND_PCS) },
-    { '5', (MM_MODEM_GSM_BAND_EGSM | MM_MODEM_GSM_BAND_DCS) },
-    { '6', (MM_MODEM_GSM_BAND_EGSM | MM_MODEM_GSM_BAND_PCS) },
-    { '7', ALL_2G_BANDS }
-};
-
-/* Setup relationship between the 3G band bitmask in the modem and the bitmask
- * in ModemManager. */
-typedef struct {
-    guint32 wavecom_band_flag;
-    guint32 mm_band_flag;
-} WavecomBand3G;
-static const WavecomBand3G bands_3g[] = {
-    { (1 << 0), MM_MODEM_GSM_BAND_U2100 },
-    { (1 << 1), MM_MODEM_GSM_BAND_U1900 },
-    { (1 << 2), MM_MODEM_GSM_BAND_U1800 },
-    { (1 << 3), MM_MODEM_GSM_BAND_U17IV },
-    { (1 << 4), MM_MODEM_GSM_BAND_U850  },
-    { (1 << 5), MM_MODEM_GSM_BAND_U800  },
-    { (1 << 6), MM_MODEM_GSM_BAND_U2600 },
-    { (1 << 7), MM_MODEM_GSM_BAND_U900  },
-    { (1 << 8), MM_MODEM_GSM_BAND_U17IX }
-};
-
-MMModem *
-mm_modem_wavecom_gsm_new (const char *device,
-                          const char *driver,
-                          const char *plugin,
-                          guint32 vendor,
-                          guint32 product)
-{
-    g_return_val_if_fail (device != NULL, NULL);
-    g_return_val_if_fail (driver != NULL, NULL);
-    g_return_val_if_fail (plugin != NULL, NULL);
-
-    return MM_MODEM (g_object_new (MM_TYPE_MODEM_WAVECOM_GSM,
-                                   MM_MODEM_MASTER_DEVICE, device,
-                                   MM_MODEM_DRIVER, driver,
-                                   MM_MODEM_PLUGIN, plugin,
-                                   MM_MODEM_HW_VID, vendor,
-                                   MM_MODEM_HW_PID, product,
-                                   NULL));
-}
-
-static const gchar *
-wavecom_ms_class_to_str (WavecomMSClass class)
-{
-    switch (class) {
-    case WAVECOM_MS_CLASS_CC:
-        return WAVECOM_MS_CLASS_CC_IDSTR;
-    case WAVECOM_MS_CLASS_CG:
-        return WAVECOM_MS_CLASS_CG_IDSTR;
-    case WAVECOM_MS_CLASS_B:
-        return WAVECOM_MS_CLASS_B_IDSTR;
-    case WAVECOM_MS_CLASS_A:
-        return WAVECOM_MS_CLASS_A_IDSTR;
-    default:
-        g_warn_if_reached ();
-        return NULL;
-    }
-}
-
-static void
-port_grabbed (MMGenericGsm *gsm,
-              MMPort *port,
-              MMAtPortFlags pflags,
-              gpointer user_data)
-{
-    gpointer parser;
-    GRegex *regex;
-
-    if (MM_IS_AT_SERIAL_PORT (port)) {
-        parser = mm_serial_parser_v1_new ();
-
-        /* AT+CPIN? replies will never have an OK appended */
-        regex = g_regex_new ("\\r\\n\\+CPIN: .*\\r\\n",
-                             G_REGEX_RAW | G_REGEX_OPTIMIZE,
-                             0, NULL);
-        mm_serial_parser_v1_set_custom_regex (parser, regex, NULL);
-        g_regex_unref (regex);
-
-        mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (port),
-                                               mm_serial_parser_v1_parse,
-                                               parser,
-                                               mm_serial_parser_v1_destroy);
-    }
-}
-
-static void
-set_property (GObject *object,
-              guint prop_id,
-              const GValue *value,
-              GParamSpec *pspec)
-{
-    /* Do nothing... see set_property() in parent, which also does nothing */
-}
-
-static void
-get_property (GObject *object,
-              guint prop_id,
-              GValue *value,
-              GParamSpec *pspec)
-{
-    switch (prop_id) {
-    case MM_GENERIC_GSM_PROP_POWER_UP_CMD:
-        /* Try to go to full functionality mode without rebooting the system.
-         * Works well if we previously switched off the power with CFUN=4
-         */
-        g_value_set_string (value, "+CFUN=1,0");
-        break;
-    case MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD:
-        /* Wavecom doesn't have XOFF/XON flow control, so we enable RTS/CTS */
-        g_value_set_string (value, "+IFC=2,2");
-        break;
-    case MM_GENERIC_GSM_PROP_POWER_DOWN_CMD:
-        /* Use AT+CFUN=4 for power down. It will stop the RF (IMSI detach), and
-         * keeps access to the SIM */
-        g_value_set_string (value, "+CFUN=4");
-        break;
-    default:
-        break;
-    }
-}
-
-static void
-set_band_done (MMAtSerialPort *port,
-               GString *response,
-               GError *error,
-               gpointer user_data)
-{
-    MMCallbackInfo *info = (MMCallbackInfo *) user_data;
-
-    /* If the modem has already been removed, return without
-     * scheduling callback */
-    if (mm_callback_info_check_modem_removed (info))
-        return;
-
-    if (error)
-        info->error = g_error_copy (error);
-    else {
-        MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem);
-
-        priv->current_bands = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "new-band"));
-    }
-
-    mm_callback_info_schedule (info);
-}
-
-static void
-set_2g_band (MMModemGsmNetwork *self,
-             MMModemGsmBand band,
-             MMAtSerialPort *port,
-             MMCallbackInfo *info)
-{
-    gchar wavecom_band;
-    gchar *cmd;
-    guint i;
-
-    /* Ensure we don't get 3G bands when trying to configure 2G bands */
-    if (band & ALL_3G_BANDS) {
-        info->error = g_error_new (MM_MODEM_ERROR,
-                                   MM_MODEM_ERROR_GENERAL,
-                                   "Not allowed to set 3G bands in 2G mode");
-        mm_callback_info_schedule (info);
-        return;
-    }
-
-    /* If we get ANY, reset to all-2G bands to get the proper value */
-    if (band == MM_MODEM_GSM_BAND_ANY)
-        band = ALL_2G_BANDS;
-
-    /* Loop looking for allowed masks */
-    wavecom_band = '\0';
-    for (i = 0; i < G_N_ELEMENTS (bands_2g); i++) {
-        if (bands_2g[i].mm_band_mask == band) {
-            wavecom_band = bands_2g[i].wavecom_band;
-            break;
-        }
-    }
-
-    /* If we didn't find a match, set an error */
-    if (wavecom_band == '\0') {
-        info->error = g_error_new (MM_MODEM_ERROR,
-                                   MM_MODEM_ERROR_GENERAL,
-                                   "Wrong 2G band mask: '%u'", band);
-        mm_callback_info_schedule (info);
-        return;
-    }
-
-    mm_callback_info_set_data (info,
-                               "new-band",
-                               GUINT_TO_POINTER ((guint)band),
-                               NULL);
-
-    cmd = g_strdup_printf ("+WMBS=%c,1", wavecom_band);
-    mm_at_serial_port_queue_command (port, cmd, 3, set_band_done, info);
-    g_free (cmd);
-}
-
-static void
-set_3g_band (MMModemGsmNetwork *self,
-             MMModemGsmBand band,
-             MMAtSerialPort *port,
-             MMCallbackInfo *info)
-{
-    guint wavecom_band;
-    gchar *cmd;
-    guint i;
-
-    /* Ensure we don't get 2G bands when trying to configure 2G bands */
-    if (band & ALL_2G_BANDS) {
-        info->error = g_error_new (MM_MODEM_ERROR,
-                                   MM_MODEM_ERROR_GENERAL,
-                                   "Not allowed to set 2G bands in 3G mode");
-        mm_callback_info_schedule (info);
-        return;
-    }
-
-    /* If we get ANY, reset to all-3G bands to get the proper value */
-    if (band == MM_MODEM_GSM_BAND_ANY)
-        band = ALL_3G_BANDS;
-
-    /* Loop looking for allowed masks */
-    wavecom_band = 0;
-    for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) {
-        if (bands_3g[i].mm_band_flag & band) {
-            wavecom_band |= bands_3g[i].wavecom_band_flag;
-        }
-    }
-
-    /* If we didn't find a match, set an error */
-    if (wavecom_band == 0) {
-        info->error = g_error_new (MM_MODEM_ERROR,
-                                   MM_MODEM_ERROR_GENERAL,
-                                   "Wrong 3G band mask: '%u'", band);
-        mm_callback_info_schedule (info);
-        return;
-    }
-
-    mm_callback_info_set_data (info,
-                               "new-band",
-                               GUINT_TO_POINTER ((guint)band),
-                               NULL);
-
-    cmd = g_strdup_printf ("+WMBS=\"%u\",1", wavecom_band);
-    mm_at_serial_port_queue_command (port, cmd, 3, set_band_done, info);
-    g_free (cmd);
-}
-
-static void
-set_band (MMModemGsmNetwork *self,
-          MMModemGsmBand band,
-          MMModemFn callback,
-          gpointer user_data)
-{
-    MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (self);
-    MMAtSerialPort *port;
-    MMCallbackInfo *info;
-
-    info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
-
-    /* Are we trying to change the band to the same bands currently
-     * being used? if so, we're done */
-    if (priv->current_bands == band) {
-        mm_callback_info_schedule (info);
-        return;
-    }
-
-    /* Otherwise ask the modem */
-    port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error);
-    if (!port) {
-        mm_callback_info_schedule (info);
-        return;
-    }
-
-    if (priv->current_ms_class != WAVECOM_MS_CLASS_A)
-        set_2g_band (self, band, port, info);
-    else
-        set_3g_band (self, band, port, info);
-}
-
-static void
-get_2g_band_done (MMAtSerialPort *port,
-                  GString *response,
-                  GError *error,
-                  gpointer user_data)
-{
-    MMCallbackInfo *info = (MMCallbackInfo *) user_data;
-
-    /* If the modem has already been removed, return without
-     * scheduling callback */
-    if (mm_callback_info_check_modem_removed (info))
-        return;
-
-    if (error)
-        info->error = g_error_copy (error);
-    else {
-        MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem);
-        const gchar *p;
-        guint32 mm_band = MM_MODEM_GSM_BAND_UNKNOWN;
-
-        p = mm_strip_tag (response->str, "+WMBS:");
-        if (p) {
-            guint i;
-
-            for (i = 0; i < G_N_ELEMENTS (bands_2g); i++) {
-                if (bands_2g[i].wavecom_band == *p) {
-                    mm_band = bands_2g[i].mm_band_mask;
-                    break;
-                }
-            }
-        }
-
-        if (mm_band == MM_MODEM_GSM_BAND_UNKNOWN) {
-            g_set_error (&info->error,
-                         MM_MODEM_ERROR,
-                         MM_MODEM_ERROR_GENERAL,
-                         "Couldn't get 2G bands");
-        } else {
-            priv->current_bands = mm_band;
-            mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL);
-        }
-    }
-
-    mm_callback_info_schedule (info);
-}
-
-static void
-get_3g_band_done (MMAtSerialPort *port,
-                  GString *response,
-                  GError *error,
-                  gpointer user_data)
-{
-    MMCallbackInfo *info = (MMCallbackInfo *) user_data;
-
-    /* If the modem has already been removed, return without
-     * scheduling callback */
-    if (mm_callback_info_check_modem_removed (info))
-        return;
-
-    if (error)
-        info->error = g_error_copy (error);
-    else {
-        MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem);
-        const gchar *p;
-        guint mm_band = MM_MODEM_GSM_BAND_UNKNOWN;
-        guint32 wavecom_band;
-
-        /* Example reply:
-         *   AT+WUBS? -->
-         *            <-- +WUBS: "3",1
-         *            <-- OK
-         * The "3" meaning here Band I and II are selected.
-         */
-
-        p = mm_strip_tag (response->str, "+WUBS:");
-        if (*p == '"')
-            p++;
-        wavecom_band = atoi (p);
-
-        if (wavecom_band > 0) {
-            guint i;
-
-            for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) {
-                if (bands_3g[i].wavecom_band_flag & wavecom_band) {
-                    mm_band |= bands_3g[i].mm_band_flag;
-                }
-            }
-        }
-
-        if (mm_band == MM_MODEM_GSM_BAND_UNKNOWN) {
-            g_set_error (&info->error,
-                         MM_MODEM_ERROR,
-                         MM_MODEM_ERROR_GENERAL,
-                         "Couldn't get 3G bands");
-        } else {
-            priv->current_bands = mm_band;
-            mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL);
-        }
-    }
-
-    mm_callback_info_schedule (info);
-}
-
-static void
-get_band (MMModemGsmNetwork *self,
-          MMModemUIntFn callback,
-          gpointer user_data)
-{
-    MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (self);
-    MMAtSerialPort *port;
-    MMCallbackInfo *info;
-
-    info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data);
-
-    /* If results are already cached, return them */
-    if (priv->current_bands > 0) {
-        mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->current_bands), NULL);
-        mm_callback_info_schedule (info);
-        return;
-    }
-
-    /* Otherwise ask the modem */
-    port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error);
-    if (!port) {
-        mm_callback_info_schedule (info);
-        return;
-    }
-
-    if (priv->current_ms_class != WAVECOM_MS_CLASS_A)
-        mm_at_serial_port_queue_command (port, "AT+WMBS?", 3, get_2g_band_done, info);
-    else
-        mm_at_serial_port_queue_command (port, "AT+WUBS?", 3, get_3g_band_done, info);
-}
-
-static void
-get_access_technology_cb (MMAtSerialPort *port,
-                          GString *response,
-                          GError *error,
-                          gpointer user_data)
-{
-    MMCallbackInfo *info = user_data;
-    MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
-    const gchar *p;
-
-    /* If the modem has already been removed, return without
-     * scheduling callback */
-    if (mm_callback_info_check_modem_removed (info))
-        return;
-
-    if (error)
-        info->error = g_error_copy (error);
-    else {
-        p = mm_strip_tag (response->str, "+WGPRSIND:");
-        if (!p) {
-            g_set_error (&info->error,
-                         MM_MODEM_ERROR,
-                         MM_MODEM_ERROR_GENERAL,
-                         "Couldn't get network capabilities");
-        } else {
-            switch (*p) {
-            case '1':
-                /* GPRS only */
-                act = MM_MODEM_GSM_ACCESS_TECH_GPRS;
-                break;
-            case '2':
-                /* EGPRS/EDGE supported */
-                act = MM_MODEM_GSM_ACCESS_TECH_EDGE;
-                break;
-            case '3':
-                /* 3G R99 supported */
-                act = MM_MODEM_GSM_ACCESS_TECH_UMTS;
-                break;
-            case '4':
-                /* HSDPA supported */
-                act = MM_MODEM_GSM_ACCESS_TECH_HSDPA;
-                break;
-            case '5':
-                /* HSUPA supported */
-                act = MM_MODEM_GSM_ACCESS_TECH_HSUPA;
-                break;
-            default:
-                break;
-            }
-        }
-    }
-
-    mm_callback_info_set_result (info, GUINT_TO_POINTER (act), NULL);
-    mm_callback_info_schedule (info);
-}
-
-static void
-get_access_technology (MMGenericGsm *gsm,
-                       MMModemUIntFn callback,
-                       gpointer user_data)
-{
-    MMAtSerialPort *port;
-    MMCallbackInfo *info;
-
-    info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data);
-
-    port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
-    if (!port) {
-        mm_callback_info_schedule (info);
-        return;
-    }
-
-    mm_at_serial_port_queue_command (port, "+WGPRS=9,2", 3, get_access_technology_cb, info);
-}
-
-static void
-get_allowed_mode_cb (MMAtSerialPort *port,
-                     GString *response,
-                     GError *error,
-                     gpointer user_data)
-{
-    MMCallbackInfo *info = user_data;
-    MMModemWavecomGsmPrivate *priv;
-    gint read_mode = -1;
-    gchar *mode_str = NULL;
-    gchar *prefer_str = NULL;
-    GRegex *r = NULL;
-    GMatchInfo *match_info = NULL;
-
-    /* If the modem has already been removed, return without
-     * scheduling callback */
-    if (mm_callback_info_check_modem_removed (info))
-        return;
-
-    if (error) {
-        info->error = g_error_copy (error);
-        mm_callback_info_schedule (info);
-        return;
-    }
-
-    priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem);
-
-    /* Possible responses:
-     *   +WWSM: 0    (2G only)
-     *   +WWSM: 1    (3G only)
-     *   +WWSM: 2,0  (Any)
-     *   +WWSM: 2,1  (2G preferred)
-     *   +WWSM: 2,2  (3G preferred)
-     */
-    r = g_regex_new ("\\r\\n\\+WWSM: ([0-2])(,([0-2]))?.*$", 0, 0, NULL);
-    if (r && g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, NULL)) {
-        mode_str = g_match_info_fetch (match_info, 1);
-        prefer_str = g_match_info_fetch (match_info, 3); /* 3, to avoid the comma */
-
-        if (mode_str) {
-            switch (atoi (mode_str)) {
-            case 0:
-                if (!prefer_str)
-                    read_mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY;
-                break;
-            case 1:
-                if (!prefer_str)
-                    read_mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY;
-                break;
-            case 2:
-                if (prefer_str) {
-                    switch (atoi (prefer_str)) {
-                    case 0:
-                        read_mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
-                        break;
-                    case 1:
-                        read_mode = MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED;
-                        break;
-                    case 2:
-                        read_mode = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED;
-                        break;
-                    default:
-                        g_warn_if_reached ();
-                        break;
-                    }
-                }
-                break;
-            default:
-                g_warn_if_reached ();
-                break;
-            }
-        }
-    }
-
-    if (read_mode < 0) {
-        info->error = g_error_new (MM_MODEM_ERROR,
-                                   MM_MODEM_ERROR_GENERAL,
-                                   "Unexpected wireless data service reply: '%s' "
-                                   "(mode: '%s', prefer: '%s')",
-                                   response->str,
-                                   mode_str ? mode_str : "none",
-                                   prefer_str ? prefer_str : "none");
-    } else {
-        priv->allowed_mode = (guint)read_mode;
-        mm_callback_info_set_result (info,
-                                     GUINT_TO_POINTER (priv->allowed_mode),
-                                     NULL);
-    }
-
-    if (r)
-        g_regex_unref (r);
-    if (match_info)
-        g_match_info_free (match_info);
-    g_free (mode_str);
-    g_free (prefer_str);
-
-    mm_callback_info_schedule (info);
-}
-
-static void
-get_allowed_mode (MMGenericGsm *gsm,
-                  MMModemUIntFn callback,
-                  gpointer user_data)
-{
-    MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (gsm);
-    MMCallbackInfo *info;
-
-    info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data);
-
-    /* For 3G devices, query WWSM status */
-    if (priv->supported_ms_classes & WAVECOM_MS_CLASS_A) {
-        MMAtSerialPort *port;
-
-        port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
-        if (!port) {
-            mm_callback_info_schedule (info);
-            return;
-        }
-
-        mm_at_serial_port_queue_command (port, "+WWSM?", 3, get_allowed_mode_cb, info);
-        return;
-    }
-
-    /* For 2G devices, just return cached value */
-    mm_callback_info_set_result (info,
-                                 GUINT_TO_POINTER (priv->allowed_mode),
-                                 NULL);
-    mm_callback_info_schedule (info);
-}
-
-static void
-set_allowed_mode_cb (MMAtSerialPort *port,
-                     GString *response,
-                     GError *error,
-                     gpointer user_data)
-{
-    MMCallbackInfo *info = (MMCallbackInfo *) user_data;
-
-    /* If the modem has already been removed, return without
-     * scheduling callback */
-    if (mm_callback_info_check_modem_removed (info))
-        return;
-
-    if (error)
-        info->error = g_error_copy (error);
-    else {
-        MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem);
-
-        priv->allowed_mode = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "new-mode"));
-    }
-
-    mm_callback_info_schedule (info);
-}
-
-static void
-set_allowed_mode (MMGenericGsm *gsm,
-                  MMModemGsmAllowedMode mode,
-                  MMModemFn callback,
-                  gpointer user_data)
-{
-    MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (gsm);
-    MMCallbackInfo *info;
-
-    info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data);
-
-    /* For 3G devices, go on with WWSM */
-    if (priv->supported_ms_classes & WAVECOM_MS_CLASS_A) {
-        MMAtSerialPort *port;
-        GString *cmd;
-        gint net = -1;
-        gint prefer = -1;
-
-        /* Get port */
-        port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
-        if (!port) {
-            mm_callback_info_schedule (info);
-            return;
-        }
-
-        mm_callback_info_set_data (info,
-                                   "new-mode",
-                                   GUINT_TO_POINTER (mode),
-                                   NULL);
-
-        switch (mode) {
-        case MM_MODEM_GSM_ALLOWED_MODE_ANY:
-            net = 2;
-            prefer = 0;
-            break;
-        case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED:
-            net = 2;
-            prefer = 1;
-            break;
-        case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED:
-            net = 2;
-            prefer = 2;
-            break;
-        case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY:
-            net = 0;
-            break;
-        case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY:
-            net = 1;
-            break;
-        }
-
-        cmd = g_string_new ("+WWSM=");
-        g_string_append_printf (cmd, "%d", net);
-        if (net == 2)
-            g_string_append_printf (cmd, ",%d", prefer);
-        mm_at_serial_port_queue_command (port, cmd->str, 3, set_allowed_mode_cb, info);
-        g_string_free (cmd, TRUE);
-
-        return;
-    }
-
-    /* For non-3G devices, allow only 2G-related allowed modes */
-    switch (mode) {
-    case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED:
-    case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY:
-    case MM_MODEM_GSM_ALLOWED_MODE_ANY:
-        priv->allowed_mode = mode;
-        break;
-    default:
-        info->error = g_error_new (MM_MODEM_ERROR,
-                                   MM_MODEM_ERROR_GENERAL,
-                                   "Cannot set desired allowed mode, "
-                                   "not a 3G device");
-        break;
-    }
-
-    mm_callback_info_schedule (info);
-}
-
-static void
-enable_complete (MMGenericGsm *gsm,
-                 GError *error,
-                 MMCallbackInfo *info)
-{
-    /* Do NOT chain up parent do_enable_power_up_done(), as it actually ignores
-     * all errors. */
-
-    mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info);
-}
-
-static void
-set_highest_ms_class_cb (MMAtSerialPort *port,
-                         GString *response,
-                         GError *error,
-                         gpointer user_data)
-{
-    MMCallbackInfo *info = (MMCallbackInfo *) user_data;
-    guint new_class;
-
-    /* If the modem has already been removed, return without
-     * scheduling callback */
-    if (mm_callback_info_check_modem_removed (info))
-        return;
-
-    if (error) {
-        enable_complete (MM_GENERIC_GSM (info->modem), error, info);
-        return;
-    }
-
-    new_class = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "new-class"));
-    if (new_class) {
-        MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem);
-
-        priv->current_ms_class = new_class;
-    }
-
-    /* All done without errors! */
-    mm_dbg ("[4/4] All done");
-    enable_complete (MM_GENERIC_GSM (info->modem), NULL, info);
-}
-
-static void
-set_highest_ms_class (MMAtSerialPort *port,
-                      MMCallbackInfo *info)
-{
-    MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem);
-    guint new_class = 0;
-
-    if (priv->supported_ms_classes & WAVECOM_MS_CLASS_A) {
-        if (priv->current_ms_class != WAVECOM_MS_CLASS_A) {
-            /* A is supported but is not currently selected, switch to A */
-            new_class = WAVECOM_MS_CLASS_A;
-        }
-    } else if (priv->supported_ms_classes & WAVECOM_MS_CLASS_B) {
-        if (priv->current_ms_class != WAVECOM_MS_CLASS_B) {
-            /* B is supported but is not currently selected, switch to B */
-            new_class = WAVECOM_MS_CLASS_B;
-        }
-    } else if (priv->supported_ms_classes & WAVECOM_MS_CLASS_CG) {
-        if (priv->current_ms_class != WAVECOM_MS_CLASS_CG) {
-            /* CG is supported but is not currently selected, switch to CG */
-            new_class = WAVECOM_MS_CLASS_CG;
-        }
-    }
-
-    if (new_class) {
-        const gchar *new_class_str;
-        gchar *cmd;
-
-        new_class_str = wavecom_ms_class_to_str (new_class);
-        mm_dbg ("Changing mobile station class to: %s", new_class_str);
-        mm_callback_info_set_data (info,
-                                   "new-class",
-                                   GUINT_TO_POINTER (new_class),
-                                   NULL);
-        cmd = g_strdup_printf ("+CGCLASS=%s", new_class_str);
-        mm_at_serial_port_queue_command (port, cmd, 3, set_highest_ms_class_cb, info);
-        g_free (cmd);
-        return;
-    }
-
-    /* if no need to change station class, then just go on */
-    mm_dbg ("No need to change mobile station class");
-    set_highest_ms_class_cb (port, NULL, NULL, info);
-}
-
-static void
-get_current_ms_class_cb (MMAtSerialPort *port,
-                         GString *response,
-                         GError *error,
-                         gpointer user_data)
-{
-    MMCallbackInfo *info = user_data;
-    MMModemWavecomGsmPrivate *priv;
-    const gchar *p;
-
-    /* If the modem has already been removed, return without
-     * scheduling callback */
-    if (mm_callback_info_check_modem_removed (info))
-        return;
-
-    if (error) {
-        enable_complete (MM_GENERIC_GSM (info->modem), error, info);
-        return;
-    }
-
-    priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem);
-
-    p = mm_strip_tag (response->str, "+CGCLASS:");
-
-    if (strncmp (p,
-                 WAVECOM_MS_CLASS_A_IDSTR,
-                 strlen (WAVECOM_MS_CLASS_A_IDSTR)) == 0) {
-        mm_dbg ("Modem configured as a Class A mobile station");
-        priv->current_ms_class = WAVECOM_MS_CLASS_A;
-    } else if (strncmp (p,
-                        WAVECOM_MS_CLASS_B_IDSTR,
-                        strlen (WAVECOM_MS_CLASS_B_IDSTR)) == 0) {
-        mm_dbg ("Modem configured as a Class B mobile station");
-        priv->current_ms_class = WAVECOM_MS_CLASS_B;
-    } else if (strncmp (p,
-                        WAVECOM_MS_CLASS_CG_IDSTR,
-                        strlen (WAVECOM_MS_CLASS_CG_IDSTR)) == 0) {
-        mm_dbg ("Modem configured as a Class CG mobile station");
-        priv->current_ms_class = WAVECOM_MS_CLASS_CG;
-    } else if (strncmp (p,
-                        WAVECOM_MS_CLASS_CC_IDSTR,
-                        strlen (WAVECOM_MS_CLASS_CC_IDSTR)) == 0) {
-        mm_dbg ("Modem configured as a Class CC mobile station");
-        priv->current_ms_class = WAVECOM_MS_CLASS_CC;
-    } else {
-        GError *inner_error;
-
-        inner_error = g_error_new (MM_MODEM_ERROR,
-                                   MM_MODEM_ERROR_GENERAL,
-                                   "Unknown mobile station class: '%s'",
-                                   p);
-        enable_complete (MM_GENERIC_GSM (info->modem), inner_error, info);
-        g_error_free (inner_error);
-        return;
-    }
-
-    /* Next, set highest mobile station class possible */
-    mm_dbg ("[3/4] Ensuring highest MS class...");
-    set_highest_ms_class (port, info);
-}
-
-static void
-get_supported_ms_classes_cb (MMAtSerialPort *port,
-                             GString *response,
-                             GError *error,
-                             gpointer user_data)
-{
-    MMCallbackInfo *info = user_data;
-    MMModemWavecomGsmPrivate *priv;
-    const gchar *p;
-
-    /* If the modem has already been removed, return without
-     * scheduling callback */
-    if (mm_callback_info_check_modem_removed (info))
-        return;
-
-    if (error) {
-        enable_complete (MM_GENERIC_GSM (info->modem), error, info);
-        return;
-    }
-
-    priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem);
-
-    /* Reset currently supported MS classes */
-    priv->supported_ms_classes = 0;
-
-    p = mm_strip_tag (response->str, "+CGCLASS:");
-
-    if (strstr (p, WAVECOM_MS_CLASS_A_IDSTR)) {
-        mm_dbg ("Modem supports Class A mobile station");
-        priv->supported_ms_classes |= WAVECOM_MS_CLASS_A;
-    }
-
-    if (strstr (p, WAVECOM_MS_CLASS_B_IDSTR)) {
-        mm_dbg ("Modem supports Class B mobile station");
-        priv->supported_ms_classes |= WAVECOM_MS_CLASS_B;
-    }
-
-    if (strstr (p, WAVECOM_MS_CLASS_CG_IDSTR)) {
-        mm_dbg ("Modem supports Class CG mobile station");
-        priv->supported_ms_classes |= WAVECOM_MS_CLASS_CG;
-    }
-
-    if (strstr (p, WAVECOM_MS_CLASS_CC_IDSTR)) {
-        mm_dbg ("Modem supports Class CC mobile station");
-        priv->supported_ms_classes |= WAVECOM_MS_CLASS_CC;
-    }
-
-    /* If none received, error */
-    if (!priv->supported_ms_classes) {
-        GError *inner_error;
-
-        inner_error = g_error_new (MM_MODEM_ERROR,
-                                   MM_MODEM_ERROR_GENERAL,
-                                   "Couldn't get supported mobile station classes");
-        enable_complete (MM_GENERIC_GSM (info->modem), inner_error, info);
-        g_error_free (inner_error);
-        return;
-    }
-
-    /* Next, query for current MS class */
-    mm_dbg ("[2/4] Getting current MS class...");
-    mm_at_serial_port_queue_command (port, "+CGCLASS?",  3, get_current_ms_class_cb, info);
-}
-
-static void
-do_enable_power_up_done (MMGenericGsm *gsm,
-                         GString *response,
-                         GError *error,
-                         MMCallbackInfo *info)
-{
-    MMAtSerialPort *port;
-    GError *inner_error = NULL;
-
-    /* If the modem has already been removed, return without
-     * scheduling callback */
-    if (mm_callback_info_check_modem_removed (info))
-        return;
-
-    if (error) {
-        enable_complete (gsm, error, info);
-        return;
-    }
-
-    /* Get port */
-    port = mm_generic_gsm_get_best_at_port (gsm, &inner_error);
-    if (!port) {
-        enable_complete (gsm, inner_error, info);
-        g_error_free (inner_error);
-        return;
-    }
-
-    mm_dbg ("[1/4] Getting supported MS classes...");
-    mm_at_serial_port_queue_command (port, "+CGCLASS=?", 3, get_supported_ms_classes_cb, info);
-}
-
-static void
-get_current_functionality_status_cb (MMAtSerialPort *port,
-                                     GString *response,
-                                     GError *error,
-                                     gpointer user_data)
-{
-    MMCallbackInfo *info = user_data;
-    guint needed = FALSE;
-
-    /* If the modem has already been removed, return without
-     * scheduling callback */
-    if (mm_callback_info_check_modem_removed (info))
-        return;
-
-    /* On error, just assume we don't need the power-up command */
-    if (!error) {
-        const gchar *p;
-
-        p = mm_strip_tag (response->str, "+CFUN:");
-        if (p && *p == '1') {
-            /* If reported functionality status is '1', then we do not need to
-             * issue the power-up command. Otherwise, do it. */
-            mm_dbg ("Already in full functionality status, skipping power-up command");
-        } else {
-            needed = TRUE;
-            mm_warn ("Not in full functionality status, power-up command is needed. "
-                     "Note that it may reboot the modem.");
-        }
-    } else
-        mm_warn ("Failed checking if power-up command is needed: '%s'. "
-                 "Will assume it isn't.",
-                 error->message);
-
-    /* Set result and schedule */
-    mm_callback_info_set_result (info,
-                                 GUINT_TO_POINTER (needed),
-                                 NULL);
-    mm_callback_info_schedule (info);
-}
-
-static void
-do_enable_power_up_check_needed (MMGenericGsm *self,
-                                 MMModemUIntFn callback,
-                                 gpointer user_data)
-{
-    MMAtSerialPort *primary;
-    MMCallbackInfo *info;
-
-    info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data);
-
-    /* Get port */
-    primary = mm_generic_gsm_get_at_port (self, MM_AT_PORT_FLAG_PRIMARY);
-    g_assert (primary);
-
-    /* Get current functionality status */
-    mm_dbg ("Getting current functionality status...");
-    mm_at_serial_port_queue_command (primary, "+CFUN?", 3, get_current_functionality_status_cb, info);
-}
-
-/*****************************************************************************/
-
-static void
-modem_gsm_network_init (MMModemGsmNetwork *network_class)
-{
-    network_class->set_band = set_band;
-    network_class->get_band = get_band;
-}
-
-static void
-mm_modem_wavecom_gsm_init (MMModemWavecomGsm *self)
-{
-    MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (self);
-
-    /* Set defaults */
-    priv->supported_ms_classes = 0; /* This is a bitmask, so empty */
-    priv->current_ms_class = WAVECOM_MS_CLASS_UNKNOWN;
-    priv->allowed_mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
-    priv->current_bands = 0; /* This is a bitmask, so empty */
-}
-
-static void
-mm_modem_wavecom_gsm_class_init (MMModemWavecomGsmClass *klass)
-{
-    GObjectClass *object_class = G_OBJECT_CLASS (klass);
-    MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass);
-
-    g_type_class_add_private (object_class, sizeof (MMModemWavecomGsmPrivate));
-
-    object_class->get_property = get_property;
-    object_class->set_property = set_property;
-
-    g_object_class_override_property (object_class,
-                                      MM_GENERIC_GSM_PROP_POWER_UP_CMD,
-                                      MM_GENERIC_GSM_POWER_UP_CMD);
-
-    g_object_class_override_property (object_class,
-                                      MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD,
-                                      MM_GENERIC_GSM_FLOW_CONTROL_CMD);
-
-    g_object_class_override_property (object_class,
-                                      MM_GENERIC_GSM_PROP_POWER_DOWN_CMD,
-                                      MM_GENERIC_GSM_POWER_DOWN_CMD);
-
-    gsm_class->port_grabbed = port_grabbed;
-    gsm_class->do_enable_power_up_check_needed = do_enable_power_up_check_needed;
-    gsm_class->do_enable_power_up_done = do_enable_power_up_done;
-    gsm_class->set_allowed_mode = set_allowed_mode;
-    gsm_class->get_allowed_mode = get_allowed_mode;
-    gsm_class->get_access_technology = get_access_technology;
-}
-
diff --git a/plugins/mm-modem-wavecom-gsm.h b/plugins/mm-modem-wavecom-gsm.h
deleted file mode 100644
index 25540ad..0000000
--- a/plugins/mm-modem-wavecom-gsm.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* -*- 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) 2011 Ammonit Measurement GmbH
- * Author: Aleksander Morgado <aleksander@lanedo.com>
- */
-
-#ifndef MM_MODEM_WAVECOM_GSM_H
-#define MM_MODEM_WAVECOM_GSM_H
-
-#include "mm-generic-gsm.h"
-
-#define MM_TYPE_MODEM_WAVECOM_GSM            (mm_modem_wavecom_gsm_get_type ())
-#define MM_MODEM_WAVECOM_GSM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_WAVECOM_GSM, MMModemWavecomGsm))
-#define MM_MODEM_WAVECOM_GSM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  MM_TYPE_MODEM_WAVECOM_AIRLINK_GSM, MMModemWavecomGsmClass))
-#define MM_IS_MODEM_WAVECOM_GSM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_WAVECOM_AIRLINK_GSM))
-#define MM_IS_MODEM_WAVECOM_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_MODEM_WAVECOM_AIRLINK_GSM))
-#define MM_MODEM_WAVECOM_GSM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_MODEM_WAVECOM_AIRLINK_GSM, MMModemWavecomGsmClass))
-
-typedef struct {
-    MMGenericGsm parent;
-} MMModemWavecomGsm;
-
-typedef struct {
-    MMGenericGsmClass parent;
-} MMModemWavecomGsmClass;
-
-GType mm_modem_wavecom_gsm_get_type (void);
-
-MMModem *mm_modem_wavecom_gsm_new (const char *device,
-                                   const char *driver,
-                                   const char *plugin_name,
-                                   guint32 vendor,
-                                   guint32 product);
-
-#endif /* MM_MODEM_WAVECOM_GSM_H */
diff --git a/plugins/mm-plugin-wavecom.c b/plugins/mm-plugin-wavecom.c
deleted file mode 100644
index 3bd82cb..0000000
--- a/plugins/mm-plugin-wavecom.c
+++ /dev/null
@@ -1,168 +0,0 @@
-/* -*- 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.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Copyright (C) 2011 Ammonit Measurement GmbH
- * Author: Aleksander Morgado <aleksander@lanedo.com>
- */
-
-#include <string.h>
-#include <gmodule.h>
-#include "mm-plugin-wavecom.h"
-#include "mm-modem-wavecom-gsm.h"
-
-G_DEFINE_TYPE (MMPluginWavecom, mm_plugin_wavecom, MM_TYPE_PLUGIN_BASE)
-
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
-
-G_MODULE_EXPORT MMPlugin *
-mm_plugin_create (void)
-{
-    return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_WAVECOM,
-                                    MM_PLUGIN_BASE_NAME, "Wavecom",
-                                    NULL));
-}
-
-static guint32
-get_level_for_capabilities (guint32 capabilities)
-{
-    if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM)
-        return 10;
-    return 0;
-}
-
-static void
-probe_result (MMPluginBase *base,
-              MMPluginBaseSupportsTask *task,
-              guint32 capabilities,
-              gpointer user_data)
-{
-    mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities));
-}
-
-static MMPluginSupportsResult
-supports_port (MMPluginBase *base,
-               MMModem *existing,
-               MMPluginBaseSupportsTask *task)
-{
-    GUdevDevice *port;
-    const char *subsys, *name;
-    guint16 vendor = 0;
-
-    /* Can't do anything with non-serial ports */
-    port = mm_plugin_base_supports_task_get_port (task);
-    if (strcmp (g_udev_device_get_subsystem (port), "tty"))
-        return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
-
-    subsys = g_udev_device_get_subsystem (port);
-    name = g_udev_device_get_name (port);
-
-    if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, NULL))
-        return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
-
-    /* Vendor: Wavecom */
-    if (vendor != 0x114f)
-        return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
-
-    /* Check if a previous probing was already launched in this port */
-    if (mm_plugin_base_supports_task_propagate_cached (task)) {
-        guint32 level;
-
-        /* A previous probing was already done, use its results */
-        level = get_level_for_capabilities (mm_plugin_base_supports_task_get_probed_capabilities (task));
-        if (level) {
-            mm_plugin_base_supports_task_complete (task, level);
-            return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS;
-        }
-        return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
-    }
-
-    /* Otherwise kick off a probe */
-    if (mm_plugin_base_probe_port (base, task, 100000, NULL))
-        return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS;
-
-    return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
-}
-
-static MMModem *
-grab_port (MMPluginBase *base,
-           MMModem *existing,
-           MMPluginBaseSupportsTask *task,
-           GError **error)
-{
-    GUdevDevice *port = NULL;
-    MMModem *modem = NULL;
-    const char *name, *subsys, *sysfs_path;
-    guint32 caps;
-    guint16 vendor = 0, product = 0;
-    MMPortType ptype;
-
-    port = mm_plugin_base_supports_task_get_port (task);
-    g_assert (port);
-
-    subsys = g_udev_device_get_subsystem (port);
-    name = g_udev_device_get_name (port);
-
-    if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) {
-        g_set_error (error, 0, 0, "Could not get modem product ID.");
-        return NULL;
-    }
-
-    caps = mm_plugin_base_supports_task_get_probed_capabilities (task);
-    ptype = mm_plugin_base_probed_capabilities_to_port_type (caps);
-    sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
-    if (!existing) {
-        if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) {
-            modem = mm_modem_wavecom_gsm_new (sysfs_path,
-                                              mm_plugin_base_supports_task_get_driver (task),
-                                              mm_plugin_get_name (MM_PLUGIN (base)),
-                                              vendor,
-                                              product);
-        }
-
-        if (modem) {
-            if (!mm_modem_grab_port (modem, subsys, name, ptype, MM_AT_PORT_FLAG_NONE, NULL, error)) {
-                g_object_unref (modem);
-                return NULL;
-            }
-        }
-    } else if (get_level_for_capabilities (caps)) {
-        modem = existing;
-        if (!mm_modem_grab_port (modem, subsys, name, ptype, MM_AT_PORT_FLAG_NONE, NULL, error))
-            return NULL;
-    }
-
-    return modem;
-}
-
-/*****************************************************************************/
-
-static void
-mm_plugin_wavecom_init (MMPluginWavecom *self)
-{
-    g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL);
-}
-
-static void
-mm_plugin_wavecom_class_init (MMPluginWavecomClass *klass)
-{
-    MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass);
-
-    pb_class->supports_port = supports_port;
-    pb_class->grab_port = grab_port;
-}
diff --git a/plugins/novatel/mm-broadband-bearer-novatel.c b/plugins/novatel/mm-broadband-bearer-novatel.c
index 20f87c1..a3482ae 100644
--- a/plugins/novatel/mm-broadband-bearer-novatel.c
+++ b/plugins/novatel/mm-broadband-bearer-novatel.c
@@ -36,36 +36,21 @@
 
 G_DEFINE_TYPE (MMBroadbandBearerNovatel, mm_broadband_bearer_novatel, MM_TYPE_BROADBAND_BEARER);
 
-enum {
-    PROP_0,
-    PROP_USER,
-    PROP_PASSWORD,
-    PROP_LAST
-};
-
-static GParamSpec *properties[PROP_LAST];
-
 /*****************************************************************************/
 
-
-
 struct _MMBroadbandBearerNovatelPrivate {
     /* timeout id for checking whether we're still connected */
     guint connection_poller;
-    /* Username for authenticating to APN */
-    gchar *user;
-    /* Password for authenticating to APN */
-    gchar *password;
 };
 
 typedef struct {
-    MMBroadbandBearer *self;
+    MMBroadbandBearerNovatel *self;
     MMBaseModem *modem;
     MMAtSerialPort *primary;
     MMPort *data;
     GCancellable *cancellable;
     GSimpleAsyncResult *result;
-    int retries;
+    gint retries;
 } DetailedConnectContext;
 
 static DetailedConnectContext *
@@ -143,13 +128,14 @@
     if (!result) {
         mm_warn ("QMI connection status failed: %s", error->message);
         g_error_free (error);
-    } else {
-        result = mm_strip_tag (result, "$NWQMISTATUS:");
-        if (g_strrstr(result, "QMI State: DISCONNECTED")) {
-            mm_bearer_report_disconnection (MM_BEARER (bearer));
-            g_source_remove (bearer->priv->connection_poller);
-            bearer->priv->connection_poller = 0;
-        }
+        return;
+    }
+
+    result = mm_strip_tag (result, "$NWQMISTATUS:");
+    if (g_strrstr (result, "QMI State: DISCONNECTED")) {
+        mm_bearer_report_disconnection (MM_BEARER (bearer));
+        g_source_remove (bearer->priv->connection_poller);
+        bearer->priv->connection_poller = 0;
     }
 }
 
@@ -157,6 +143,7 @@
 poll_connection (MMBroadbandBearerNovatel *bearer)
 {
     MMBaseModem *modem = NULL;
+
     g_object_get (MM_BEARER (bearer),
                   MM_BEARER_MODEM, &modem,
                   NULL);
@@ -168,6 +155,7 @@
         (GAsyncReadyCallback)poll_connection_ready,
         bearer);
     g_object_unref (modem);
+
     return TRUE;
 }
 
@@ -185,35 +173,41 @@
     if (!result) {
         mm_warn ("QMI connection status failed: %s", error->message);
         g_simple_async_result_take_error (ctx->result, error);
-    } else {
-        result = mm_strip_tag (result, "$NWQMISTATUS:");
-        if (g_strrstr(result, "QMI State: CONNECTED")) {
-            MMBearerIpConfig *config;
-            MMBroadbandBearerNovatel *bearer = MM_BROADBAND_BEARER_NOVATEL (ctx->self);
-            mm_dbg("Connected");
-            bearer->priv->connection_poller = g_timeout_add_seconds (CONNECTION_CHECK_TIMEOUT_SEC,
-                                                                     (GSourceFunc)poll_connection,
-                                                                     bearer);
-            config = mm_bearer_ip_config_new ();
-            mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP);
-            g_simple_async_result_set_op_res_gpointer (ctx->result,
-                                                       config,
-                                                       (GDestroyNotify)g_object_unref);
-        } else {
-            mm_dbg("Error: '%s'", result);
-            if (ctx->retries > 0) {
-                ctx->retries--;
-                mm_dbg("Retrying status check in a second. %d retries left.",
-                       ctx->retries);
-                g_timeout_add_seconds(1, (GSourceFunc)connect_3gpp_qmistatus, ctx);
-                return;
-            }
-            g_simple_async_result_set_error (ctx->result,
-                                             MM_CORE_ERROR,
-                                             MM_CORE_ERROR_FAILED,
-                                             "%s", result);
-        }
+        detailed_connect_context_complete_and_free (ctx);
+        return;
     }
+
+    result = mm_strip_tag (result, "$NWQMISTATUS:");
+    if (g_strrstr (result, "QMI State: CONNECTED")) {
+        MMBearerIpConfig *config;
+
+        mm_dbg("Connected");
+        ctx->self->priv->connection_poller = g_timeout_add_seconds (CONNECTION_CHECK_TIMEOUT_SEC,
+                                                                    (GSourceFunc)poll_connection,
+                                                                    ctx->self);
+        config = mm_bearer_ip_config_new ();
+        mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP);
+        g_simple_async_result_set_op_res_gpointer (ctx->result,
+                                                   config,
+                                                   (GDestroyNotify)g_object_unref);
+        detailed_connect_context_complete_and_free (ctx);
+        return;
+    }
+
+    mm_dbg ("Error: '%s'", result);
+    if (ctx->retries > 0) {
+        ctx->retries--;
+        mm_dbg ("Retrying status check in a second. %d retries left.",
+                ctx->retries);
+        g_timeout_add_seconds (1, (GSourceFunc)connect_3gpp_qmistatus, ctx);
+        return;
+    }
+
+    /* Already exhausted all retries */
+    g_simple_async_result_set_error (ctx->result,
+                                     MM_CORE_ERROR,
+                                     MM_CORE_ERROR_FAILED,
+                                     "%s", result);
     detailed_connect_context_complete_and_free (ctx);
 }
 
@@ -255,7 +249,7 @@
      * happened. Instead, we need to poll the modem to see if it's
      * ready.
      */
-    g_timeout_add_seconds(1, (GSourceFunc)connect_3gpp_qmistatus, ctx);
+    g_timeout_add_seconds (1, (GSourceFunc)connect_3gpp_qmistatus, ctx);
 }
 
 static void
@@ -268,9 +262,9 @@
               GAsyncReadyCallback callback,
               gpointer user_data)
 {
-    MMBroadbandBearerNovatel *self = MM_BROADBAND_BEARER_NOVATEL (bearer);
     DetailedConnectContext *ctx;
     gchar *command, *apn, *user, *password;
+    MMBearerProperties *config;
 
     ctx = detailed_connect_context_new (bearer,
                                         modem,
@@ -280,9 +274,10 @@
                                         callback,
                                         user_data);
 
-    apn = mm_at_serial_port_quote_string (mm_broadband_bearer_get_3gpp_apn (bearer));
-    user = mm_at_serial_port_quote_string (self->priv->user);
-    password = mm_at_serial_port_quote_string (self->priv->password);
+    config = mm_bearer_peek_config (MM_BEARER (bearer));
+    apn = mm_at_serial_port_quote_string (mm_bearer_properties_get_apn (config));
+    user = mm_at_serial_port_quote_string (mm_bearer_properties_get_user (config));
+    password = mm_at_serial_port_quote_string (mm_bearer_properties_get_password (config));
     command = g_strdup_printf ("$NWQMICONNECT=,,,,,,%s,,,%s,%s",
                                apn, user, password);
     g_free (apn);
@@ -298,7 +293,6 @@
     g_free (command);
 }
 
-
 typedef struct {
     MMBroadbandBearer *self;
     MMBaseModem *modem;
@@ -308,7 +302,6 @@
     GSimpleAsyncResult *result;
 } DetailedDisconnectContext;
 
-
 static DetailedDisconnectContext *
 detailed_disconnect_context_new (MMBroadbandBearer *self,
                                  MMBroadbandModem *modem,
@@ -366,21 +359,24 @@
     result = mm_base_modem_at_command_finish (MM_BASE_MODEM (modem),
                                               res,
                                               &error);
-
-    g_simple_async_result_set_op_res_gboolean (ctx->result, FALSE);
     if (error) {
         mm_dbg("QMI connection status failed: %s", error->message);
         g_error_free (error);
     }
 
     result = mm_strip_tag (result, "$NWQMISTATUS:");
-    if (g_strrstr(result, "QMI State: DISCONNECTED"))
+    if (result && g_strrstr (result, "QMI State: DISCONNECTED"))
         g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+    else
+        g_simple_async_result_set_error (ctx->result,
+                                         MM_CORE_ERROR,
+                                         MM_CORE_ERROR_FAILED,
+                                         "Error checking if disconnected (%s)",
+                                         result ? result : "no result");
 
     detailed_disconnect_context_complete_and_free (ctx);
 }
 
-
 static void
 disconnect_3gpp_check_status (MMBaseModem *modem,
                               GAsyncResult *res,
@@ -435,14 +431,11 @@
         ctx); /* user_data */
 }
 
-
 static void
 finalize (GObject *object)
 {
     MMBroadbandBearerNovatel *self = MM_BROADBAND_BEARER_NOVATEL (object);
 
-    g_free (self->priv->user);
-    g_free (self->priv->password);
     if (self->priv->connection_poller)
         g_source_remove (self->priv->connection_poller);
 
@@ -450,94 +443,12 @@
 }
 
 static void
-set_property (GObject *object,
-              guint prop_id,
-              const GValue *value,
-              GParamSpec *pspec)
-{
-    MMBroadbandBearerNovatel *self = MM_BROADBAND_BEARER_NOVATEL (object);
-
-    switch (prop_id) {
-    case PROP_USER:
-        g_free (self->priv->user);
-        self->priv->user = g_value_dup_string (value);
-        break;
-    case PROP_PASSWORD:
-        g_free (self->priv->password);
-        self->priv->password = g_value_dup_string (value);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-        break;
-    }
-}
-
-static void
-get_property (GObject *object,
-              guint prop_id,
-              GValue *value,
-              GParamSpec *pspec)
-{
-    MMBroadbandBearerNovatel *self = MM_BROADBAND_BEARER_NOVATEL (object);
-
-    switch (prop_id) {
-    case PROP_USER:
-        g_value_set_string (value, self->priv->user);
-        break;
-    case PROP_PASSWORD:
-        g_value_set_string (value, self->priv->password);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-        break;
-    }
-}
-
-static gboolean
-cmp_properties (MMBearer *bearer,
-                MMBearerProperties *properties)
-{
-    MMBroadbandBearerNovatel *self = MM_BROADBAND_BEARER_NOVATEL (bearer);
-
-    return ((mm_broadband_bearer_get_allow_roaming (MM_BROADBAND_BEARER (self)) ==
-             mm_bearer_properties_get_allow_roaming (properties)) &&
-            (!g_strcmp0 (mm_broadband_bearer_get_ip_type (MM_BROADBAND_BEARER (self)),
-                         mm_bearer_properties_get_ip_type (properties))) &&
-            (!g_strcmp0 (mm_broadband_bearer_get_3gpp_apn (MM_BROADBAND_BEARER (self)),
-                         mm_bearer_properties_get_apn (properties))) &&
-            (!g_strcmp0 (self->priv->user,
-                         mm_bearer_properties_get_user (properties))) &&
-            (!g_strcmp0 (self->priv->password,
-                         mm_bearer_properties_get_password (properties))));
-}
-
-static MMBearerProperties *
-expose_properties (MMBearer *bearer)
-{
-    MMBroadbandBearerNovatel *self = MM_BROADBAND_BEARER_NOVATEL (bearer);
-    MMBearerProperties *properties;
-
-    properties = mm_bearer_properties_new ();
-    mm_bearer_properties_set_apn (properties,
-                                  mm_broadband_bearer_get_3gpp_apn (MM_BROADBAND_BEARER (self)));
-    mm_bearer_properties_set_ip_type (properties,
-                                      mm_broadband_bearer_get_ip_type (MM_BROADBAND_BEARER (self)));
-    mm_bearer_properties_set_allow_roaming (properties,
-                                            mm_broadband_bearer_get_allow_roaming (MM_BROADBAND_BEARER (self)));
-    mm_bearer_properties_set_user (properties, self->priv->user);
-    mm_bearer_properties_set_password (properties, self->priv->user);
-    return properties;
-}
-
-static void
 mm_broadband_bearer_novatel_init (MMBroadbandBearerNovatel *self)
 {
     self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
                                               MM_TYPE_BROADBAND_BEARER_NOVATEL,
                                               MMBroadbandBearerNovatelPrivate);
 
-    self->priv->user = NULL;
-    self->priv->password = NULL;
     self->priv->connection_poller = 0;
 }
 
@@ -545,38 +456,16 @@
 mm_broadband_bearer_novatel_class_init (MMBroadbandBearerNovatelClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
-    MMBearerClass *bearer_class = MM_BEARER_CLASS (klass);
     MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
 
     g_type_class_add_private (object_class, sizeof (MMBroadbandBearerNovatelPrivate));
 
-    object_class->get_property = get_property;
-    object_class->set_property = set_property;
     object_class->finalize = finalize;
 
-    bearer_class->cmp_properties = cmp_properties;
-    bearer_class->expose_properties = expose_properties;
-
     broadband_bearer_class->connect_3gpp = connect_3gpp;
     broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish;
     broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;
     broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish;
-
-    properties[PROP_USER] =
-        g_param_spec_string (MM_BROADBAND_BEARER_NOVATEL_USER,
-                             "User",
-                             "Username to authenticate to APN",
-                             NULL,
-                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-    g_object_class_install_property (object_class, PROP_USER, properties[PROP_USER]);
-
-    properties[PROP_PASSWORD] =
-        g_param_spec_string (MM_BROADBAND_BEARER_NOVATEL_PASSWORD,
-                             "Password",
-                             "Password to authenticate to APN",
-                             NULL,
-                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-    g_object_class_install_property (object_class, PROP_PASSWORD, properties[PROP_PASSWORD]);
 }
 
 MMBearer *
@@ -599,11 +488,12 @@
     return MM_BEARER (bearer);
 }
 
-void mm_broadband_bearer_novatel_new (MMBroadbandModemNovatel *modem,
-                                      MMBearerProperties *properties,
-                                      GCancellable *cancellable,
-                                      GAsyncReadyCallback callback,
-                                      gpointer user_data)
+void
+mm_broadband_bearer_novatel_new (MMBroadbandModemNovatel *modem,
+                                 MMBearerProperties *config,
+                                 GCancellable *cancellable,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
 {
     g_async_initable_new_async (
         MM_TYPE_BROADBAND_BEARER_NOVATEL,
@@ -612,10 +502,6 @@
         callback,
         user_data,
         MM_BEARER_MODEM, modem,
-        MM_BROADBAND_BEARER_3GPP_APN,         mm_bearer_properties_get_apn (properties),
-        MM_BROADBAND_BEARER_IP_TYPE,          mm_bearer_properties_get_ip_type (properties),
-        MM_BROADBAND_BEARER_ALLOW_ROAMING,    mm_bearer_properties_get_allow_roaming (properties),
-        MM_BROADBAND_BEARER_NOVATEL_USER,     mm_bearer_properties_get_user (properties),
-        MM_BROADBAND_BEARER_NOVATEL_PASSWORD, mm_bearer_properties_get_password (properties),
+        MM_BEARER_CONFIG, config,
         NULL);
 }
diff --git a/plugins/option/mm-broadband-bearer-hso.c b/plugins/option/mm-broadband-bearer-hso.c
index 1d8e1d1..d2cfd91 100644
--- a/plugins/option/mm-broadband-bearer-hso.c
+++ b/plugins/option/mm-broadband-bearer-hso.c
@@ -36,20 +36,8 @@
 
 G_DEFINE_TYPE (MMBroadbandBearerHso, mm_broadband_bearer_hso, MM_TYPE_BROADBAND_BEARER);
 
-enum {
-    PROP_0,
-    PROP_USER,
-    PROP_PASSWORD,
-    PROP_LAST
-};
-
-static GParamSpec *properties[PROP_LAST];
-
 struct _MMBroadbandBearerHsoPrivate {
-    gchar *user;
-    gchar *password;
     guint auth_idx;
-
     gpointer connect_pending;
     guint connect_pending_id;
     gulong connect_cancellable_id;
@@ -544,6 +532,8 @@
 authenticate (Dial3gppContext *ctx)
 {
     gchar *command;
+    const gchar *user;
+    const gchar *password;
 
     if (!auth_commands[ctx->auth_idx]) {
         g_simple_async_result_set_error (ctx->result,
@@ -554,8 +544,11 @@
         return;
     }
 
+    user = mm_bearer_properties_get_user (mm_bearer_peek_config (MM_BEARER (ctx->self)));
+    password = mm_bearer_properties_get_password (mm_bearer_peek_config (MM_BEARER (ctx->self)));
+
     /* Both user and password are required; otherwise firmware returns an error */
-    if (!ctx->self->priv->user || !ctx->self->priv->password)
+    if (!user || !password)
 		command = g_strdup_printf ("%s=%d,0",
                                    auth_commands[ctx->auth_idx],
                                    ctx->cid);
@@ -563,8 +556,8 @@
         command = g_strdup_printf ("%s=%d,1,\"%s\",\"%s\"",
                                    auth_commands[ctx->auth_idx],
                                    ctx->cid,
-                                   ctx->self->priv->password,
-                                   ctx->self->priv->user);
+                                   password,
+                                   user);
 
     mm_base_modem_at_command_full (ctx->modem,
                                    ctx->primary,
@@ -683,44 +676,6 @@
 
 /*****************************************************************************/
 
-static gboolean
-cmp_properties (MMBearer *self,
-                MMBearerProperties *properties)
-{
-    MMBroadbandBearerHso *hso = MM_BROADBAND_BEARER_HSO (self);
-
-    return ((mm_broadband_bearer_get_allow_roaming (MM_BROADBAND_BEARER (self)) ==
-             mm_bearer_properties_get_allow_roaming (properties)) &&
-            (!g_strcmp0 (mm_broadband_bearer_get_ip_type (MM_BROADBAND_BEARER (self)),
-                         mm_bearer_properties_get_ip_type (properties))) &&
-            (!g_strcmp0 (mm_broadband_bearer_get_3gpp_apn (MM_BROADBAND_BEARER (self)),
-                         mm_bearer_properties_get_apn (properties))) &&
-            (!g_strcmp0 (hso->priv->user,
-                         mm_bearer_properties_get_user (properties))) &&
-            (!g_strcmp0 (hso->priv->password,
-                         mm_bearer_properties_get_password (properties))));
-}
-
-static MMBearerProperties *
-expose_properties (MMBearer *self)
-{
-    MMBroadbandBearerHso *hso = MM_BROADBAND_BEARER_HSO (self);
-    MMBearerProperties *properties;
-
-    properties = mm_bearer_properties_new ();
-    mm_bearer_properties_set_apn (properties,
-                                  mm_broadband_bearer_get_3gpp_apn (MM_BROADBAND_BEARER (self)));
-    mm_bearer_properties_set_ip_type (properties,
-                                      mm_broadband_bearer_get_ip_type (MM_BROADBAND_BEARER (self)));
-    mm_bearer_properties_set_allow_roaming (properties,
-                                            mm_broadband_bearer_get_allow_roaming (MM_BROADBAND_BEARER (self)));
-    mm_bearer_properties_set_user (properties, hso->priv->user);
-    mm_bearer_properties_set_password (properties, hso->priv->user);
-    return properties;
-}
-
-/*****************************************************************************/
-
 MMBearer *
 mm_broadband_bearer_hso_new_finish (GAsyncResult *res,
                                     GError **error)
@@ -743,7 +698,7 @@
 
 void
 mm_broadband_bearer_hso_new (MMBroadbandModemHso *modem,
-                             MMBearerProperties *properties,
+                             MMBearerProperties *config,
                              GCancellable *cancellable,
                              GAsyncReadyCallback callback,
                              gpointer user_data)
@@ -755,59 +710,11 @@
         callback,
         user_data,
         MM_BEARER_MODEM, modem,
-        MM_BROADBAND_BEARER_3GPP_APN,      mm_bearer_properties_get_apn (properties),
-        MM_BROADBAND_BEARER_IP_TYPE,       mm_bearer_properties_get_ip_type (properties),
-        MM_BROADBAND_BEARER_ALLOW_ROAMING, mm_bearer_properties_get_allow_roaming (properties),
-        MM_BROADBAND_BEARER_HSO_USER,      mm_bearer_properties_get_user (properties),
-        MM_BROADBAND_BEARER_HSO_PASSWORD,  mm_bearer_properties_get_password (properties),
+        MM_BEARER_CONFIG, config,
         NULL);
 }
 
 static void
-set_property (GObject *object,
-              guint prop_id,
-              const GValue *value,
-              GParamSpec *pspec)
-{
-    MMBroadbandBearerHso *self = MM_BROADBAND_BEARER_HSO (object);
-
-    switch (prop_id) {
-    case PROP_USER:
-        g_free (self->priv->user);
-        self->priv->user = g_value_dup_string (value);
-        break;
-    case PROP_PASSWORD:
-        g_free (self->priv->password);
-        self->priv->password = g_value_dup_string (value);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-        break;
-    }
-}
-
-static void
-get_property (GObject *object,
-              guint prop_id,
-              GValue *value,
-              GParamSpec *pspec)
-{
-    MMBroadbandBearerHso *self = MM_BROADBAND_BEARER_HSO (object);
-
-    switch (prop_id) {
-    case PROP_USER:
-        g_value_set_string (value, self->priv->user);
-        break;
-    case PROP_PASSWORD:
-        g_value_set_string (value, self->priv->password);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-        break;
-    }
-}
-
-static void
 mm_broadband_bearer_hso_init (MMBroadbandBearerHso *self)
 {
     /* Initialize private data */
@@ -817,52 +724,17 @@
 }
 
 static void
-finalize (GObject *object)
-{
-    MMBroadbandBearerHso *self = MM_BROADBAND_BEARER_HSO (object);
-
-    g_free (self->priv->user);
-    g_free (self->priv->password);
-
-    G_OBJECT_CLASS (mm_broadband_bearer_hso_parent_class)->finalize (object);
-}
-
-static void
 mm_broadband_bearer_hso_class_init (MMBroadbandBearerHsoClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
-    MMBearerClass *bearer_class = MM_BEARER_CLASS (klass);
     MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
 
     g_type_class_add_private (object_class, sizeof (MMBroadbandBearerHsoPrivate));
 
-    object_class->get_property = get_property;
-    object_class->set_property = set_property;
-    object_class->finalize = finalize;
-
-    bearer_class->cmp_properties = cmp_properties;
-    bearer_class->expose_properties = expose_properties;
-
     broadband_bearer_class->dial_3gpp = dial_3gpp;
     broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish;
     broadband_bearer_class->get_ip_config_3gpp = get_ip_config_3gpp;
     broadband_bearer_class->get_ip_config_3gpp_finish = get_ip_config_3gpp_finish;
     broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;
     broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish;
-
-    properties[PROP_USER] =
-        g_param_spec_string (MM_BROADBAND_BEARER_HSO_USER,
-                             "User",
-                             "Username to use to authenticate the connection",
-                             NULL,
-                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-    g_object_class_install_property (object_class, PROP_USER, properties[PROP_USER]);
-
-    properties[PROP_PASSWORD] =
-        g_param_spec_string (MM_BROADBAND_BEARER_HSO_PASSWORD,
-                             "Password",
-                             "Password to use to authenticate the connection",
-                             NULL,
-                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-    g_object_class_install_property (object_class, PROP_PASSWORD, properties[PROP_PASSWORD]);
 }
diff --git a/plugins/option/mm-broadband-bearer-hso.h b/plugins/option/mm-broadband-bearer-hso.h
index 4773d46..24e7aa3 100644
--- a/plugins/option/mm-broadband-bearer-hso.h
+++ b/plugins/option/mm-broadband-bearer-hso.h
@@ -31,9 +31,6 @@
 #define MM_IS_BROADBAND_BEARER_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_BROADBAND_BEARER_HSO))
 #define MM_BROADBAND_BEARER_HSO_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_BROADBAND_BEARER_HSO, MMBroadbandBearerHsoClass))
 
-#define MM_BROADBAND_BEARER_HSO_USER     "broadband-bearer-hso-user"
-#define MM_BROADBAND_BEARER_HSO_PASSWORD "broadband-bearer-hso-password"
-
 typedef enum {
     MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_UNKNOWN,
     MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_CONNECTED,
@@ -58,7 +55,7 @@
 
 /* Default 3GPP bearer creation implementation */
 void mm_broadband_bearer_hso_new (MMBroadbandModemHso *modem,
-                                  MMBearerProperties *properties,
+                                  MMBearerProperties *config,
                                   GCancellable *cancellable,
                                   GAsyncReadyCallback callback,
                                   gpointer user_data);
diff --git a/plugins/wavecom/mm-broadband-modem-wavecom.c b/plugins/wavecom/mm-broadband-modem-wavecom.c
new file mode 100644
index 0000000..9be84c0
--- /dev/null
+++ b/plugins/wavecom/mm-broadband-modem-wavecom.c
@@ -0,0 +1,1231 @@
+/* -*- 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) 2011 Ammonit Measurement GmbH
+ * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
+ * Author: Aleksander Morgado <aleksander@lanedo.com>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include <libmm-common.h>
+
+#include "ModemManager.h"
+#include "mm-log.h"
+#include "mm-serial-parsers.h"
+#include "mm-modem-helpers.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-3gpp.h"
+#include "mm-base-modem-at.h"
+#include "mm-broadband-modem-wavecom.h"
+
+static void iface_modem_init (MMIfaceModem *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemWavecom, mm_broadband_modem_wavecom, MM_TYPE_BROADBAND_MODEM, 0,
+                        G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init))
+
+#define WAVECOM_MS_CLASS_CC_IDSTR "\"CC\""
+#define WAVECOM_MS_CLASS_CG_IDSTR "\"CG\""
+#define WAVECOM_MS_CLASS_B_IDSTR  "\"B\""
+#define WAVECOM_MS_CLASS_A_IDSTR  "\"A\""
+
+/* Setup relationship between 2G bands in the modem (identified by a
+ * single digit in ASCII) and the bitmask in ModemManager. */
+typedef struct {
+    gchar wavecom_band;
+    guint n_mm_bands;
+    MMModemBand mm_bands[4];
+} WavecomBand2G;
+static const WavecomBand2G bands_2g[] = {
+    { '0', 1, { MM_MODEM_BAND_G850, 0, 0, 0 }},
+    { '1', 1, { MM_MODEM_BAND_EGSM, 0, 0, 0 }},
+    { '2', 1, { MM_MODEM_BAND_DCS,  0, 0, 0 }},
+    { '3', 1, { MM_MODEM_BAND_PCS,  0, 0, 0 }},
+    { '4', 2, { MM_MODEM_BAND_G850, MM_MODEM_BAND_PCS, 0, 0 }},
+    { '5', 2, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, 0, 0 }},
+    { '6', 2, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_PCS, 0, 0 }},
+    { '7', 4, { MM_MODEM_BAND_DCS,  MM_MODEM_BAND_PCS, MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM }}
+};
+
+/* Setup relationship between the 3G band bitmask in the modem and the bitmask
+ * in ModemManager. */
+typedef struct {
+    guint32 wavecom_band_flag;
+    MMModemBand mm_band;
+} WavecomBand3G;
+static const WavecomBand3G bands_3g[] = {
+    { (1 << 0), MM_MODEM_BAND_U2100 },
+    { (1 << 1), MM_MODEM_BAND_U1900 },
+    { (1 << 2), MM_MODEM_BAND_U1800 },
+    { (1 << 3), MM_MODEM_BAND_U17IV },
+    { (1 << 4), MM_MODEM_BAND_U850  },
+    { (1 << 5), MM_MODEM_BAND_U800  },
+    { (1 << 6), MM_MODEM_BAND_U2600 },
+    { (1 << 7), MM_MODEM_BAND_U900  },
+    { (1 << 8), MM_MODEM_BAND_U17IX }
+};
+
+/*****************************************************************************/
+/* Supported modes (Modem interface) */
+
+static MMModemMode
+load_supported_modes_finish (MMIfaceModem *self,
+                             GAsyncResult *res,
+                             GError **error)
+{
+    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+        return MM_MODEM_MODE_NONE;
+
+    return (MMModemMode) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
+                                               G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+static void
+supported_ms_classes_query_ready (MMBaseModem *self,
+                                  GAsyncResult *res,
+                                  GSimpleAsyncResult *simple)
+{
+    const gchar *response;
+    GError *error = NULL;
+    MMModemMode mode;
+
+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+    if (!response) {
+        /* Let the error be critical. */
+        g_simple_async_result_take_error (simple, error);
+        g_simple_async_result_complete (simple);
+        g_object_unref (simple);
+        return;
+    }
+
+    response = mm_strip_tag (response, "+CGCLASS:");
+    mode = MM_MODEM_MODE_NONE;
+
+    if (strstr (response, WAVECOM_MS_CLASS_A_IDSTR)) {
+        mm_dbg ("Modem supports Class A mobile station");
+        mode |= MM_MODEM_MODE_3G;
+    }
+
+    if (strstr (response, WAVECOM_MS_CLASS_B_IDSTR)) {
+        mm_dbg ("Modem supports Class B mobile station");
+        mode |= (MM_MODEM_MODE_2G | MM_MODEM_MODE_CS);
+    }
+
+    if (strstr (response, WAVECOM_MS_CLASS_CG_IDSTR)) {
+        mm_dbg ("Modem supports Class CG mobile station");
+        mode |= MM_MODEM_MODE_2G;
+    }
+
+    if (strstr (response, WAVECOM_MS_CLASS_CC_IDSTR)) {
+        mm_dbg ("Modem supports Class CC mobile station");
+        mode |= MM_MODEM_MODE_CS;
+    }
+
+    /* If none received, error */
+    if (mode == MM_MODEM_MODE_NONE)
+        g_simple_async_result_set_error (simple,
+                                         MM_CORE_ERROR,
+                                         MM_CORE_ERROR_FAILED,
+                                         "Couldn't get supported mobile station classes: '%s'",
+                                         response);
+    else
+        g_simple_async_result_set_op_res_gpointer (simple,
+                                                   GUINT_TO_POINTER (mode),
+                                                   NULL);
+
+    g_simple_async_result_complete (simple);
+    g_object_unref (simple);
+}
+
+static void
+load_supported_modes (MMIfaceModem *self,
+                      GAsyncReadyCallback callback,
+                      gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        load_supported_modes);
+
+    mm_base_modem_at_command (
+        MM_BASE_MODEM (self),
+        "+CGCLASS=?",
+        3,
+        FALSE,
+        (GAsyncReadyCallback)supported_ms_classes_query_ready,
+        result);
+}
+
+/*****************************************************************************/
+/* Load initial allowed/preferred modes (Modem interface) */
+
+typedef struct {
+    MMModemMode allowed;
+    MMModemMode preferred;
+} LoadAllowedModesResult;
+
+static gboolean
+load_allowed_modes_finish (MMIfaceModem *self,
+                           GAsyncResult *res,
+                           MMModemMode *allowed,
+                           MMModemMode *preferred,
+                           GError **error)
+{
+    LoadAllowedModesResult *result;
+
+    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+        return FALSE;
+
+    result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+
+    *allowed = result->allowed;
+    *preferred = result->preferred;
+    return TRUE;
+}
+
+static void
+wwsm_read_ready (MMBaseModem *self,
+                 GAsyncResult *res,
+                 GSimpleAsyncResult *simple)
+{
+    GRegex *r;
+    GMatchInfo *match_info = NULL;
+    LoadAllowedModesResult result;
+    const gchar *response;
+    GError *error = NULL;
+
+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+    if (!response) {
+        g_simple_async_result_take_error (simple, error);
+        g_simple_async_result_complete (simple);
+        g_object_unref (simple);
+        return;
+    }
+
+    result.allowed = MM_MODEM_MODE_NONE;
+    result.preferred = MM_MODEM_MODE_NONE;
+
+    /* Possible responses:
+     *   +WWSM: 0    (2G only)
+     *   +WWSM: 1    (3G only)
+     *   +WWSM: 2,0  (Any)
+     *   +WWSM: 2,1  (2G preferred)
+     *   +WWSM: 2,2  (3G preferred)
+     */
+    r = g_regex_new ("\\r\\n\\+WWSM: ([0-2])(,([0-2]))?.*$", 0, 0, NULL);
+    g_assert (r != NULL);
+
+    if (g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, NULL)) {
+        guint allowed = 0;
+
+        if (mm_get_uint_from_match_info (match_info, 1, &allowed)) {
+            switch (allowed) {
+            case 0:
+                result.allowed = MM_MODEM_MODE_2G;
+                result.preferred = MM_MODEM_MODE_NONE;
+                break;
+            case 1:
+                result.allowed = MM_MODEM_MODE_3G;
+                result.preferred = MM_MODEM_MODE_NONE;
+                break;
+            case 2: {
+                guint preferred = 0;
+
+                result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+
+                /* 3, to avoid the comma */
+                if (mm_get_uint_from_match_info (match_info, 3, &preferred)) {
+                    switch (preferred) {
+                    case 0:
+                        result.preferred = MM_MODEM_MODE_NONE;
+                        break;
+                    case 1:
+                        result.preferred = MM_MODEM_MODE_2G;
+                        break;
+                    case 2:
+                        result.preferred = MM_MODEM_MODE_3G;
+                        break;
+                    default:
+                        g_warn_if_reached ();
+                        break;
+                    }
+                }
+                break;
+            }
+            default:
+                g_warn_if_reached ();
+                break;
+            }
+        }
+    }
+
+    if (result.allowed == MM_MODEM_MODE_NONE)
+        g_simple_async_result_set_error (simple,
+                                         MM_CORE_ERROR,
+                                         MM_CORE_ERROR_FAILED,
+                                         "Unknown wireless data service reply: '%s'",
+                                         response);
+    else
+        g_simple_async_result_set_op_res_gpointer (simple, &result, NULL);
+    g_simple_async_result_complete (simple);
+    g_object_unref (simple);
+
+    g_regex_unref (r);
+    if (match_info)
+        g_match_info_free (match_info);
+}
+
+static void
+current_ms_class_ready (MMBaseModem *self,
+                        GAsyncResult *res,
+                        GSimpleAsyncResult *simple)
+{
+    LoadAllowedModesResult result;
+    const gchar *response;
+    GError *error = NULL;
+
+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+    if (!response) {
+        g_simple_async_result_take_error (simple, error);
+        g_simple_async_result_complete (simple);
+        g_object_unref (simple);
+        return;
+    }
+
+    response = mm_strip_tag (response, "+CGCLASS:");
+
+    if (strncmp (response,
+                 WAVECOM_MS_CLASS_A_IDSTR,
+                 strlen (WAVECOM_MS_CLASS_A_IDSTR)) == 0) {
+        mm_dbg ("Modem configured as a Class A mobile station");
+        /* For 3G devices, query WWSM status */
+        mm_base_modem_at_command (MM_BASE_MODEM (self),
+                                  "+WWSM?",
+                                  3,
+                                  FALSE,
+                                  (GAsyncReadyCallback)wwsm_read_ready,
+                                  simple);
+        return;
+    }
+
+    result.allowed = MM_MODEM_MODE_NONE;
+    result.preferred = MM_MODEM_MODE_NONE;
+
+    if (strncmp (response,
+                 WAVECOM_MS_CLASS_B_IDSTR,
+                 strlen (WAVECOM_MS_CLASS_B_IDSTR)) == 0) {
+        mm_dbg ("Modem configured as a Class B mobile station");
+        result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_CS);
+        result.preferred = MM_MODEM_MODE_2G;
+    } else if (strncmp (response,
+                        WAVECOM_MS_CLASS_CG_IDSTR,
+                        strlen (WAVECOM_MS_CLASS_CG_IDSTR)) == 0) {
+        mm_dbg ("Modem configured as a Class CG mobile station");
+        result.allowed = MM_MODEM_MODE_2G;
+        result.preferred = MM_MODEM_MODE_NONE;
+    } else if (strncmp (response,
+                        WAVECOM_MS_CLASS_CC_IDSTR,
+                        strlen (WAVECOM_MS_CLASS_CC_IDSTR)) == 0) {
+        mm_dbg ("Modem configured as a Class CC mobile station");
+        result.allowed = MM_MODEM_MODE_CS;
+        result.preferred = MM_MODEM_MODE_NONE;
+    }
+
+    if (result.allowed == MM_MODEM_MODE_NONE)
+        g_simple_async_result_set_error (simple,
+                                         MM_CORE_ERROR,
+                                         MM_CORE_ERROR_FAILED,
+                                         "Unknown mobile station class: '%s'",
+                                         response);
+    else
+        g_simple_async_result_set_op_res_gpointer (simple, &result, NULL);
+    g_simple_async_result_complete (simple);
+    g_object_unref (simple);
+}
+
+static void
+load_allowed_modes (MMIfaceModem *self,
+                    GAsyncReadyCallback callback,
+                    gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        load_allowed_modes);
+
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "+CGCLASS?",
+                              3,
+                              FALSE,
+                              (GAsyncReadyCallback)current_ms_class_ready,
+                              result);
+}
+
+/*****************************************************************************/
+/* Set allowed modes (Modem interface) */
+
+typedef struct {
+    MMBroadbandModemWavecom *self;
+    GSimpleAsyncResult *result;
+    MMModemMode allowed;
+    MMModemMode preferred;
+    gchar *cgclass_command;
+    gchar *wwsm_command;
+} SetAllowedModesContext;
+
+static void
+set_allowed_modes_context_complete_and_free (SetAllowedModesContext *ctx)
+{
+    g_simple_async_result_complete_in_idle (ctx->result);
+    g_object_unref (ctx->result);
+    g_object_unref (ctx->self);
+    g_free (ctx->cgclass_command);
+    g_free (ctx->wwsm_command);
+    g_free (ctx);
+}
+
+static gboolean
+set_allowed_modes_finish (MMIfaceModem *self,
+                          GAsyncResult *res,
+                          GError **error)
+{
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+wwsm_update_ready (MMBaseModem *self,
+                   GAsyncResult *res,
+                   SetAllowedModesContext *ctx)
+{
+    GError *error = NULL;
+
+    mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+    if (error)
+        /* Let the error be critical. */
+        g_simple_async_result_take_error (ctx->result, error);
+    else
+        g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+
+    set_allowed_modes_context_complete_and_free (ctx);
+}
+
+static void
+cgclass_update_ready (MMBaseModem *self,
+                      GAsyncResult *res,
+                      SetAllowedModesContext *ctx)
+{
+    GError *error = NULL;
+
+    mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+    if (error) {
+        /* Let the error be critical. */
+        g_simple_async_result_take_error (ctx->result, error);
+        set_allowed_modes_context_complete_and_free (ctx);
+        return;
+    }
+
+    if (!ctx->wwsm_command) {
+        g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+        set_allowed_modes_context_complete_and_free (ctx);
+        return;
+    }
+
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              ctx->wwsm_command,
+                              3,
+                              FALSE,
+                              (GAsyncReadyCallback)wwsm_update_ready,
+                              ctx);
+}
+
+static void
+set_allowed_modes (MMIfaceModem *self,
+                   MMModemMode allowed,
+                   MMModemMode preferred,
+                   GAsyncReadyCallback callback,
+                   gpointer user_data)
+{
+    SetAllowedModesContext *ctx;
+
+    ctx = g_new (SetAllowedModesContext, 1);
+    ctx->self = g_object_ref (self);
+    ctx->result = g_simple_async_result_new (G_OBJECT (self),
+                                             callback,
+                                             user_data,
+                                             set_allowed_modes);
+    ctx->allowed = allowed;
+    ctx->preferred = preferred;
+
+    if (allowed == MM_MODEM_MODE_CS)
+        ctx->cgclass_command = g_strdup ("+CGCLASS=" WAVECOM_MS_CLASS_CC_IDSTR);
+    else if (allowed == MM_MODEM_MODE_2G)
+        ctx->cgclass_command = g_strdup ("+CGCLASS=" WAVECOM_MS_CLASS_CG_IDSTR);
+    else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_CS) &&
+             preferred == MM_MODEM_MODE_NONE)
+        ctx->cgclass_command = g_strdup ("+CGCLASS=" WAVECOM_MS_CLASS_B_IDSTR);
+    else if (allowed & MM_MODEM_MODE_3G) {
+        if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G) ||
+            allowed == (MM_MODEM_MODE_CS | MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) {
+            if (preferred == MM_MODEM_MODE_2G)
+                ctx->wwsm_command = g_strdup ("+WWSM=2,1");
+            else if (preferred == MM_MODEM_MODE_3G)
+                ctx->wwsm_command = g_strdup ("+WWSM=2,2");
+            else if (preferred == MM_MODEM_MODE_NONE)
+                ctx->wwsm_command = g_strdup ("+WWSM=2,0");
+        } else if (allowed == MM_MODEM_MODE_3G)
+            ctx->wwsm_command = g_strdup ("+WWSM=1");
+
+        if (ctx->wwsm_command)
+            ctx->cgclass_command = g_strdup ("+CGCLASS=" WAVECOM_MS_CLASS_A_IDSTR);
+    }
+
+    if (!ctx->cgclass_command) {
+        gchar *allowed_str;
+        gchar *preferred_str;
+
+        allowed_str = mm_modem_mode_build_string_from_mask (allowed);
+        preferred_str = mm_modem_mode_build_string_from_mask (preferred);
+        g_simple_async_result_set_error (ctx->result,
+                                         MM_CORE_ERROR,
+                                         MM_CORE_ERROR_FAILED,
+                                         "Requested mode (allowed: '%s', preferred: '%s') not "
+                                         "supported by the modem.",
+                                         allowed_str,
+                                         preferred_str);
+        g_free (allowed_str);
+        g_free (preferred_str);
+
+        set_allowed_modes_context_complete_and_free (ctx);
+        return;
+    }
+
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              ctx->cgclass_command,
+                              3,
+                              FALSE,
+                              (GAsyncReadyCallback)cgclass_update_ready,
+                              ctx);
+}
+
+/*****************************************************************************/
+/* Load supported bands (Modem interface) */
+
+static GArray *
+load_supported_bands_finish (MMIfaceModem *self,
+                             GAsyncResult *res,
+                             GError **error)
+{
+    /* Never fails */
+    return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer (
+                                       G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+static void
+load_supported_bands (MMIfaceModem *self,
+                      GAsyncReadyCallback callback,
+                      gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+    GArray *bands;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        load_supported_bands);
+
+    /* We do assume that we already know if the modem is 2G-only, 3G-only or
+     * 2G+3G. This is checked quite before trying to load supported bands. */
+
+    /* Add 3G-specific bands */
+    if (mm_iface_modem_is_3g (self)) {
+        bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 10);
+        g_array_index (bands, MMModemBand, 0) = MM_MODEM_BAND_U2100;
+        g_array_index (bands, MMModemBand, 1) = MM_MODEM_BAND_U1800;
+        g_array_index (bands, MMModemBand, 2) = MM_MODEM_BAND_U17IV;
+        g_array_index (bands, MMModemBand, 3) = MM_MODEM_BAND_U800;
+        g_array_index (bands, MMModemBand, 4) = MM_MODEM_BAND_U850;
+        g_array_index (bands, MMModemBand, 5) = MM_MODEM_BAND_U900;
+        g_array_index (bands, MMModemBand, 6) = MM_MODEM_BAND_U900;
+        g_array_index (bands, MMModemBand, 7) = MM_MODEM_BAND_U17IX;
+        g_array_index (bands, MMModemBand, 8) = MM_MODEM_BAND_U1900;
+        g_array_index (bands, MMModemBand, 9) = MM_MODEM_BAND_U2600;
+    }
+    /* Add 2G-specific bands */
+    else {
+        bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 4);
+        g_array_index (bands, MMModemBand, 0) = MM_MODEM_BAND_EGSM;
+        g_array_index (bands, MMModemBand, 1) = MM_MODEM_BAND_DCS;
+        g_array_index (bands, MMModemBand, 2) = MM_MODEM_BAND_PCS;
+        g_array_index (bands, MMModemBand, 3) = MM_MODEM_BAND_G850;
+    }
+
+    g_simple_async_result_set_op_res_gpointer (result,
+                                               bands,
+                                               (GDestroyNotify)g_array_unref);
+    g_simple_async_result_complete_in_idle (result);
+    g_object_unref (result);
+}
+
+/*****************************************************************************/
+/* Load current bands (Modem interface) */
+
+static GArray *
+load_current_bands_finish (MMIfaceModem *self,
+                           GAsyncResult *res,
+                           GError **error)
+{
+    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+        return NULL;
+
+    return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer (
+                                       G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+static void
+get_2g_band_ready (MMBroadbandModemWavecom *self,
+                   GAsyncResult *res,
+                   GSimpleAsyncResult *operation_result)
+{
+    const gchar *response;
+    const gchar *p;
+    GError *error = NULL;
+    GArray *bands_array = NULL;
+
+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+    if (!response) {
+        /* Let the error be critical. */
+        g_simple_async_result_take_error (operation_result, error);
+        g_simple_async_result_complete (operation_result);
+        g_object_unref (operation_result);
+        return;
+    }
+
+    p = mm_strip_tag (response, "+WMBS:");
+    if (p) {
+        guint i;
+
+        for (i = 0; i < G_N_ELEMENTS (bands_2g); i++) {
+            if (bands_2g[i].wavecom_band == *p) {
+                guint j;
+
+                if (G_UNLIKELY (!bands_array))
+                    bands_array = g_array_new (FALSE, FALSE, sizeof (MMModemBand));
+
+                for (j = 0; j < bands_2g[i].n_mm_bands; j++)
+                    g_array_append_val (bands_array, bands_2g[i].mm_bands[j]);
+            }
+        }
+    }
+
+    if (!bands_array)
+        g_simple_async_result_set_error (operation_result,
+                                         MM_CORE_ERROR,
+                                         MM_CORE_ERROR_FAILED,
+                                         "Couldn't parse current bands reply: '%s'",
+                                         response);
+    else
+        g_simple_async_result_set_op_res_gpointer (operation_result,
+                                                   bands_array,
+                                                   (GDestroyNotify)g_array_unref);
+
+    g_simple_async_result_complete (operation_result);
+    g_object_unref (operation_result);
+}
+
+static void
+get_3g_band_ready (MMBroadbandModemWavecom *self,
+                   GAsyncResult *res,
+                   GSimpleAsyncResult *operation_result)
+{
+    const gchar *response;
+    const gchar *p;
+    GError *error = NULL;
+    GArray *bands_array = NULL;
+    guint32 wavecom_band;
+
+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+    if (!response) {
+        /* Let the error be critical. */
+        g_simple_async_result_take_error (operation_result, error);
+        g_simple_async_result_complete (operation_result);
+        g_object_unref (operation_result);
+        return;
+    }
+
+    /* Example reply:
+     *   AT+WUBS? -->
+     *            <-- +WUBS: "3",1
+     *            <-- OK
+     * The "3" meaning here Band I and II are selected.
+     */
+
+    p = mm_strip_tag (response, "+WUBS:");
+    if (*p == '"')
+        p++;
+
+    wavecom_band = atoi (p);
+    if (wavecom_band > 0) {
+        guint i;
+
+        for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) {
+            if (bands_3g[i].wavecom_band_flag & wavecom_band) {
+                if (G_UNLIKELY (!bands_array))
+                    bands_array = g_array_new (FALSE, FALSE, sizeof (MMModemBand));
+                g_array_append_val (bands_array, bands_3g[i].mm_band);
+            }
+        }
+    }
+
+    if (!bands_array)
+        g_simple_async_result_set_error (operation_result,
+                                         MM_CORE_ERROR,
+                                         MM_CORE_ERROR_FAILED,
+                                         "Couldn't parse current bands reply: '%s'",
+                                         response);
+    else
+        g_simple_async_result_set_op_res_gpointer (operation_result,
+                                                   bands_array,
+                                                   (GDestroyNotify)g_array_unref);
+
+    g_simple_async_result_complete (operation_result);
+    g_object_unref (operation_result);
+}
+
+static void
+load_current_bands (MMIfaceModem *self,
+                    GAsyncReadyCallback callback,
+                    gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        load_current_bands);
+
+    if (mm_iface_modem_is_3g (self))
+        mm_base_modem_at_command (MM_BASE_MODEM (self),
+                                  "AT+WUBS?",
+                                  3,
+                                  FALSE,
+                                  (GAsyncReadyCallback)get_3g_band_ready,
+                                  result);
+    else
+        mm_base_modem_at_command (MM_BASE_MODEM (self),
+                                  "AT+WMBS?",
+                                  3,
+                                  FALSE,
+                                  (GAsyncReadyCallback)get_2g_band_ready,
+                                  result);
+}
+
+/*****************************************************************************/
+/* Set bands (Modem interface) */
+
+static gboolean
+set_bands_finish (MMIfaceModem *self,
+                  GAsyncResult *res,
+                  GError **error)
+{
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+wmbs_set_ready (MMBaseModem *self,
+                GAsyncResult *res,
+                GSimpleAsyncResult *operation_result)
+{
+    GError *error = NULL;
+
+    if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
+        /* Let the error be critical */
+        g_simple_async_result_take_error (operation_result, error);
+    else
+        g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
+
+    g_simple_async_result_complete (operation_result);
+    g_object_unref (operation_result);
+}
+
+static void
+set_bands_3g (MMIfaceModem *self,
+              GArray *bands_array,
+              GSimpleAsyncResult *result)
+{
+    GArray *bands_array_final;
+    guint wavecom_band = 0;
+    guint i;
+    gchar *bands_string;
+    gchar *cmd;
+
+    /* The special case of ANY should be treated separately. */
+    if (bands_array->len == 1 &&
+        g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
+        /* We build an array with all bands to set; so that we use the same
+         * logic to build the cinterion_band, and so that we can log the list of
+         * bands being set properly */
+        bands_array_final = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), G_N_ELEMENTS (bands_3g));
+        for (i = 0; i < G_N_ELEMENTS (bands_3g); i++)
+            g_array_append_val (bands_array_final, bands_3g[i].mm_band);
+    } else
+        bands_array_final = g_array_ref (bands_array);
+
+    for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) {
+        guint j;
+
+        for (j = 0; j < bands_array_final->len; j++) {
+            if (g_array_index (bands_array_final, MMModemBand, j) == bands_3g[i].mm_band) {
+                wavecom_band |= bands_3g[i].wavecom_band_flag;
+                break;
+            }
+        }
+    }
+
+    bands_string = mm_common_build_bands_string ((MMModemBand *)bands_array_final->data,
+                                                 bands_array_final->len);
+    g_array_unref (bands_array_final);
+
+    if (wavecom_band == 0) {
+        g_simple_async_result_set_error (result,
+                                         MM_CORE_ERROR,
+                                         MM_CORE_ERROR_UNSUPPORTED,
+                                         "The given band combination is not supported: '%s'",
+                                         bands_string);
+        g_simple_async_result_complete_in_idle (result);
+        g_object_unref (result);
+        g_free (bands_string);
+        return;
+    }
+
+    mm_dbg ("Setting new bands to use: '%s'", bands_string);
+    cmd = g_strdup_printf ("+WMBS=\"%u\",1", wavecom_band);
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              cmd,
+                              3,
+                              FALSE,
+                              (GAsyncReadyCallback)wmbs_set_ready,
+                              result);
+    g_free (cmd);
+    g_free (bands_string);
+}
+
+static void
+set_bands_2g (MMIfaceModem *self,
+              GArray *bands_array,
+              GSimpleAsyncResult *result)
+{
+    GArray *bands_array_final;
+    gchar wavecom_band = '\0';
+    guint i;
+    gchar *bands_string;
+    gchar *cmd;
+
+    /* If the iface properly checked the given list against the supported bands,
+     * it's not possible to get an array longer than 4 here. */
+    g_assert (bands_array->len <= 4);
+
+    /* The special case of ANY should be treated separately. */
+    if (bands_array->len == 1 &&
+        g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
+        const WavecomBand2G *all;
+
+        /* All bands is the last element in our 2G bands array */
+        all = &bands_2g[G_N_ELEMENTS (bands_2g) - 1];
+
+        /* We build an array with all bands to set; so that we use the same
+         * logic to build the cinterion_band, and so that we can log the list of
+         * bands being set properly */
+        bands_array_final = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 4);
+        g_array_append_vals (bands_array_final, all->mm_bands, all->n_mm_bands);
+    } else
+        bands_array_final = g_array_ref (bands_array);
+
+    for (i = 0; wavecom_band == '\0' && i < G_N_ELEMENTS (bands_2g); i++) {
+        GArray *supported_combination;
+
+        supported_combination = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), bands_2g[i].n_mm_bands);
+        g_array_append_vals (supported_combination, bands_2g[i].mm_bands, bands_2g[i].n_mm_bands);
+
+        /* Check if the given array is exactly one of the supported combinations */
+        if (mm_common_bands_garray_cmp (bands_array_final, supported_combination))
+            wavecom_band = bands_2g[i].wavecom_band;
+
+        g_array_unref (supported_combination);
+    }
+
+    bands_string = mm_common_build_bands_string ((MMModemBand *)bands_array_final->data,
+                                                 bands_array_final->len);
+    g_array_unref (bands_array_final);
+
+    if (wavecom_band == '\0') {
+        g_simple_async_result_set_error (result,
+                                         MM_CORE_ERROR,
+                                         MM_CORE_ERROR_UNSUPPORTED,
+                                         "The given band combination is not supported: '%s'",
+                                         bands_string);
+        g_simple_async_result_complete_in_idle (result);
+        g_object_unref (result);
+        g_free (bands_string);
+        return;
+    }
+
+    mm_dbg ("Setting new bands to use: '%s'", bands_string);
+    cmd = g_strdup_printf ("+WMBS=%c,1", wavecom_band);
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              cmd,
+                              3,
+                              FALSE,
+                              (GAsyncReadyCallback)wmbs_set_ready,
+                              result);
+
+    g_free (cmd);
+    g_free (bands_string);
+}
+
+static void
+set_bands (MMIfaceModem *self,
+           GArray *bands_array,
+           GAsyncReadyCallback callback,
+           gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+
+    /* The bands that we get here are previously validated by the interface, and
+     * that means that ALL the bands given here were also given in the list of
+     * supported bands. BUT BUT, that doesn't mean that the exact list of bands
+     * will end up being valid, as not all combinations are possible. E.g,
+     * Wavecom modems supporting only 2G have specific combinations allowed.
+     */
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        set_bands);
+
+    if (mm_iface_modem_is_3g (self))
+        set_bands_3g (self, bands_array, result);
+    else
+        set_bands_2g (self, bands_array, result);
+}
+
+/*****************************************************************************/
+/* Load access technologies (Modem interface) */
+
+static gboolean
+load_access_technologies_finish (MMIfaceModem *self,
+                                 GAsyncResult *res,
+                                 MMModemAccessTechnology *access_technologies,
+                                 guint *mask,
+                                 GError **error)
+{
+    MMModemAccessTechnology act;
+    const gchar *p;
+    const gchar *response;
+
+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+    if (!response)
+        return FALSE;
+
+    act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+    p = mm_strip_tag (response, "+WGPRSIND:");
+    if (p) {
+        switch (*p) {
+        case '1':
+            /* GPRS only */
+            act = MM_MODEM_ACCESS_TECHNOLOGY_GPRS;
+            break;
+        case '2':
+            /* EGPRS/EDGE supported */
+            act = MM_MODEM_ACCESS_TECHNOLOGY_EDGE;
+            break;
+        case '3':
+            /* 3G R99 supported */
+            act = MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
+            break;
+        case '4':
+            /* HSDPA supported */
+            act = MM_MODEM_ACCESS_TECHNOLOGY_HSDPA;
+            break;
+        case '5':
+            /* HSUPA supported */
+            act = MM_MODEM_ACCESS_TECHNOLOGY_HSUPA;
+            break;
+        default:
+            break;
+        }
+    }
+
+    if (act == MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN) {
+        g_set_error (error,
+                     MM_CORE_ERROR,
+                     MM_CORE_ERROR_FAILED,
+                     "Couldn't parse access technologies result: '%s'",
+                     response);
+        return FALSE;
+    }
+
+    /* We are reporting ALL 3GPP access technologies here */
+    *access_technologies = act;
+    *mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK;
+    return TRUE;
+}
+
+static void
+load_access_technologies (MMIfaceModem *self,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data)
+{
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "+WGPRS=9,2",
+                              3,
+                              FALSE,
+                              callback,
+                              user_data);
+}
+
+/*****************************************************************************/
+/* Flow control (Modem interface) */
+
+static gboolean
+setup_flow_control_finish (MMIfaceModem *self,
+                           GAsyncResult *res,
+                           GError **error)
+{
+    return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+}
+
+static void
+setup_flow_control (MMIfaceModem *self,
+                    GAsyncReadyCallback callback,
+                    gpointer user_data)
+{
+    /* Wavecom doesn't have XOFF/XON flow control, so we enable RTS/CTS */
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "+IFC=2,2",
+                              3,
+                              FALSE,
+                              callback,
+                              user_data);
+}
+
+/*****************************************************************************/
+/* Modem power up (Modem interface) */
+
+static gboolean
+modem_power_up_finish (MMIfaceModem *self,
+                       GAsyncResult *res,
+                       GError **error)
+{
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+full_functionality_status_ready (MMBaseModem *self,
+                                 GAsyncResult *res,
+                                 GSimpleAsyncResult *simple)
+{
+    GError *error = NULL;
+
+    if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
+        g_simple_async_result_take_error (simple, error);
+    else
+        g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+    g_simple_async_result_complete (simple);
+    g_object_unref (simple);
+}
+
+static void
+get_current_functionality_status_ready (MMBaseModem *self,
+                                        GAsyncResult *res,
+                                        GSimpleAsyncResult *simple)
+{
+    const gchar *response;
+    GError *error = NULL;
+
+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+    if (!response) {
+        mm_warn ("Failed checking if power-up command is needed: '%s'. "
+                 "Will assume it isn't.",
+                 error->message);
+        g_error_free (error);
+        /* On error, just assume we don't need the power-up command */
+        g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+        g_simple_async_result_complete (simple);
+        g_object_unref (simple);
+        return;
+    }
+
+    response = mm_strip_tag (response, "+CFUN:");
+    if (response && *response == '1') {
+        /* If reported functionality status is '1', then we do not need to
+         * issue the power-up command. Otherwise, do it. */
+        mm_dbg ("Already in full functionality status, skipping power-up command");
+        g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+        g_simple_async_result_complete (simple);
+        g_object_unref (simple);
+        return;
+    }
+
+    mm_warn ("Not in full functionality status, power-up command is needed. "
+             "Note that it may reboot the modem.");
+
+    /* Try to go to full functionality mode without rebooting the system.
+     * Works well if we previously switched off the power with CFUN=4
+     */
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "+CFUN=1,0",
+                              3,
+                              FALSE,
+                              (GAsyncReadyCallback)full_functionality_status_ready,
+                              simple);
+}
+
+static void
+modem_power_up (MMIfaceModem *self,
+                GAsyncReadyCallback callback,
+                gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        modem_power_up);
+
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "+CFUN?",
+                              3,
+                              FALSE,
+                              (GAsyncReadyCallback)get_current_functionality_status_ready,
+                              result);
+}
+
+/*****************************************************************************/
+/* Modem power down (Modem interface) */
+
+static gboolean
+modem_power_down_finish (MMIfaceModem *self,
+                         GAsyncResult *res,
+                         GError **error)
+{
+    return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+}
+
+static void
+modem_power_down (MMIfaceModem *self,
+                  GAsyncReadyCallback callback,
+                  gpointer user_data)
+{
+    /* Use AT+CFUN=4 for power down. It will stop the RF (IMSI detach), and
+     * keeps access to the SIM */
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "+CFUN=4",
+                              3,
+                              FALSE,
+                              callback,
+                              user_data);
+}
+
+static void
+setup_ports (MMBroadbandModem *self)
+{
+    gpointer parser;
+    MMAtSerialPort *primary;
+    GRegex *regex;
+
+    /* Call parent's setup ports first always */
+    MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_wavecom_parent_class)->setup_ports (self);
+
+    /* Set 9600 baudrate by default in the AT port */
+    mm_dbg ("Baudrate will be set to 9600 bps...");
+    primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+    if (!primary)
+        return;
+
+    /* AT+CPIN? replies will never have an OK appended */
+    parser = mm_serial_parser_v1_new ();
+    regex = g_regex_new ("\\r\\n\\+CPIN: .*\\r\\n",
+                         G_REGEX_RAW | G_REGEX_OPTIMIZE,
+                         0, NULL);
+    mm_serial_parser_v1_set_custom_regex (parser, regex, NULL);
+    g_regex_unref (regex);
+
+    mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (primary),
+                                           mm_serial_parser_v1_parse,
+                                           parser,
+                                           mm_serial_parser_v1_destroy);
+}
+
+/*****************************************************************************/
+
+MMBroadbandModemWavecom *
+mm_broadband_modem_wavecom_new (const gchar *device,
+                                const gchar *driver,
+                                const gchar *plugin,
+                                guint16 vendor_id,
+                                guint16 product_id)
+{
+    return g_object_new (MM_TYPE_BROADBAND_MODEM_WAVECOM,
+                         MM_BASE_MODEM_DEVICE, device,
+                         MM_BASE_MODEM_DRIVER, driver,
+                         MM_BASE_MODEM_PLUGIN, plugin,
+                         MM_BASE_MODEM_VENDOR_ID, vendor_id,
+                         MM_BASE_MODEM_PRODUCT_ID, product_id,
+                         NULL);
+}
+
+static void
+mm_broadband_modem_wavecom_init (MMBroadbandModemWavecom *self)
+{
+}
+
+static void
+iface_modem_init (MMIfaceModem *iface)
+{
+    iface->load_supported_modes = load_supported_modes;
+    iface->load_supported_modes_finish = load_supported_modes_finish;
+    iface->load_allowed_modes = load_allowed_modes;
+    iface->load_allowed_modes_finish = load_allowed_modes_finish;
+    iface->set_allowed_modes = set_allowed_modes;
+    iface->set_allowed_modes_finish = set_allowed_modes_finish;
+    iface->load_supported_bands = load_supported_bands;
+    iface->load_supported_bands_finish = load_supported_bands_finish;
+    iface->load_current_bands = load_current_bands;
+    iface->load_current_bands_finish = load_current_bands_finish;
+    iface->set_bands = set_bands;
+    iface->set_bands_finish = set_bands_finish;
+    iface->load_access_technologies = load_access_technologies;
+    iface->load_access_technologies_finish = load_access_technologies_finish;
+    iface->setup_flow_control = setup_flow_control;
+    iface->setup_flow_control_finish = setup_flow_control_finish;
+    iface->modem_power_up = modem_power_up;
+    iface->modem_power_up_finish = modem_power_up_finish;
+    iface->modem_power_down = modem_power_down;
+    iface->modem_power_down_finish = modem_power_down_finish;
+}
+
+static void
+mm_broadband_modem_wavecom_class_init (MMBroadbandModemWavecomClass *klass)
+{
+    MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
+
+    broadband_modem_class->setup_ports = setup_ports;
+}
diff --git a/plugins/wavecom/mm-broadband-modem-wavecom.h b/plugins/wavecom/mm-broadband-modem-wavecom.h
new file mode 100644
index 0000000..3bec594
--- /dev/null
+++ b/plugins/wavecom/mm-broadband-modem-wavecom.h
@@ -0,0 +1,50 @@
+/* -*- 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) 2011 Ammonit Measurement GmbH
+ * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
+ *
+ * Author: Aleksander Morgado <aleksander@lanedo.com>
+ */
+
+#ifndef MM_BROADBAND_MODEM_WAVECOM_H
+#define MM_BROADBAND_MODEM_WAVECOM_H
+
+#include "mm-broadband-modem.h"
+
+#define MM_TYPE_BROADBAND_MODEM_WAVECOM            (mm_broadband_modem_wavecom_get_type ())
+#define MM_BROADBAND_MODEM_WAVECOM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_WAVECOM, MMBroadbandModemWavecom))
+#define MM_BROADBAND_MODEM_WAVECOM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  MM_TYPE_BROADBAND_MODEM_WAVECOM, MMBroadbandModemWavecomClass))
+#define MM_IS_BROADBAND_MODEM_WAVECOM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_WAVECOM))
+#define MM_IS_BROADBAND_MODEM_WAVECOM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_BROADBAND_MODEM_WAVECOM))
+#define MM_BROADBAND_MODEM_WAVECOM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_BROADBAND_MODEM_WAVECOM, MMBroadbandModemWavecomClass))
+
+typedef struct _MMBroadbandModemWavecom MMBroadbandModemWavecom;
+typedef struct _MMBroadbandModemWavecomClass MMBroadbandModemWavecomClass;
+
+struct _MMBroadbandModemWavecom {
+    MMBroadbandModem parent;
+};
+
+struct _MMBroadbandModemWavecomClass{
+    MMBroadbandModemClass parent;
+};
+
+GType mm_broadband_modem_wavecom_get_type (void);
+
+MMBroadbandModemWavecom *mm_broadband_modem_wavecom_new (const gchar *device,
+                                                         const gchar *driver,
+                                                         const gchar *plugin,
+                                                         guint16 vendor_id,
+                                                         guint16 product_id);
+
+#endif /* MM_BROADBAND_MODEM_WAVECOM_H */
diff --git a/plugins/wavecom/mm-plugin-wavecom.c b/plugins/wavecom/mm-plugin-wavecom.c
new file mode 100644
index 0000000..d83f96e
--- /dev/null
+++ b/plugins/wavecom/mm-plugin-wavecom.c
@@ -0,0 +1,120 @@
+/* -*- 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2011 Ammonit Measurement GmbH
+ * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
+ *
+ * Author: Aleksander Morgado <aleksander@lanedo.com>
+ */
+
+#include <string.h>
+#include <gmodule.h>
+
+#include <libmm-common.h>
+
+#include "mm-plugin-wavecom.h"
+#include "mm-broadband-modem-wavecom.h"
+
+G_DEFINE_TYPE (MMPluginWavecom, mm_plugin_wavecom, MM_TYPE_PLUGIN_BASE)
+
+int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
+int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+
+/*****************************************************************************/
+
+static MMBaseModem *
+grab_port (MMPluginBase *base,
+           MMBaseModem *existing,
+           MMPortProbe *probe,
+           GError **error)
+{
+    MMBaseModem *modem = NULL;
+    const gchar *name, *subsys;
+    guint16 vendor = 0, product = 0;
+
+    /* The Wavecom plugin cannot do anything with non-AT ports */
+    if (!mm_port_probe_is_at (probe)) {
+        g_set_error_literal (error,
+                             MM_CORE_ERROR,
+                             MM_CORE_ERROR_UNSUPPORTED,
+                             "Ignoring non-AT port");
+        return NULL;
+    }
+
+    subsys = mm_port_probe_get_port_subsys (probe);
+    name = mm_port_probe_get_port_name (probe);
+
+    if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) {
+        g_set_error_literal (error,
+                             MM_CORE_ERROR,
+                             MM_CORE_ERROR_FAILED,
+                             "Could not get modem product ID");
+        return NULL;
+    }
+
+    /* If this is the first port being grabbed, create a new modem object */
+    if (!existing)
+        modem = MM_BASE_MODEM (mm_broadband_modem_wavecom_new (mm_port_probe_get_port_physdev (probe),
+                                                               mm_port_probe_get_port_driver (probe),
+                                                               mm_plugin_get_name (MM_PLUGIN (base)),
+                                                               vendor,
+                                                               product));
+
+    if (!mm_base_modem_grab_port (existing ? existing : modem,
+                                  subsys,
+                                  name,
+                                  MM_PORT_TYPE_AT, /* we only allow AT ports here */
+                                  MM_AT_PORT_FLAG_NONE,
+                                  error)) {
+        if (modem)
+            g_object_unref (modem);
+        return NULL;
+    }
+
+    return existing ? existing : modem;
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+    static const gchar *subsystems[] = { "tty", NULL };
+    static const guint16 vendor_ids[] = { 0x114f, 0 };
+
+    return MM_PLUGIN (
+        g_object_new (MM_TYPE_PLUGIN_WAVECOM,
+                      MM_PLUGIN_BASE_NAME, "Wavecom",
+                      MM_PLUGIN_BASE_ALLOWED_SUBSYSTEMS, subsystems,
+                      MM_PLUGIN_BASE_ALLOWED_VENDOR_IDS, vendor_ids,
+                      MM_PLUGIN_BASE_ALLOWED_AT, TRUE,
+                      NULL));
+}
+
+static void
+mm_plugin_wavecom_init (MMPluginWavecom *self)
+{
+}
+
+static void
+mm_plugin_wavecom_class_init (MMPluginWavecomClass *klass)
+{
+    MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass);
+
+    pb_class->grab_port = grab_port;
+}
diff --git a/plugins/mm-plugin-wavecom.h b/plugins/wavecom/mm-plugin-wavecom.h
similarity index 96%
rename from plugins/mm-plugin-wavecom.h
rename to plugins/wavecom/mm-plugin-wavecom.h
index 5563a37..9b355d5 100644
--- a/plugins/mm-plugin-wavecom.h
+++ b/plugins/wavecom/mm-plugin-wavecom.h
@@ -17,6 +17,8 @@
  * Boston, MA 02111-1307, USA.
  *
  * Copyright (C) 2011 Ammonit Measurement GmbH
+ * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
+ *
  * Author: Aleksander Morgado <aleksander@lanedo.com>
  */
 
@@ -24,7 +26,6 @@
 #define MM_PLUGIN_WAVECOM_H
 
 #include "mm-plugin-base.h"
-#include "mm-generic-gsm.h"
 
 #define MM_TYPE_PLUGIN_WAVECOM            (mm_plugin_wavecom_get_type ())
 #define MM_PLUGIN_WAVECOM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_WAVECOM, MMPluginWavecom))
diff --git a/src/mm-bearer-list.c b/src/mm-bearer-list.c
index 551279f..3c672f8 100644
--- a/src/mm-bearer-list.c
+++ b/src/mm-bearer-list.c
@@ -170,7 +170,7 @@
     GList *l;
 
     for (l = self->priv->bearers; l; l = g_list_next (l)) {
-        if (mm_bearer_cmp_properties (MM_BEARER (l->data), properties))
+        if (mm_bearer_properties_cmp (mm_bearer_peek_config (MM_BEARER (l->data)), properties))
             return g_object_ref (l->data);
     }
 
diff --git a/src/mm-bearer.c b/src/mm-bearer.c
index c0b8c33..3cce948 100644
--- a/src/mm-bearer.c
+++ b/src/mm-bearer.c
@@ -45,6 +45,7 @@
     PROP_CONNECTION,
     PROP_MODEM,
     PROP_STATUS,
+    PROP_CONFIG,
     PROP_LAST
 };
 
@@ -59,6 +60,8 @@
     gchar *path;
     /* Status of this bearer */
     MMBearerStatus status;
+    /* Configuration of the bearer */
+    MMBearerProperties *config;
 
     /* Cancellable for connect() */
     GCancellable *connect_cancellable;
@@ -68,28 +71,12 @@
 
 /*****************************************************************************/
 
-static void
-bearer_expose_properties (MMBearer *self)
-{
-    MMBearerProperties *properties;
-    GVariant *dictionary;
-
-    properties = MM_BEARER_GET_CLASS (self)->expose_properties (self);
-    dictionary = mm_bearer_properties_get_dictionary (properties);
-    mm_gdbus_bearer_set_properties (MM_GDBUS_BEARER (self), dictionary);
-    g_variant_unref (dictionary);
-    g_object_unref (properties);
-}
-
 void
 mm_bearer_export (MMBearer *self)
 {
     static guint id = 0;
     gchar *path;
 
-    /* Expose properties before exporting */
-    bearer_expose_properties (self);
-
     path = g_strdup_printf (MM_DBUS_BEARER_PREFIX "/%d", id++);
     g_object_set (self,
                   MM_BEARER_PATH, path,
@@ -625,6 +612,20 @@
     return self->priv->path;
 }
 
+MMBearerProperties *
+mm_bearer_peek_config (MMBearer *self)
+{
+    return self->priv->config;
+}
+
+MMBearerProperties *
+mm_bearer_get_config (MMBearer *self)
+{
+    return (self->priv->config ?
+            g_object_ref (self->priv->config) :
+            NULL);
+}
+
 /*****************************************************************************/
 
 static void
@@ -685,17 +686,6 @@
     return MM_BEARER_GET_CLASS (self)->report_disconnection (self);
 }
 
-/*****************************************************************************/
-
-gboolean
-mm_bearer_cmp_properties (MMBearer *self,
-                          MMBearerProperties *properties)
-{
-    return MM_BEARER_GET_CLASS (self)->cmp_properties (self, properties);
-}
-
-/*****************************************************************************/
-
 static void
 set_property (GObject *object,
               guint prop_id,
@@ -738,6 +728,18 @@
         /* We don't allow g_object_set()-ing the status property */
         g_assert_not_reached ();
         break;
+    case PROP_CONFIG: {
+        GVariant *dictionary;
+
+        g_clear_object (&self->priv->config);
+        self->priv->config = g_value_dup_object (value);
+        /* Also expose the properties */
+        dictionary = mm_bearer_properties_get_dictionary (self->priv->config);
+        mm_gdbus_bearer_set_properties (MM_GDBUS_BEARER (self), dictionary);
+        if (dictionary)
+            g_variant_unref (dictionary);
+        break;
+    }
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -765,6 +767,9 @@
     case PROP_STATUS:
         g_value_set_enum (value, self->priv->status);
         break;
+    case PROP_CONFIG:
+        g_value_set_object (value, self->priv->config);
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -812,8 +817,8 @@
         g_clear_object (&self->priv->connection);
     }
 
-    if (self->priv->modem)
-        g_clear_object (&self->priv->modem);
+    g_clear_object (&self->priv->modem);
+    g_clear_object (&self->priv->config);
 
     G_OBJECT_CLASS (mm_bearer_parent_class)->dispose (object);
 }
@@ -865,4 +870,12 @@
                            MM_BEARER_STATUS_DISCONNECTED,
                            G_PARAM_READABLE);
     g_object_class_install_property (object_class, PROP_STATUS, properties[PROP_STATUS]);
+
+    properties[PROP_CONFIG] =
+        g_param_spec_object (MM_BEARER_CONFIG,
+                             "Bearer configuration",
+                             "List of user provided properties",
+                             MM_TYPE_BEARER_PROPERTIES,
+                             G_PARAM_READWRITE);
+    g_object_class_install_property (object_class, PROP_CONFIG, properties[PROP_CONFIG]);
 }
diff --git a/src/mm-bearer.h b/src/mm-bearer.h
index 6f35e2d..1791b35 100644
--- a/src/mm-bearer.h
+++ b/src/mm-bearer.h
@@ -40,6 +40,7 @@
 #define MM_BEARER_CONNECTION "bearer-connection"
 #define MM_BEARER_MODEM      "bearer-modem"
 #define MM_BEARER_STATUS     "bearer-status"
+#define MM_BEARER_CONFIG     "bearer-config"
 
 typedef enum { /*< underscore_name=mm_bearer_status >*/
     MM_BEARER_STATUS_DISCONNECTED,
@@ -78,25 +79,17 @@
 
     /* Report disconnection */
     void (* report_disconnection) (MMBearer *bearer);
-
-    /* Check if the bearer has the exact same properties */
-    gboolean (* cmp_properties) (MMBearer *self,
-                                 MMBearerProperties *properties);
-
-    /* Builder of the properties to be exposed in DBus. Bearers should expose only
-     * the input properties they actually ended up using */
-    MMBearerProperties * (* expose_properties) (MMBearer *self);
 };
 
 GType mm_bearer_get_type (void);
 
 void         mm_bearer_export   (MMBearer *self);
-const gchar *mm_bearer_get_path (MMBearer *bearer);
 
-void mm_bearer_expose_properties (MMBearer *bearer,
-                                  MMBearerProperties *properties);
+const gchar        *mm_bearer_get_path    (MMBearer *bearer);
+MMBearerStatus      mm_bearer_get_status  (MMBearer *bearer);
+MMBearerProperties *mm_bearer_peek_config (MMBearer *self);
+MMBearerProperties *mm_bearer_get_config  (MMBearer *self);
 
-MMBearerStatus mm_bearer_get_status (MMBearer *bearer);
 
 void     mm_bearer_connect        (MMBearer *self,
                                    GAsyncReadyCallback callback,
@@ -116,7 +109,4 @@
 
 void mm_bearer_report_disconnection (MMBearer *self);
 
-gboolean mm_bearer_cmp_properties (MMBearer *self,
-                                   MMBearerProperties *properties);
-
 #endif /* MM_BEARER_H */
diff --git a/src/mm-broadband-bearer.c b/src/mm-broadband-bearer.c
index a90360b..cf55c7b 100644
--- a/src/mm-broadband-bearer.c
+++ b/src/mm-broadband-bearer.c
@@ -53,24 +53,8 @@
     CONNECTION_TYPE_CDMA,
 } ConnectionType;
 
-enum {
-    PROP_0,
-    PROP_3GPP_APN,
-    PROP_CDMA_NUMBER,
-    PROP_CDMA_RM_PROTOCOL,
-    PROP_IP_TYPE,
-    PROP_ALLOW_ROAMING,
-    PROP_LAST
-};
-
-static GParamSpec *properties[PROP_LAST];
-
 struct _MMBroadbandBearerPrivate {
     /*-- Common stuff --*/
-    /* IP type  */
-    gchar *ip_type;
-    /* Flag to allow/forbid connections while roaming */
-    gboolean allow_roaming;
     /* Data port used when modem is connected */
     MMPort *port;
     /* Current connection type */
@@ -81,8 +65,6 @@
     ConnectionForbiddenReason reason_3gpp;
     /* Handler ID for the registration state change signals */
     guint id_3gpp_registration_change;
-    /* APN of the PDP context */
-    gchar *apn;
     /* CID of the PDP context */
     guint cid;
 
@@ -92,10 +74,6 @@
     /* Handler IDs for the registration state change signals */
     guint id_cdma1x_registration_change;
     guint id_evdo_registration_change;
-    /* (Optional) Number to dial */
-    gchar *number;
-    /* Protocol of the Rm interface */
-    MMModemCdmaRmProtocol rm_protocol;
 };
 
 /*****************************************************************************/
@@ -107,38 +85,6 @@
 };
 
 /*****************************************************************************/
-
-const gchar *
-mm_broadband_bearer_get_3gpp_apn (MMBroadbandBearer *self)
-{
-    return self->priv->apn;
-}
-
-MMModemCdmaRmProtocol
-mm_broadband_bearer_get_cdma_rm_protocol (MMBroadbandBearer *self)
-{
-    return self->priv->rm_protocol;
-}
-
-const gchar *
-mm_broadband_bearer_get_ip_type (MMBroadbandBearer *self)
-{
-    return self->priv->ip_type;
-}
-
-gboolean
-mm_broadband_bearer_get_allow_roaming (MMBroadbandBearer *self)
-{
-    return self->priv->allow_roaming;
-}
-
-guint
-mm_broadband_bearer_get_3gpp_cid (MMBroadbandBearer *self)
-{
-    return self->priv->cid;
-}
-
-/*****************************************************************************/
 /* Detailed connect result, used in both CDMA and 3GPP sequences */
 typedef struct {
     MMBearerIpConfig *ipv4_config;
@@ -328,12 +274,15 @@
 cdma_connect_context_dial (DetailedConnectContext *ctx)
 {
     gchar *command;
+    const gchar *number;
+
+    number = mm_bearer_properties_get_number (mm_bearer_peek_config (MM_BEARER (ctx->self)));
 
     /* If a number was given when creating the bearer, use that one.
      * Otherwise, use the default one, #777
      */
-    if (ctx->self->priv->number)
-        command = g_strconcat ("DT", ctx->self->priv->number, NULL);
+    if (number)
+        command = g_strconcat ("DT", number, NULL);
     else
         command = g_strdup ("DT#777");
 
@@ -405,14 +354,14 @@
         return;
     }
 
-    if (current_rm != ctx->self->priv->rm_protocol) {
+    if (current_rm != mm_bearer_properties_get_rm_protocol (mm_bearer_peek_config (MM_BEARER (self)))) {
         guint new_index;
         gchar *command;
 
         mm_dbg ("Setting requested RM protocol...");
 
         new_index = (mm_cdma_get_index_from_rm_protocol (
-                         ctx->self->priv->rm_protocol,
+                         mm_bearer_properties_get_rm_protocol (mm_bearer_peek_config (MM_BEARER (self))),
                          &error));
         if (error) {
             mm_warn ("Cannot set RM protocol: '%s'",
@@ -462,7 +411,9 @@
                                         callback,
                                         user_data);
 
-    if (self->priv->rm_protocol != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) {
+    if (mm_bearer_properties_get_rm_protocol (
+            mm_bearer_peek_config (MM_BEARER (self))) !=
+        MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) {
         /* Need to query current RM protocol */
         mm_dbg ("Querying current RM protocol set...");
         mm_base_modem_at_command_full (ctx->modem,
@@ -754,7 +705,7 @@
 }
 
 static void
-initialize_pdp_context_ready (MMBaseModem *self,
+initialize_pdp_context_ready (MMBaseModem *modem,
                               GAsyncResult *res,
                               DetailedConnectContext *ctx)
 {
@@ -764,7 +715,7 @@
     if (detailed_connect_context_complete_and_free_if_cancelled (ctx))
         return;
 
-    mm_base_modem_at_command_full_finish (self, res, &error);
+    mm_base_modem_at_command_full_finish (modem, res, &error);
     if (error) {
         mm_warn ("Couldn't initialize PDP context with our APN: '%s'",
                  error->message);
@@ -783,7 +734,7 @@
 }
 
 static void
-find_cid_ready (MMBaseModem *self,
+find_cid_ready (MMBaseModem *modem,
                 GAsyncResult *res,
                 DetailedConnectContext *ctx)
 {
@@ -791,7 +742,7 @@
     gchar *command;
     GError *error = NULL;
 
-    result = mm_base_modem_at_sequence_full_finish (self, res, NULL, &error);
+    result = mm_base_modem_at_sequence_full_finish (modem, res, NULL, &error);
     if (!result) {
         mm_warn ("Couldn't find best CID to use: '%s'", error->message);
         g_simple_async_result_take_error (ctx->result, error);
@@ -807,9 +758,10 @@
 
     /* Initialize PDP context with our APN */
     ctx->cid = g_variant_get_uint32 (result);
-    command = g_strdup_printf ("+CGDCONT=%u,\"IP\",\"%s\"",
+    command = g_strdup_printf ("+CGDCONT=%u,\"%s\",\"%s\"",
                                ctx->cid,
-                               ctx->self->priv->apn);
+                               mm_bearer_properties_get_ip_type (mm_bearer_peek_config (MM_BEARER (ctx->self))),
+                               mm_bearer_properties_get_apn (mm_bearer_peek_config (MM_BEARER (ctx->self))));
     mm_base_modem_at_command_full (ctx->modem,
                                    ctx->primary,
                                    command,
@@ -822,7 +774,7 @@
 }
 
 static gboolean
-parse_cid_range (MMBaseModem *self,
+parse_cid_range (MMBaseModem *modem,
                  DetailedConnectContext *ctx,
                  const gchar *command,
                  const gchar *response,
@@ -867,8 +819,7 @@
 
             pdp_type = g_match_info_fetch (match_info, 3);
 
-            /* TODO: What about PDP contexts of type "IPV6"? */
-            if (g_str_equal (pdp_type, "IP")) {
+            if (g_str_equal (pdp_type, mm_bearer_properties_get_ip_type (mm_bearer_peek_config (MM_BEARER (ctx->self))))) {
                 gchar *max_cid_range_str;
                 guint max_cid_range;
 
@@ -907,7 +858,7 @@
 }
 
 static gboolean
-parse_pdp_list (MMBaseModem *self,
+parse_pdp_list (MMBaseModem *modem,
                 DetailedConnectContext *ctx,
                 const gchar *command,
                 const gchar *response,
@@ -954,20 +905,25 @@
                 pdp->cid,
                 pdp->pdp_type ? pdp->pdp_type : "",
                 pdp->apn ? pdp->apn : "");
-        if (g_str_equal (pdp->pdp_type, "IP")) {
+        if (g_str_equal (pdp->pdp_type, mm_bearer_properties_get_ip_type (mm_bearer_peek_config (MM_BEARER (ctx->self))))) {
             /* PDP with no APN set? we may use that one if not exact match found */
             if (!pdp->apn || !pdp->apn[0]) {
                 mm_dbg ("Found PDP context with CID %u and no APN",
                         pdp->cid);
                 cid = pdp->cid;
-            } else if (ctx->self->priv->apn &&
-                       g_str_equal (pdp->apn, ctx->self->priv->apn)) {
-                /* Found a PDP context with the same CID, we'll use it. */
-                mm_dbg ("Found PDP context with CID %u for APN '%s'",
-                        pdp->cid, pdp->apn);
-                cid = pdp->cid;
-                /* In this case, stop searching */
-                break;
+            } else {
+                const gchar *apn;
+
+                apn = mm_bearer_properties_get_apn (mm_bearer_peek_config (MM_BEARER (ctx->self)));
+                if (apn &&
+                    g_str_equal (pdp->apn, apn)) {
+                    /* Found a PDP context with the same CID and PDP type, we'll use it. */
+                    mm_dbg ("Found PDP context with CID %u and PDP type %s for APN '%s'",
+                            pdp->cid, pdp->pdp_type, pdp->apn);
+                    cid = pdp->cid;
+                    /* In this case, stop searching */
+                    break;
+                }
             }
         }
 
@@ -1767,41 +1723,6 @@
 
 /*****************************************************************************/
 
-static gboolean
-cmp_properties (MMBearer *self,
-                MMBearerProperties *properties)
-{
-    MMBroadbandBearer *broadband = MM_BROADBAND_BEARER (self);
-
-    return ((!g_strcmp0 (broadband->priv->apn,
-                         mm_bearer_properties_get_apn (properties))) &&
-            (!g_strcmp0 (broadband->priv->ip_type,
-                         mm_bearer_properties_get_ip_type (properties))) &&
-            (broadband->priv->allow_roaming ==
-             mm_bearer_properties_get_allow_roaming (properties)) &&
-            (!g_strcmp0 (broadband->priv->number,
-                         mm_bearer_properties_get_number (properties))) &&
-            (broadband->priv->rm_protocol ==
-             mm_bearer_properties_get_rm_protocol (properties)));
-}
-
-static MMBearerProperties *
-expose_properties (MMBearer *self)
-{
-    MMBroadbandBearer *broadband = MM_BROADBAND_BEARER (self);
-    MMBearerProperties *properties;
-
-    properties = mm_bearer_properties_new ();
-    mm_bearer_properties_set_apn (properties, broadband->priv->apn);
-    mm_bearer_properties_set_number (properties, broadband->priv->number);
-    mm_bearer_properties_set_rm_protocol (properties, broadband->priv->rm_protocol);
-    mm_bearer_properties_set_ip_type (properties, broadband->priv->ip_type);
-    mm_bearer_properties_set_allow_roaming (properties, broadband->priv->allow_roaming);
-    return properties;
-}
-
-/*****************************************************************************/
-
 typedef struct _InitAsyncContext InitAsyncContext;
 static void interface_initialization_step (InitAsyncContext *ctx);
 
@@ -1885,9 +1806,12 @@
         if (mm_cdma_parse_crm_test_response (response,
                                              &min, &max,
                                              &error)) {
+            MMModemCdmaRmProtocol current;
+
+            current = mm_bearer_properties_get_rm_protocol (mm_bearer_peek_config (MM_BEARER (ctx->self)));
             /* Check if value within the range */
-            if (ctx->self->priv->rm_protocol >= min &&
-                ctx->self->priv->rm_protocol <= max) {
+            if (current >= min &&
+                current <= max) {
                 /* Fine, go on with next step */
                 ctx->step++;
                 interface_initialization_step (ctx);
@@ -1897,8 +1821,7 @@
             error = g_error_new (MM_CORE_ERROR,
                                  MM_CORE_ERROR_FAILED,
                                  "Requested RM protocol '%s' is not supported",
-                                 mm_modem_cdma_rm_protocol_get_string (
-                                     ctx->self->priv->rm_protocol));
+                                 mm_modem_cdma_rm_protocol_get_string (current));
         }
 
         /* Failed, set as fatal as well */
@@ -1933,7 +1856,7 @@
         self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_NONE;
         break;
     case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING:
-        if (self->priv->allow_roaming) {
+        if (mm_bearer_properties_get_allow_roaming (mm_bearer_peek_config (MM_BEARER (self)))) {
             mm_dbg ("Bearer allowed to connect, registered in roaming network");
             self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_NONE;
         } else {
@@ -1964,7 +1887,7 @@
 
     if (cdma1x_state == MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING ||
         evdo_state == MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING) {
-        if (self->priv->allow_roaming) {
+        if (mm_bearer_properties_get_allow_roaming (mm_bearer_peek_config (MM_BEARER (self)))) {
             mm_dbg ("Bearer allowed to connect, registered in roaming network");
             self->priv->reason_cdma = CONNECTION_FORBIDDEN_REASON_NONE;
         } else {
@@ -1998,7 +1921,8 @@
         /* If a specific RM protocol is given, we need to check whether it is
          * supported. */
         if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->modem)) &&
-            ctx->self->priv->rm_protocol != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) {
+            mm_bearer_properties_get_rm_protocol (
+                mm_bearer_peek_config (MM_BEARER (ctx->self))) != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) {
             mm_base_modem_at_command_full (ctx->modem,
                                            ctx->port,
                                            "+CRM=?",
@@ -2106,79 +2030,12 @@
         cancellable,
         callback,
         user_data,
-        MM_BEARER_MODEM,                      modem,
-        MM_BROADBAND_BEARER_3GPP_APN,         mm_bearer_properties_get_apn (properties),
-        MM_BROADBAND_BEARER_CDMA_NUMBER,      mm_bearer_properties_get_number (properties),
-        MM_BROADBAND_BEARER_CDMA_RM_PROTOCOL, mm_bearer_properties_get_rm_protocol (properties),
-        MM_BROADBAND_BEARER_IP_TYPE,          mm_bearer_properties_get_ip_type (properties),
-        MM_BROADBAND_BEARER_ALLOW_ROAMING,    mm_bearer_properties_get_allow_roaming (properties),
+        MM_BEARER_MODEM,  modem,
+        MM_BEARER_CONFIG, properties,
         NULL);
 }
 
 static void
-set_property (GObject *object,
-              guint prop_id,
-              const GValue *value,
-              GParamSpec *pspec)
-{
-    MMBroadbandBearer *self = MM_BROADBAND_BEARER (object);
-
-    switch (prop_id) {
-    case PROP_3GPP_APN:
-        g_free (self->priv->apn);
-        self->priv->apn = g_value_dup_string (value);
-        break;
-    case PROP_CDMA_NUMBER:
-        g_free (self->priv->number);
-        self->priv->number = g_value_dup_string (value);
-        break;
-    case PROP_CDMA_RM_PROTOCOL:
-        self->priv->rm_protocol = g_value_get_enum (value);
-        break;
-    case PROP_IP_TYPE:
-        g_free (self->priv->ip_type);
-        self->priv->ip_type = g_value_dup_string (value);
-        break;
-    case PROP_ALLOW_ROAMING:
-        self->priv->allow_roaming = g_value_get_boolean (value);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-        break;
-    }
-}
-
-static void
-get_property (GObject *object,
-              guint prop_id,
-              GValue *value,
-              GParamSpec *pspec)
-{
-    MMBroadbandBearer *self = MM_BROADBAND_BEARER (object);
-
-    switch (prop_id) {
-    case PROP_3GPP_APN:
-        g_value_set_string (value, self->priv->apn);
-        break;
-    case PROP_CDMA_NUMBER:
-        g_value_set_string (value, self->priv->number);
-        break;
-    case PROP_CDMA_RM_PROTOCOL:
-        g_value_set_enum (value, self->priv->rm_protocol);
-        break;
-    case PROP_IP_TYPE:
-        g_value_set_string (value, self->priv->ip_type);
-        break;
-    case PROP_ALLOW_ROAMING:
-        g_value_set_boolean (value, self->priv->allow_roaming);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-        break;
-    }
-}
-
-static void
 mm_broadband_bearer_init (MMBroadbandBearer *self)
 {
     /* Initialize private data */
@@ -2188,8 +2045,6 @@
 
     /* Set defaults */
     self->priv->connection_type = CONNECTION_TYPE_NONE;
-    self->priv->allow_roaming = TRUE;
-    self->priv->rm_protocol = MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN;
     self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_NONE;
     self->priv->reason_cdma = CONNECTION_FORBIDDEN_REASON_NONE;
 }
@@ -2225,17 +2080,6 @@
 }
 
 static void
-finalize (GObject *object)
-{
-    MMBroadbandBearer *self = MM_BROADBAND_BEARER (object);
-
-    g_free (self->priv->apn);
-    g_free (self->priv->ip_type);
-
-    G_OBJECT_CLASS (mm_broadband_bearer_parent_class)->finalize (object);
-}
-
-static void
 async_initable_iface_init (GAsyncInitableIface *iface)
 {
     iface->init_async = initable_init_async;
@@ -2251,13 +2095,8 @@
     g_type_class_add_private (object_class, sizeof (MMBroadbandBearerPrivate));
 
     /* Virtual methods */
-    object_class->get_property = get_property;
-    object_class->set_property = set_property;
-    object_class->finalize = finalize;
     object_class->dispose = dispose;
 
-    bearer_class->cmp_properties = cmp_properties;
-    bearer_class->expose_properties = expose_properties;
     bearer_class->connect = connect;
     bearer_class->connect_finish = connect_finish;
     bearer_class->disconnect = disconnect;
@@ -2276,45 +2115,4 @@
     klass->disconnect_3gpp_finish = detailed_disconnect_finish;
     klass->disconnect_cdma = disconnect_cdma;
     klass->disconnect_cdma_finish = detailed_disconnect_finish;
-
-    properties[PROP_3GPP_APN] =
-        g_param_spec_string (MM_BROADBAND_BEARER_3GPP_APN,
-                             "3GPP APN",
-                             "Access Point Name to use in the connection",
-                             NULL,
-                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-    g_object_class_install_property (object_class, PROP_3GPP_APN, properties[PROP_3GPP_APN]);
-
-    properties[PROP_CDMA_NUMBER] =
-        g_param_spec_string (MM_BROADBAND_BEARER_CDMA_NUMBER,
-                             "Number to dial",
-                             "Number to dial when launching the CDMA connection",
-                             NULL,
-                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-    g_object_class_install_property (object_class, PROP_CDMA_NUMBER, properties[PROP_CDMA_NUMBER]);
-
-    properties[PROP_CDMA_RM_PROTOCOL] =
-        g_param_spec_enum (MM_BROADBAND_BEARER_CDMA_RM_PROTOCOL,
-                           "Rm Protocol",
-                           "Protocol to use in the CDMA Rm interface",
-                           MM_TYPE_MODEM_CDMA_RM_PROTOCOL,
-                           MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN,
-                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-    g_object_class_install_property (object_class, PROP_CDMA_RM_PROTOCOL, properties[PROP_CDMA_RM_PROTOCOL]);
-
-    properties[PROP_IP_TYPE] =
-        g_param_spec_string (MM_BROADBAND_BEARER_IP_TYPE,
-                             "IP type",
-                             "IP setup to use in the connection",
-                             NULL,
-                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-    g_object_class_install_property (object_class, PROP_IP_TYPE, properties[PROP_IP_TYPE]);
-
-    properties[PROP_ALLOW_ROAMING] =
-        g_param_spec_boolean (MM_BROADBAND_BEARER_ALLOW_ROAMING,
-                              "Allow roaming",
-                              "Whether connections are allowed when roaming",
-                              TRUE,
-                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-    g_object_class_install_property (object_class, PROP_ALLOW_ROAMING, properties[PROP_ALLOW_ROAMING]);
 }
diff --git a/src/mm-broadband-bearer.h b/src/mm-broadband-bearer.h
index 5911a61..00478f2 100644
--- a/src/mm-broadband-bearer.h
+++ b/src/mm-broadband-bearer.h
@@ -146,11 +146,6 @@
 MMBearer *mm_broadband_bearer_new_finish (GAsyncResult *res,
                                           GError **error);
 
-const gchar *mm_broadband_bearer_get_3gpp_apn         (MMBroadbandBearer *self);
-guint        mm_broadband_bearer_get_cdma_rm_protocol (MMBroadbandBearer *self);
-const gchar *mm_broadband_bearer_get_ip_type          (MMBroadbandBearer *self);
-gboolean     mm_broadband_bearer_get_allow_roaming    (MMBroadbandBearer *self);
-
 guint        mm_broadband_bearer_get_3gpp_cid         (MMBroadbandBearer *self);
 
 #endif /* MM_BROADBAND_BEARER_H */
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
index 60a9be9..9a787ca 100644
--- a/src/mm-broadband-modem.c
+++ b/src/mm-broadband-modem.c
@@ -340,6 +340,7 @@
 static const ModemCaps modem_caps[] = {
     { "+CGSM",     MM_MODEM_CAPABILITY_GSM_UMTS  },
     { "+CLTE2",    MM_MODEM_CAPABILITY_LTE       }, /* Novatel */
+    { "+CLTE1",    MM_MODEM_CAPABILITY_LTE       }, /* Novatel */
     { "+CLTE",     MM_MODEM_CAPABILITY_LTE       },
     { "+CIS707-A", MM_MODEM_CAPABILITY_CDMA_EVDO },
     { "+CIS707A",  MM_MODEM_CAPABILITY_CDMA_EVDO }, /* Cmotech */
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index a99eac3..6019dfe 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -1332,7 +1332,7 @@
 /*************************************************************************/
 
 MMModemAccessTechnology
-mm_3gpp_string_to_access_tech (const gchar *string)
+mm_string_to_access_tech (const gchar *string)
 {
     MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
 
@@ -1367,6 +1367,15 @@
     if (strcasestr (string, "GSM"))
         act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM;
 
+    if (strcasestr (string, "EvDO Rel0"))
+        act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDO0;
+
+    if (strcasestr (string, "EvDO RelA"))
+        act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDOA;
+
+    if (strcasestr (string, "1xRTT"))
+        act |= MM_MODEM_ACCESS_TECHNOLOGY_1XRTT;
+
     return act;
 }
 
diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h
index 10ec800..8da0a17 100644
--- a/src/mm-modem-helpers.h
+++ b/src/mm-modem-helpers.h
@@ -147,7 +147,7 @@
 MMModem3gppFacility mm_3gpp_acronym_to_facility (const gchar *str);
 gchar *mm_3gpp_facility_to_acronym (MMModem3gppFacility facility);
 
-MMModemAccessTechnology mm_3gpp_string_to_access_tech (const gchar *string);
+MMModemAccessTechnology mm_string_to_access_tech (const gchar *string);
 
 gchar *mm_3gpp_parse_operator (const gchar *reply,
                                MMModemCharset cur_charset);
diff --git a/src/mm-sms.c b/src/mm-sms.c
index 849722f..b68ee76 100644
--- a/src/mm-sms.c
+++ b/src/mm-sms.c
@@ -1292,7 +1292,7 @@
                               "Is multipart",
                               "Flag specifying if the SMS is multipart",
                               FALSE,
-                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+                              G_PARAM_READWRITE);
     g_object_class_install_property (object_class, PROP_IS_MULTIPART, properties[PROP_IS_MULTIPART]);
 
     properties[PROP_MAX_PARTS] =
@@ -1300,7 +1300,7 @@
                            "Max parts",
                            "Maximum number of parts composing this SMS",
                            1,255, 1,
-                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+                           G_PARAM_READWRITE);
     g_object_class_install_property (object_class, PROP_MAX_PARTS, properties[PROP_MAX_PARTS]);
 
     properties[PROP_MULTIPART_REFERENCE] =
@@ -1308,6 +1308,6 @@
                            "Multipart reference",
                            "Common reference for all parts in the multipart SMS",
                            0, G_MAXUINT, 0,
-                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+                           G_PARAM_READWRITE);
     g_object_class_install_property (object_class, PROP_MULTIPART_REFERENCE, properties[PROP_MULTIPART_REFERENCE]);
 }
diff --git a/src/tests/test-sms-part.c b/src/tests/test-sms-part.c
index d3bf7d5..8ed02d5 100644
--- a/src/tests/test-sms-part.c
+++ b/src/tests/test-sms-part.c
@@ -45,6 +45,7 @@
                               const gchar *expected_smsc,
                               const gchar *expected_number,
                               const gchar *expected_timestamp,
+                              gboolean expected_multipart,
                               const gchar *expected_text,
                               const guint8 *expected_data,
                               gsize expected_data_size,
@@ -65,6 +66,7 @@
         g_assert_cmpstr (expected_timestamp, ==, mm_sms_part_get_timestamp (part));
     if (expected_text)
         g_assert_cmpstr (expected_text, ==, mm_sms_part_get_text (part));
+    g_assert_cmpuint (expected_multipart, ==, mm_sms_part_should_concat (part));
 
     if (expected_data) {
         guint32 i;
@@ -87,6 +89,7 @@
                            const gchar *expected_smsc,
                            const gchar *expected_number,
                            const gchar *expected_timestamp,
+                           gboolean expected_multipart,
                            const gchar *expected_text,
                            const guint8 *expected_data,
                            gsize expected_data_size,
@@ -99,6 +102,7 @@
                                   expected_smsc,
                                   expected_number,
                                   expected_timestamp,
+                                  expected_multipart,
                                   expected_text,
                                   expected_data,
                                   expected_data_size,
@@ -133,6 +137,7 @@
         "+12404492164", /* smsc */
         "+16175927198", /* number */
         "110228115050-05", /* timestamp */
+        FALSE,
         "Here's a longer message [{with some extended characters}] "
         "thrown in, such as £ and ΩΠΨ and §¿ as well.", /* text */
         NULL, 0, 0);
@@ -153,6 +158,7 @@
         "+79037011111", /* smsc */
         "InternetSMS", /* number */
         "110329192004+04", /* timestamp */
+        FALSE,
         "ั‚ะตัั‚", /* text */
         NULL, 0, 0);
 }
@@ -172,6 +178,7 @@
         "+12345678901", /* smsc */
         "+18005551212", /* number */
         "110101123456+00", /* timestamp */
+        FALSE,
         "hellohello", /* text */
         NULL, 0, 0);
 }
@@ -192,6 +199,7 @@
         "+12345678901", /* smsc */
         "+18005551212", /* number */
         "110101123456+00", /* timestamp */
+        FALSE,
         "hellohello", /* text */
         NULL, 0, 0);
 }
@@ -212,6 +220,7 @@
         "+12345678901", /* smsc */
         "+18005551212", /* number */
         "110101123456+00", /* timestamp */
+        FALSE,
         "hellohello", /* text */
         NULL, 0, 0);
 }
@@ -232,6 +241,7 @@
         "+12345678901", /* smsc */
         "18005551212", /* number, no plus */
         "110101123456+00", /* timestamp */
+        FALSE,
         "hellohello", /* text */
         NULL, 0, 0);
 }
@@ -253,6 +263,7 @@
         "+12345678901", /* smsc */
         "+18005551212", /* number */
         "110101123456+00", /* timestamp */
+        FALSE,
         "", /* text */
         expected_data, /* data */
         sizeof (expected_data), /* data size */
@@ -301,6 +312,7 @@
         "+33609001390", /* smsc */
         "1800", /* number */
         "110624130815+02", /* timestamp */
+        FALSE,
         "Info SFR - Confidentiel, à ne jamais transmettre -\r\n"
         "Voici votre nouveau mot de passe : sw2ced pour gérer "
         "votre compte SFR sur www.sfr.fr ou par téléphone au 963", /* text */
@@ -324,6 +336,7 @@
         "+12345678901", /* smsc */
         "+18005551212", /* number */
         "110101123456+00", /* timestamp */
+        FALSE,
         "", /* text */
         expected_data, /* data */
         sizeof (expected_data), /* data size */
@@ -369,12 +382,46 @@
         "+31653131316", /* smsc */
         "1002", /* number */
         "110629233219+02", /* timestamp */
+        TRUE,
         "Welkom, bel om uw Voicemail te beluisteren naar +31612001233"
         " (PrePay: *100*1233#). Voicemail ontvangen is altijd gratis."
         " Voor gebruik van mobiel interne", /* text */
         NULL, 0, 0);
 }
 
+static void
+test_pdu_multipart (void)
+{
+    static const gchar *hexpdu1 =
+        "07912160130320F5440B916171056429F5000021405291650569A00500034C0201A9E8F41C949E"
+        "83C2207B599E07B1DFEE33885E9ED341E4F23C7D7697C920FA1B54C697E5E3F4BC0C6AD7D9F434"
+        "081E96D341E3303C2C4EB3D3F4BC0B94A483E6E8779D4D06CDD1EF3BA80E0785E7A0B7BB0C6A97"
+        "E7F3F0B9CC02B9DF7450780EA2DFDF2C50780EA2A3CBA0BA9B5C96B3F369F71954768FDFE4B4FB"
+        "0C9297E1F2F2BCECA6CF41";
+    static const gchar *hexpdu2 =
+        "07912160130320F6440B916171056429F5000021405291651569320500034C0202E9E8301D4447"
+        "9741F0B09C3E0785E56590BCCC0ED3CB6410FD0D7ABBCBA0B0FB4D4797E52E10";
+
+    common_test_part_from_hexpdu (
+        hexpdu1,
+        "+12063130025", /* smsc */
+        "+16175046925", /* number */
+        "120425195650-04", /* timestamp */
+        TRUE, /* multipart! */
+        "This is a very long test designed to exercise multi part capability. It should "
+        "show up as one message, not as two, as the underlying encoding represents ", /* text */
+        NULL, 0, 0);
+
+    common_test_part_from_hexpdu (
+        hexpdu2,
+        "+12063130026", /* smsc */
+        "+16175046925", /* number */
+        "120425195651-04", /* timestamp */
+        TRUE, /* multipart! */
+        "that the parts are related to one another. ", /* text */
+        NULL, 0, 0);
+}
+
 /********************* SMS ADDRESS ENCODER TESTS *********************/
 
 static void
@@ -637,6 +684,7 @@
     g_test_add_func ("/MM/SMS/PDU-Parser/pdu-dcsf-8bit", test_pdu_dcsf_8bit);
     g_test_add_func ("/MM/SMS/PDU-Parser/pdu-insufficient-data", test_pdu_insufficient_data);
     g_test_add_func ("/MM/SMS/PDU-Parser/pdu-udhi", test_pdu_udhi);
+    g_test_add_func ("/MM/SMS/PDU-Parser/pdu-multipart", test_pdu_multipart);
 
     g_test_add_func ("/MM/SMS/Address-Encoder/smsc-intl", test_address_encode_smsc_intl);
     g_test_add_func ("/MM/SMS/Address-Encoder/smsc-unknown", test_address_encode_smsc_unknown);