[PATCH] New Modem Manager Plugin for GCT Semiconductor's GDM724X usb devices

Won Kang wkang77 at gmail.com
Thu Aug 8 04:34:42 PDT 2013


Signed-off-by: Won Kang <wonkang at gctsemi.com>
---
 plugins/Makefile.am                   |   16 +-
 plugins/gct/mm-broadband-bearer-gct.c | 1121 +++++++++++++++++++++++++++
 plugins/gct/mm-broadband-bearer-gct.h |   70 ++
 plugins/gct/mm-broadband-modem-gct.c  | 1333 +++++++++++++++++++++++++++++++++
 plugins/gct/mm-broadband-modem-gct.h  |   54 ++
 plugins/gct/mm-plugin-gct.c           |  224 ++++++
 plugins/gct/mm-plugin-gct.h           |   47 ++
 plugins/gct/mm-sim-gct.c              |  131 ++++
 plugins/gct/mm-sim-gct.h              |   52 ++
 9 files changed, 3047 insertions(+), 1 deletion(-)
 create mode 100644 plugins/gct/mm-broadband-bearer-gct.c
 create mode 100644 plugins/gct/mm-broadband-bearer-gct.h
 create mode 100644 plugins/gct/mm-broadband-modem-gct.c
 create mode 100644 plugins/gct/mm-broadband-modem-gct.h
 create mode 100644 plugins/gct/mm-plugin-gct.c
 create mode 100644 plugins/gct/mm-plugin-gct.h
 create mode 100644 plugins/gct/mm-sim-gct.c
 create mode 100644 plugins/gct/mm-sim-gct.h

diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 83f1fae..b3b3b6d 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -77,7 +77,8 @@ pkglib_LTLIBRARIES = \
 	libmm-plugin-sierra.la \
 	libmm-plugin-mbm.la \
 	libmm-plugin-via.la \
-	libmm-plugin-telit.la
+	libmm-plugin-telit.la \
+	libmm-plugin-gct.la
 
 # Generic
 libmm_plugin_generic_la_SOURCES = \
@@ -297,6 +298,19 @@ libmm_plugin_samsung_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(ICERA_COMMO
 libmm_plugin_samsung_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
 libmm_plugin_samsung_la_LIBADD = $(ICERA_COMMON_LIBADD_FLAGS)
 
+# Gct modem
+libmm_plugin_gct_la_SOURCES = \
+    gct/mm-plugin-gct.c \
+    gct/mm-plugin-gct.h \
+    gct/mm-broadband-modem-gct.c \
+    gct/mm-broadband-modem-gct.h \
+    gct/mm-broadband-bearer-gct.c \
+    gct/mm-broadband-bearer-gct.h \
+    gct/mm-sim-gct.c \
+    gct/mm-sim-gct.h
+libmm_plugin_gct_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
+libmm_plugin_gct_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
 # Cinterion (previously Siemens) modem
 libmm_plugin_cinterion_la_SOURCES = \
 	cinterion/mm-plugin-cinterion.c \
diff --git a/plugins/gct/mm-broadband-bearer-gct.c b/plugins/gct/mm-broadband-bearer-gct.c
new file mode 100644
index 0000000..3f23f08
--- /dev/null
+++ b/plugins/gct/mm-broadband-bearer-gct.c
@@ -0,0 +1,1121 @@
+/* -*- 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) 2013 GCT Semiconductor, Inc.
+ * Author: Glen Lee <glenlee at gctsemi.com>
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <arpa/inet.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-base-modem-at.h"
+#include "mm-broadband-bearer-gct.h"
+#include "mm-log.h"
+#include "mm-modem-helpers.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-3gpp.h"
+
+G_DEFINE_TYPE (MMBroadbandBearerGct, mm_broadband_bearer_gct, MM_TYPE_BROADBAND_BEARER);
+
+#define MAX_CID_SUPPORTED 4
+
+#define CGCONTRDP_TAG "+CGCONTRDP: "
+#define GCTNETCFG_TAG "%GCTNETCFG: "
+
+typedef enum {
+    MM_BEARER_IP_TYPE_NONE  = 0,
+    MM_BEARER_IP_TYPE_V4    = 1 << 0,
+    MM_BEARER_IP_TYPE_V6    = 1 << 1
+} MMBearerIpType;
+
+struct _MMBroadbandBearerGctPrivate {
+    guint auth_idx;
+    gpointer connect_pending;
+    guint connect_pending_id;
+    gulong connect_cancellable_id;
+    gulong connect_port_closed_id;
+    MMBearerIpType ip_type;
+};
+
+/*****************************************************************************/
+/* 3GPP IP config retrieval (sub-step of the 3GPP Connection sequence) */
+
+typedef enum {
+    GET_IP_STEP_FIRST,
+    GET_IP_STEP_SECOND,
+    GET_IP_STEP_LAST
+} GetIp3gppConfigStep;
+
+typedef struct {
+    guint cid;
+    guint nic_type;
+    guint internal_type;
+} GctNetCfg;
+
+typedef struct {
+    MMBroadbandBearerGct *self;
+    MMBearerIpConfig *ipv4_config;
+    MMBearerIpConfig *ipv6_config;
+} MMBearerIpConfigSet;
+
+typedef struct {
+    MMBroadbandBearerGct *self;
+    MMBaseModem *modem;
+    MMAtSerialPort *primary;
+    guint cid;
+    GSimpleAsyncResult *result;
+    guint gct_net_conf_cnt;
+    GctNetCfg gct_net_conf[5];
+    GetIp3gppConfigStep step;
+} GetIpConfig3gppContext;
+
+static guint
+char_cnt(gchar *ptr, gchar ch)
+{
+    guint i = 0;
+
+    while(*ptr)
+        if(*ptr++ == ch)
+            i++;
+
+    return i;
+}
+
+static void
+get_ip_config_context_complete_and_free (GetIpConfig3gppContext *ctx)
+{
+    g_simple_async_result_complete_in_idle (ctx->result);
+    g_object_unref (ctx->result);
+    g_object_unref (ctx->primary);
+    g_object_unref (ctx->modem);
+    g_object_unref (ctx->self);
+    g_slice_free (GetIpConfig3gppContext, ctx);
+}
+
+static gboolean
+get_ip_config_3gpp_finish (MMBroadbandBearer *self,
+                           GAsyncResult *res,
+                           MMBearerIpConfig **ipv4_config,
+                           MMBearerIpConfig **ipv6_config,
+                           GError **error)
+{
+    MMBearerIpConfigSet *ip_config_set;
+
+    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+        return FALSE;
+
+    ip_config_set = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+
+    mm_dbg("Bearer IPv4 configuration %s", ip_config_set->ipv4_config ? "SET" : "NOT SET");
+    mm_dbg("Bearer IPv6 configuration %s", ip_config_set->ipv6_config ? "SET" : "NOT SET");
+
+    *ipv4_config = ip_config_set->ipv4_config ? g_object_ref (ip_config_set->ipv4_config) : NULL;
+    *ipv6_config = ip_config_set->ipv6_config ? g_object_ref (ip_config_set->ipv6_config) : NULL;
+
+    return TRUE;
+}
+
+static void
+_bearer_ipv4_config_set_gateway_and_prefix(MMBearerIpConfigSet *ip_config_set, gchar *ip)
+{
+    gint prefix = 0;
+    struct in_addr inp;
+    guint32 ipv4;
+    guint32 gateway;
+    gchar net;
+
+    if (!inet_pton (AF_INET, ip, &ipv4)) {
+        /* Error converting IP */
+        return;
+    }
+
+    net = ipv4 & 0xFF;
+
+    /* prefix calculation based on default ip class */
+    if ((net & 0x80) == 0)
+        prefix = 8;
+    else if ((net & 0xC0) == 0x80)
+        prefix = 16;
+    else if ((net & 0xE0) == 0xC0)
+        prefix = 24;
+
+    /* fake gateway based on the inverted value of the last octet of the ip address */
+    gateway = ipv4;
+    gateway &= 0x00FFFFFF;
+    gateway |= (~ipv4) & 0xFF000000;
+    memcpy(&inp, &gateway, sizeof(struct in_addr));
+
+    mm_dbg("ipv4 address: %d,%d,%d,%d/%d",
+        ipv4>>0 & 0xFF,
+        ipv4>>8 & 0xFF,
+        ipv4>>16 & 0xFF,
+        ipv4>>24 & 0xFF,
+        prefix
+    );
+    mm_dbg("ipv4 gateway: %d.%d.%d.%d",
+        gateway>>0 & 0xFF,
+        gateway>>8 & 0xFF,
+        gateway>>16 & 0xFF,
+        gateway>>24 & 0xFF
+    );
+
+    /* configure the prefix and gateway */
+    mm_bearer_ip_config_set_prefix (ip_config_set->ipv4_config, prefix);
+    mm_bearer_ip_config_set_gateway (ip_config_set->ipv4_config, inet_ntoa(inp));
+}
+
+static void
+run_get_ip_config_context_step (GetIpConfig3gppContext *ctx);
+
+static void
+ip_config_ready (MMBaseModem *modem,
+                 GAsyncResult *res,
+                 GetIpConfig3gppContext *ctx)
+{
+    MMBearerIpConfigSet *ip_config_set = NULL;
+    const gchar *response;
+    GError *error = NULL;
+    gchar **items;
+    gchar *dns_v4[3] = { 0, };
+    gchar *dns_v6[3] = { 0, };
+    guint i;
+    guint gct_loop;
+    guint dns_i;
+    gchar *items_sub;
+    gchar **lines;
+    gchar **iter;
+    guint32 tmp_ipv4;
+    struct in6_addr tmp_pv6;
+    gchar* ip;
+
+    ip_config_set = (MMBearerIpConfigSet *)g_malloc0(sizeof(MMBearerIpConfigSet));
+    ip_config_set->self = ctx->self;
+
+    response = mm_base_modem_at_command_full_finish (MM_BASE_MODEM (modem), res, &error);
+    if (error) {
+        g_simple_async_result_take_error (ctx->result, error);
+        get_ip_config_context_complete_and_free (ctx);
+        return;
+    }
+
+    /*
+     * TODO: use a regex to parse this
+     * The response for IPv6 would be something like the following
+
+        +CGCONTRDP: 3,6,"vzwinternet.mnc480.mcc311.gprs","10.168.86.201","","198.224.174.135","198.224.173.135","",""
+
+        +CGCONTRDP: 3,6,"vzwinternet.mnc480.mcc311.gprs","38.0.16.16.177.35.78.84.0.0.0.25.171.20.242.1","","32.1.72.136.0.97.255.0.6.4.0.13.0.0.0.0","32.1.72.136.0.104.255.0.6.8.0.13.0.0.0.0","32.1.72.136.0.3.255.240.0.192.1.4.0.0.0.83","32.1.72.136.0.2.255.240.0.160.1.4.0.0.0.83"
+
+        OK
+    */
+
+    lines = g_strsplit_set (response, "\r\n", 0);
+    if (!lines)
+    {
+        get_ip_config_context_complete_and_free (ctx);
+        return;
+    }
+
+    for (iter = lines; iter && *iter; iter++) {
+
+        /* skip emtpy line */
+        if (!g_str_has_prefix (*iter, CGCONTRDP_TAG)) {
+            continue;
+        }
+
+        items = g_strsplit ( mm_strip_tag (*iter, CGCONTRDP_TAG), ",", 0);
+
+        for (i = 0, dns_i = 0; items[i]; i++) {
+            if (i == 0) { /* CID */
+                gint num;
+
+                if (!mm_get_int_from_str (items[i], &num) ||
+                    num != ctx->cid) {
+                    error = g_error_new (MM_CORE_ERROR,
+                                         MM_CORE_ERROR_FAILED,
+                                         "Unknown CID in CGCONTRDP response ("
+                                         "got %d, expected %d)", (guint) num, ctx->cid);
+                    break;
+                }
+
+            } else if (i == 1) { /* bearer id */
+                /* Do nothing */
+            } else if (i == 2) { /* APN */
+                /* Do nothing */
+            } else if (i == 3) { /* IP address */
+
+                for( gct_loop = 0; gct_loop < ctx->gct_net_conf_cnt; gct_loop++)
+                {
+                    if (ctx->gct_net_conf[gct_loop].cid == ctx->cid)
+                    {
+                        guint internal_type;
+                        internal_type= ctx->gct_net_conf[gct_loop].internal_type;
+                        mm_dbg("ip config: cid %d, internal pdn %d", ctx->cid, internal_type);
+
+                        if(internal_type)
+                            goto skip_ip_configuration;
+                    }
+
+                }
+
+                /* Remove the starting and ending quatation mark */
+                items_sub = g_strndup (items[i] + 1, strlen(items[i]) - 2);
+
+                if (char_cnt(items_sub, '.') == 3) /* IPv4 */
+                {
+                    if (!inet_pton (AF_INET, items_sub, &tmp_ipv4))
+                    {
+                        g_clear_object (&ip_config_set);
+                        g_free(items_sub);
+                        break;
+                    }
+
+                    ip_config_set->ipv4_config = mm_bearer_ip_config_new ();
+                    mm_bearer_ip_config_set_method (ip_config_set->ipv4_config, MM_BEARER_IP_METHOD_STATIC);
+                    mm_bearer_ip_config_set_address (ip_config_set->ipv4_config,  items_sub);
+                    _bearer_ipv4_config_set_gateway_and_prefix(ip_config_set, items_sub);
+
+                    ctx->self->priv->ip_type = MM_BEARER_IP_TYPE_V4;
+                }
+                else if (char_cnt(items_sub, '.') == 15) /* IPv6 */
+                {
+                    gchar **items_ipv6 = g_strsplit (items_sub, ".", 0);
+
+                    ip = g_strdup_printf ("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+                                          atoi(items_ipv6[0]),
+                                          atoi(items_ipv6[1]),
+                                          atoi(items_ipv6[2]),
+                                          atoi(items_ipv6[3]),
+                                          atoi(items_ipv6[4]),
+                                          atoi(items_ipv6[5]),
+                                          atoi(items_ipv6[6]),
+                                          atoi(items_ipv6[7]),
+                                          atoi(items_ipv6[8]),
+                                          atoi(items_ipv6[9]),
+                                          atoi(items_ipv6[10]),
+                                          atoi(items_ipv6[11]),
+                                          atoi(items_ipv6[12]),
+                                          atoi(items_ipv6[13]),
+                                          atoi(items_ipv6[14]),
+                                          atoi(items_ipv6[15]));
+                    g_free(items_ipv6);
+
+                    if (!inet_pton (AF_INET6, ip, &tmp_pv6))
+                    {
+                        g_clear_object (&ip_config_set);
+                        g_free(ip);
+                        g_free(items_sub);
+                        break;
+                    }
+
+                    ip_config_set->ipv6_config = mm_bearer_ip_config_new ();
+
+                    mm_bearer_ip_config_set_method (ip_config_set->ipv6_config, MM_BEARER_IP_METHOD_STATIC);
+                    mm_bearer_ip_config_set_address (ip_config_set->ipv6_config, ip);
+
+                    ctx->self->priv->ip_type = MM_BEARER_IP_TYPE_V6;
+                    g_free(ip);
+                }
+
+                g_free(items_sub);
+            }
+            else if (i == 5 || i == 6) { /* DNS entries */
+                items_sub = g_strndup (items[i] + 1, strlen(items[i]) - 2);
+
+                if (ctx->self->priv->ip_type == MM_BEARER_IP_TYPE_V4)
+                {
+                    mm_dbg("ipv4 dns: %s", items_sub);
+                    if (inet_pton (AF_INET, items_sub, &tmp_ipv4)) {
+                        dns_v4[dns_i++] = g_strdup(items_sub);
+                    }
+                }
+                else if (ctx->self->priv->ip_type == MM_BEARER_IP_TYPE_V6)
+                {
+                    gchar **items_ipv6 = g_strsplit (items_sub, ".", 0);
+                    ip = g_strdup_printf ("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+                                          atoi(items_ipv6[0]),
+                                          atoi(items_ipv6[1]),
+                                          atoi(items_ipv6[2]),
+                                          atoi(items_ipv6[3]),
+                                          atoi(items_ipv6[4]),
+                                          atoi(items_ipv6[5]),
+                                          atoi(items_ipv6[6]),
+                                          atoi(items_ipv6[7]),
+                                          atoi(items_ipv6[8]),
+                                          atoi(items_ipv6[9]),
+                                          atoi(items_ipv6[10]),
+                                          atoi(items_ipv6[11]),
+                                          atoi(items_ipv6[12]),
+                                          atoi(items_ipv6[13]),
+                                          atoi(items_ipv6[14]),
+                                          atoi(items_ipv6[15]));
+                    g_free(items_ipv6);
+                    mm_dbg("ipv4 dns: %s", items_sub);
+                    if (inet_pton (AF_INET6, ip, &tmp_pv6)) {
+                        dns_v6[dns_i++] = g_strdup(ip);
+                    }
+
+                    g_free(ip);
+                }
+                g_free(items_sub);
+            }
+        }
+skip_ip_configuration:
+        g_strfreev (items);
+    }
+
+    if (!ip_config_set) {
+        if (error)
+            g_simple_async_result_take_error (ctx->result, error);
+        else
+            g_simple_async_result_set_error (ctx->result,
+                                             MM_CORE_ERROR,
+                                             MM_CORE_ERROR_FAILED,
+                                             "Couldn't get IP config: couldn't parse response '%s'",
+                                             response);
+    } else {
+        guint k;
+
+        /* If we got IPv4 DNS entries, set them in the IP config */
+        if (dns_v4[0])
+        {
+            mm_bearer_ip_config_set_dns (ip_config_set->ipv4_config, (const gchar **)dns_v4);
+            for (k=0; dns_v4[k]; k++)
+                g_free(dns_v4[k]);
+        }
+
+        /* If we got IPv6 DNS entries, set them in the IP config */
+        if (dns_v6[0])
+        {
+            mm_bearer_ip_config_set_dns (ip_config_set->ipv6_config, (const gchar **)dns_v6);
+            for (k=0; dns_v6[k]; k++)
+                g_free(dns_v6[k]);
+        }
+
+        g_simple_async_result_set_op_res_gpointer (ctx->result,
+                                                   ip_config_set,
+                                                   (GDestroyNotify)g_object_unref);
+    }
+
+    ctx->step++;
+    run_get_ip_config_context_step(ctx);
+
+    g_strfreev (lines);
+
+}
+
+static void
+gct_net_config_ready (MMBaseModem *modem,
+                 GAsyncResult *res,
+                 GetIpConfig3gppContext *ctx)
+{
+    const gchar *response;
+    GError *error = NULL;
+    gchar **lines;
+    gchar **iter;
+    guint gct_net_conf_cnt = 0;
+
+    gint cid;
+    gint nic_type;
+    gint internal_type;
+
+    response = mm_base_modem_at_command_full_finish (MM_BASE_MODEM (modem), res, &error);
+
+    if (error) {
+        g_simple_async_result_take_error (ctx->result, error);
+        get_ip_config_context_complete_and_free (ctx);
+        return;
+    }
+
+    lines = g_strsplit_set (response, "\r\n", 0);
+    if (!lines)
+    {
+        get_ip_config_context_complete_and_free (ctx);
+        return;
+    }
+
+    for (iter = lines; iter && *iter; iter++) {
+
+        /* skip emtpy line */
+        if (!g_str_has_prefix (*iter, GCTNETCFG_TAG)) {
+            continue;
+        }
+
+        sscanf (*iter + strlen(GCTNETCFG_TAG), "%d,%x,%d", &cid, &nic_type, &internal_type);
+
+        mm_dbg("GCT net config: cid(%d), nic_type(0x%x), internal_type(%d)",
+               cid, nic_type, internal_type);
+
+        ctx->gct_net_conf[gct_net_conf_cnt].cid = cid;
+        ctx->gct_net_conf[gct_net_conf_cnt].nic_type = nic_type;
+        ctx->gct_net_conf[gct_net_conf_cnt++].internal_type = internal_type;
+    }
+
+    g_strfreev (lines);
+
+    ctx->gct_net_conf_cnt = gct_net_conf_cnt;
+
+    ctx->step++;
+    run_get_ip_config_context_step(ctx);
+}
+
+static void
+run_get_ip_config_context_step (GetIpConfig3gppContext *ctx)
+{
+    gchar *command;
+    
+    switch (ctx->step) {
+        case GET_IP_STEP_FIRST:
+            mm_base_modem_at_command_full (
+                MM_BASE_MODEM (ctx->modem),
+                ctx->primary,
+                "%GCTNETCFG",
+                3,
+                FALSE,
+                FALSE, /* raw */
+                NULL, /* cancellable */
+                (GAsyncReadyCallback)gct_net_config_ready,
+                ctx);
+            break;
+
+        case GET_IP_STEP_SECOND:
+            command = g_strdup_printf ("+CGCONTRDP=%d", ctx->cid);
+            mm_base_modem_at_command_full (
+                MM_BASE_MODEM (ctx->modem),
+                ctx->primary,
+                command,
+                3,
+                FALSE,
+                FALSE, /* raw */
+                NULL, /* cancellable */
+                (GAsyncReadyCallback)ip_config_ready,
+                ctx);
+            g_free (command);
+            break;
+
+        case GET_IP_STEP_LAST:
+            get_ip_config_context_complete_and_free (ctx);
+            break;
+    }
+}
+
+static void
+get_ip_config_3gpp (MMBroadbandBearer *self,
+                    MMBroadbandModem *modem,
+                    MMAtSerialPort *primary,
+                    MMAtSerialPort *secondary,
+                    MMPort *data,
+                    guint cid,
+                    GAsyncReadyCallback callback,
+                    gpointer user_data)
+{
+    GetIpConfig3gppContext *ctx;
+
+    ctx = g_slice_new0 (GetIpConfig3gppContext);
+    ctx->self = g_object_ref (self);
+    ctx->modem = g_object_ref (modem);
+    ctx->primary = g_object_ref (primary);
+    ctx->cid = cid;
+    ctx->result = g_simple_async_result_new (G_OBJECT (self),
+                                             callback,
+                                             user_data,
+                                             get_ip_config_3gpp);
+
+    ctx->step = GET_IP_STEP_FIRST;
+
+    run_get_ip_config_context_step (ctx);
+}
+
+/*****************************************************************************/
+/* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */
+
+typedef enum {
+    DIAL_3GPP_STEP_FIRST,
+    DIAL_3GPP_STEP_PLMN_SELECTION,
+    DIAL_3GPP_STEP_PS_ATTACH,
+    DIAL_3GPP_STEP_GET_DATA_PORT,
+    DIAL_3GPP_STEP_LAST
+} Dial3gppStep;
+
+typedef struct {
+    MMBroadbandBearerGct *self;
+    MMBaseModem *modem;
+    MMAtSerialPort *primary;
+    guint cid;
+    GCancellable *cancellable;
+    GSimpleAsyncResult *result;
+    MMPort *data;
+    Dial3gppStep step;
+} Dial3gppContext;
+
+static void
+dial_3gpp_context_complete_and_free (Dial3gppContext *ctx)
+{
+    g_simple_async_result_complete_in_idle (ctx->result);
+    g_object_unref (ctx->cancellable);
+    g_object_unref (ctx->result);
+    g_object_unref (ctx->data);
+    g_object_unref (ctx->primary);
+    g_object_unref (ctx->modem);
+    g_object_unref (ctx->self);
+    g_slice_free (Dial3gppContext, ctx);
+}
+
+static MMPort *
+dial_3gpp_finish (MMBroadbandBearer *self,
+                  GAsyncResult *res,
+                  GError **error)
+{
+    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+        return NULL;
+
+    return MM_PORT (g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))));
+}
+
+static void dial_3gpp_context_step (Dial3gppContext *ctx);
+
+static void
+cops_ready (MMBaseModem *modem,
+             GAsyncResult *res,
+             Dial3gppContext *ctx)
+{
+    const gchar *response;
+    GError *error = NULL;
+
+    response = mm_base_modem_at_command_full_finish (MM_BASE_MODEM (modem), res, &error);
+
+    if(error) {
+        g_simple_async_result_take_error (ctx->result, error);
+        dial_3gpp_context_complete_and_free (ctx);
+        return;
+    }
+
+    /* Go on */
+    ctx->step++;
+    dial_3gpp_context_step (ctx);
+}
+
+static void
+cgact_ready (MMBaseModem *modem,
+             GAsyncResult *res,
+             Dial3gppContext *ctx)
+{
+    const gchar *response;
+    GError *error = NULL;
+
+    response = mm_base_modem_at_command_full_finish (MM_BASE_MODEM (modem), res, &error);
+
+    if(error) {
+        g_simple_async_result_take_error (ctx->result, error);
+        dial_3gpp_context_complete_and_free (ctx);
+        return;
+    }
+
+    /* Go on */
+    ctx->step++;
+    dial_3gpp_context_step (ctx);
+}
+
+static void
+data_port_ready (MMBaseModem *modem,
+             GAsyncResult *res,
+             Dial3gppContext *ctx)
+{
+    const gchar *response;
+    GError *error = NULL;
+    gchar **lines;
+    gchar **iter;
+    GList *dataports;
+    GList *l;
+    gint cid, nic_type, internal_type;
+
+    response = mm_base_modem_at_command_full_finish (MM_BASE_MODEM (modem), res, &error);
+
+    if (error) {
+        g_simple_async_result_take_error (ctx->result, error);
+        dial_3gpp_context_complete_and_free (ctx);
+        return;
+    }
+
+    lines = g_strsplit_set (response, "\r\n", 0);
+    if (!lines) {
+        dial_3gpp_context_complete_and_free (ctx);
+        return;
+    }
+
+    for (iter = lines; iter && *iter; iter++) {
+        if (!g_str_has_prefix (*iter, GCTNETCFG_TAG)) {
+            continue;
+        }
+
+        if (sscanf (*iter + strlen(GCTNETCFG_TAG), "%d, %x, %d", &cid, &nic_type, &internal_type) != 3) {
+            continue;
+        }
+
+        mm_dbg("GCT net config: cid(%d), nic_type(0x%x), internal_type(%d)",
+               cid, nic_type, internal_type);
+
+        if (cid == ctx->cid)
+        {
+            /* Mask out the nic_type to match the pdn number */
+            nic_type &= 0x0F;
+            g_assert (nic_type >= 0 && nic_type < MAX_CID_SUPPORTED);
+
+            /* From the list of data ports, find one that matches the nic_type value */
+            dataports = mm_base_modem_get_data_ports (modem);
+            for (l = dataports; l; l = g_list_next (l) ) {
+                MMPort *port = (MMPort *) l->data;
+                const gchar *port_name = mm_port_get_device (port);
+                int device_id, pdn_number;
+
+                if (sscanf (port_name, "lte%dpdn%d", &device_id, &pdn_number) == 2) {
+                    if (ctx->cid == cid && pdn_number == nic_type) {
+                        /* Found the port with this pdn number and cid */
+                        ctx->data = port;
+                        mm_dbg("GCT net interface %s chosen for cid (%d)",
+                               port_name,
+                               ctx->cid);
+                        break;
+                    }
+                }
+            }
+
+            break;
+        }
+    }
+
+    g_strfreev (lines);
+
+    if (!ctx->data) {
+        g_simple_async_result_set_error (
+            ctx->result,
+            MM_CORE_ERROR,
+            MM_CORE_ERROR_NOT_FOUND,
+            "No valid data port found to launch connection");
+        dial_3gpp_context_complete_and_free (ctx);
+        return;
+    }
+
+    /* Go on */
+    ctx->step++;
+    dial_3gpp_context_step (ctx);
+}
+
+static void
+dial_3gpp_context_step (Dial3gppContext *ctx)
+{
+    gchar *command;
+    command = NULL;
+    if (g_cancellable_is_cancelled (ctx->cancellable)) {
+         g_simple_async_result_set_error (ctx->result,
+                                          MM_CORE_ERROR,
+                                          MM_CORE_ERROR_CANCELLED,
+                                          "Dial operation has been cancelled");
+         dial_3gpp_context_complete_and_free (ctx);
+         return;
+    }
+
+    switch (ctx->step) {
+    case DIAL_3GPP_STEP_FIRST:
+        /* Fall down */
+        ctx->step++;
+    case DIAL_3GPP_STEP_PLMN_SELECTION: {
+        MMBroadbandModemGct *modem = NULL;
+        gchar* command;
+
+        g_object_get (ctx->self,
+                      MM_BEARER_MODEM, &modem,
+                      NULL);
+
+        g_assert (modem != NULL);
+
+        command = g_strdup_printf ("+COPS=0");
+
+        mm_base_modem_at_command_full (MM_BASE_MODEM (ctx->modem),
+                                       ctx->primary,
+                                       command,
+                                       120,
+                                       FALSE,
+                                       FALSE, /* raw */
+                                       NULL, /* cancellable */
+                                       (GAsyncReadyCallback)cops_ready,
+                                       ctx);
+        return;
+    }
+    case DIAL_3GPP_STEP_PS_ATTACH: {
+
+        MMBroadbandModemGct *modem = NULL;
+        gchar* command;
+
+        g_object_get (ctx->self,
+                      MM_BEARER_MODEM, &modem,
+                      NULL);
+
+        g_assert (modem != NULL);
+
+        command = g_strdup_printf ("+CGACT=1,%d",ctx->cid);
+
+        mm_base_modem_at_command_full (MM_BASE_MODEM (ctx->modem),
+                                       ctx->primary,
+                                       command,
+                                       10,
+                                       FALSE,
+                                       FALSE, /* raw */
+                                       NULL, /* cancellable */
+                                       (GAsyncReadyCallback)cgact_ready,
+                                       ctx);
+        return;
+    }
+    case DIAL_3GPP_STEP_GET_DATA_PORT: {
+
+        MMBroadbandModemGct *modem = NULL;
+
+        g_object_get (ctx->self,
+                      MM_BEARER_MODEM, &modem,
+                      NULL);
+
+        g_assert (modem != NULL);
+
+        mm_base_modem_at_command_full (MM_BASE_MODEM (ctx->modem),
+                                       ctx->primary,
+                                       "%GCTNETCFG",
+                                       10,
+                                       FALSE,
+                                       FALSE, /* raw */
+                                       NULL, /* cancellable */
+                                       (GAsyncReadyCallback)data_port_ready,
+                                       ctx);
+        return;
+    }
+    case DIAL_3GPP_STEP_LAST:
+        g_simple_async_result_set_op_res_gpointer (ctx->result,
+                                                   g_object_ref (ctx->data),
+                                                   (GDestroyNotify)g_object_unref);
+        dial_3gpp_context_complete_and_free (ctx);
+        return;
+    }
+}
+
+static void
+dial_3gpp (MMBroadbandBearer *self,
+           MMBaseModem *modem,
+           MMAtSerialPort *primary,
+           guint cid,
+           GCancellable *cancellable,
+           GAsyncReadyCallback callback,
+           gpointer user_data)
+{
+    Dial3gppContext *ctx;
+
+    g_assert (primary != NULL);
+
+    ctx = g_slice_new0 (Dial3gppContext);
+    ctx->self = g_object_ref (self);
+    ctx->modem = g_object_ref (modem);
+    ctx->primary = g_object_ref (primary);
+    ctx->cid = cid;
+    ctx->result = g_simple_async_result_new (G_OBJECT (self),
+                                             callback,
+                                             user_data,
+                                             dial_3gpp);
+    ctx->cancellable = g_object_ref (cancellable);
+
+    /* We need a net data port */
+
+    if (ctx->cid > MAX_CID_SUPPORTED) {
+        g_simple_async_result_set_error (
+            ctx->result,
+            MM_CORE_ERROR,
+            MM_CORE_ERROR_NOT_FOUND,
+            "Invalid CID %d, Gct modem supports up to only %d", ctx->cid, MAX_CID_SUPPORTED);
+        dial_3gpp_context_complete_and_free (ctx);
+        return;
+    }
+
+    ctx->step = DIAL_3GPP_STEP_FIRST;
+
+    dial_3gpp_context_step (ctx);
+}
+
+/*****************************************************************************/
+/* 3GPP disconnect */
+
+typedef enum {
+    DISCONNECT_3GPP_STEP_FIRST,
+    DISCONNECT_3GPP_STEP_PS_DETACH,
+    DISCONNECT_3GPP_STEP_PLMN_SELECTION,
+    DISCONNECT_3GPP_STEP_LAST
+} Disconnect3gppStep;
+
+typedef struct {
+    MMBroadbandBearerGct *self;
+    MMBaseModem *modem;
+    MMAtSerialPort *primary;
+    GSimpleAsyncResult *result;
+    Disconnect3gppStep step;
+} DisconnectContext;
+
+static void
+disconnect_context_complete_and_free (DisconnectContext *ctx)
+{
+    g_simple_async_result_complete (ctx->result);
+    g_object_unref (ctx->result);
+    g_object_unref (ctx->primary);
+    g_object_unref (ctx->self);
+    g_object_unref (ctx->modem);
+    g_free (ctx);
+}
+
+static gboolean
+disconnect_3gpp_finish (MMBroadbandBearer *self,
+                        GAsyncResult *res,
+                        GError **error)
+{
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+
+static void disconnect_3gpp_context_step (DisconnectContext *ctx);
+
+static void
+re_cops_ready (MMBaseModem *modem,
+             GAsyncResult *res,
+             DisconnectContext *ctx)
+{
+    const gchar *response;
+    GError *error = NULL;
+
+    response = mm_base_modem_at_command_full_finish (MM_BASE_MODEM (modem), res, &error);
+
+    if(error) {
+        g_simple_async_result_take_error (ctx->result, error);
+        disconnect_context_complete_and_free (ctx);
+        return;
+    }
+
+    /* Go on */
+    ctx->step++;
+    disconnect_3gpp_context_step (ctx);
+}
+
+
+static void
+detach_ready (MMBaseModem *modem,
+             GAsyncResult *res,
+             DisconnectContext *ctx)
+{
+    const gchar *response;
+    GError *error = NULL;
+
+    response = mm_base_modem_at_command_full_finish (MM_BASE_MODEM (modem), res, &error);
+
+    if(error) {
+        g_simple_async_result_take_error (ctx->result, error);
+        disconnect_context_complete_and_free (ctx);
+        return;
+    }
+
+    sleep(1);
+
+    /* Go on */
+    ctx->step++;
+    disconnect_3gpp_context_step (ctx);
+}
+
+static void
+disconnect_3gpp_context_step (DisconnectContext *ctx)
+{
+    gchar *command;
+    command = NULL;
+
+    switch (ctx->step) {
+    case DISCONNECT_3GPP_STEP_FIRST:
+        /* Fall down */
+        ctx->step++;
+    case DISCONNECT_3GPP_STEP_PS_DETACH: {
+        MMBroadbandModemGct *modem = NULL;
+        gchar* command;
+
+        g_object_get (ctx->self,
+                      MM_BEARER_MODEM, &modem,
+                      NULL);
+
+        g_assert (modem != NULL);
+
+        command = g_strdup_printf ("+CGATT=0");
+
+        mm_base_modem_at_command_full (MM_BASE_MODEM (ctx->modem),
+                                       ctx->primary,
+                                       command,
+                                       10,
+                                       FALSE,
+                                       FALSE, /* raw */
+                                       NULL, /* cancellable */
+                                       (GAsyncReadyCallback)detach_ready,
+                                       ctx);
+        return;
+    }
+    case DISCONNECT_3GPP_STEP_PLMN_SELECTION: {
+
+        MMBroadbandModemGct *modem = NULL;
+        gchar* command;
+
+        g_object_get (ctx->self,
+                      MM_BEARER_MODEM, &modem,
+                      NULL);
+
+        g_assert (modem != NULL);
+
+        command = g_strdup_printf ("+COPS=0");
+
+        mm_base_modem_at_command_full (MM_BASE_MODEM (ctx->modem),
+                                       ctx->primary,
+                                       command,
+                                       120,
+                                       FALSE,
+                                       FALSE, /* raw */
+                                       NULL, /* cancellable */
+                                       (GAsyncReadyCallback)re_cops_ready,
+                                       ctx);
+        return;
+    }
+
+    case DISCONNECT_3GPP_STEP_LAST:
+        g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+        disconnect_context_complete_and_free (ctx);
+        return;
+    }
+}
+
+static void
+disconnect_3gpp (MMBroadbandBearer *self,
+                 MMBroadbandModem *modem,
+                 MMAtSerialPort *primary,
+                 MMAtSerialPort *secondary,
+                 MMPort *data,
+                 guint cid,
+                 GAsyncReadyCallback callback,
+                 gpointer user_data)
+{
+    DisconnectContext *ctx;
+    MMBroadbandModemGct *gct_modem = NULL;
+
+    g_object_get (self,
+                  MM_BEARER_MODEM, &gct_modem,
+                  NULL);
+
+    g_assert (modem != NULL);
+
+    g_assert (primary != NULL);
+
+    ctx = g_new0 (DisconnectContext, 1);
+    ctx->self = g_object_ref (self);
+    ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
+    ctx->primary = g_object_ref (primary);
+    ctx->result = g_simple_async_result_new (G_OBJECT (self),
+                                             callback,
+                                             user_data,
+                                             disconnect_3gpp);
+    ctx->step = DISCONNECT_3GPP_STEP_FIRST;
+
+    disconnect_3gpp_context_step (ctx);
+}
+
+/*****************************************************************************/
+
+MMBearer *
+mm_broadband_bearer_gct_new_finish (GAsyncResult *res,
+                                    GError **error)
+{
+    GObject *bearer;
+    GObject *source;
+
+    source = g_async_result_get_source_object (res);
+    bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error);
+    g_object_unref (source);
+
+    if (!bearer)
+        return NULL;
+
+    /* Only export valid bearers */
+    mm_bearer_export (MM_BEARER (bearer));
+
+    return MM_BEARER (bearer);
+}
+
+void
+mm_broadband_bearer_gct_new (MMBroadbandModemGct *modem,
+                             MMBearerProperties *config,
+                             GCancellable *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data)
+{
+    const gchar *apn;
+    MMBearerIpFamily ip_type;
+
+    g_async_initable_new_async (
+        MM_TYPE_BROADBAND_BEARER_GCT,
+        G_PRIORITY_DEFAULT,
+        cancellable,
+        callback,
+        user_data,
+        MM_BEARER_MODEM, modem,
+        MM_BEARER_CONFIG, config,
+        NULL);
+
+    apn = mm_bearer_properties_get_apn (config);
+    ip_type = mm_bearer_properties_get_ip_type (config);
+
+    mm_dbg ("Bearer creation : apn=%s, ip_type=%s", apn, mm_3gpp_get_pdp_type_from_ip_family (ip_type));
+
+    /* Configure the default ip type */
+    if (ip_type != MM_BEARER_IP_FAMILY_IPV4V6 && strncasecmp(apn, "vzw", 3) == 0) {
+        mm_bearer_properties_set_ip_type (config, MM_BEARER_IP_FAMILY_IPV4V6);
+        mm_dbg ("Bearer IP type changed to %s for VZW",
+        mm_3gpp_get_pdp_type_from_ip_family ( mm_bearer_properties_get_ip_type (config)));
+    }
+}
+
+static void
+mm_broadband_bearer_gct_init (MMBroadbandBearerGct *self)
+{
+    /* Initialize private data */
+    self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
+                                              MM_TYPE_BROADBAND_BEARER_GCT,
+                                              MMBroadbandBearerGctPrivate);
+}
+
+static void
+mm_broadband_bearer_gct_class_init (MMBroadbandBearerGctClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
+
+    g_type_class_add_private (object_class, sizeof (MMBroadbandBearerGctPrivate));
+
+    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;
+}
diff --git a/plugins/gct/mm-broadband-bearer-gct.h b/plugins/gct/mm-broadband-bearer-gct.h
new file mode 100644
index 0000000..09dec80
--- /dev/null
+++ b/plugins/gct/mm-broadband-bearer-gct.h
@@ -0,0 +1,70 @@
+/* -*- 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) 2013 GCT Semiconductor, Inc.
+ * Author: Glen Lee <glenlee at gctsemi.com>
+ */
+
+#ifndef MM_BROADBAND_BEARER_GCT_H
+#define MM_BROADBAND_BEARER_GCT_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-broadband-bearer.h"
+#include "mm-broadband-modem-gct.h"
+
+#define MM_TYPE_BROADBAND_BEARER_GCT            (mm_broadband_bearer_gct_get_type ())
+#define MM_BROADBAND_BEARER_GCT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_GCT, MMBroadbandBearerGct))
+#define MM_BROADBAND_BEARER_GCT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  MM_TYPE_BROADBAND_BEARER_GCT, MMBroadbandBearerGctClass))
+#define MM_IS_BROADBAND_BEARER_GCT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_GCT))
+#define MM_IS_BROADBAND_BEARER_GCT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_BROADBAND_BEARER_GCT))
+#define MM_BROADBAND_BEARER_GCT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_BROADBAND_BEARER_GCT, MMBroadbandBearerGctClass))
+
+typedef enum {
+    MM_BROADBAND_BEARER_GCT_CONNECTION_STATUS_UNKNOWN,
+    MM_BROADBAND_BEARER_GCT_CONNECTION_STATUS_CONNECTED,
+    MM_BROADBAND_BEARER_GCT_CONNECTION_STATUS_CONNECTION_FAILED,
+    MM_BROADBAND_BEARER_GCT_CONNECTION_STATUS_DISCONNECTED
+} MMBroadbandBearerGctConnectionStatus;
+
+typedef struct _MMBroadbandBearerGct MMBroadbandBearerGct;
+typedef struct _MMBroadbandBearerGctClass MMBroadbandBearerGctClass;
+typedef struct _MMBroadbandBearerGctPrivate MMBroadbandBearerGctPrivate;
+
+struct _MMBroadbandBearerGct {
+    MMBroadbandBearer parent;
+    MMBroadbandBearerGctPrivate *priv;
+};
+
+struct _MMBroadbandBearerGctClass {
+    MMBroadbandBearerClass parent;
+};
+
+GType mm_broadband_bearer_gct_get_type (void);
+
+/* Default 3GPP bearer creation implementation */
+void mm_broadband_bearer_gct_new (MMBroadbandModemGct *modem,
+                                  MMBearerProperties *config,
+                                  GCancellable *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data);
+MMBearer *mm_broadband_bearer_gct_new_finish (GAsyncResult *res,
+                                              GError **error);
+
+void mm_broadband_bearer_gct_report_connection_status (MMBroadbandBearerGct *self,
+                                                       MMBroadbandBearerGctConnectionStatus status);
+
+#endif /* MM_BROADBAND_BEARER_GCT_H */
diff --git a/plugins/gct/mm-broadband-modem-gct.c b/plugins/gct/mm-broadband-modem-gct.c
new file mode 100644
index 0000000..537824b
--- /dev/null
+++ b/plugins/gct/mm-broadband-modem-gct.c
@@ -0,0 +1,1333 @@
+/* -*- 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) 2013 GCT Semiconductor, Inc.
+ * Author: Glen Lee <glenlee at gctsemi.com>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "ModemManager.h"
+#include "mm-base-modem-at.h"
+#include "mm-broadband-modem-gct.h"
+#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-3gpp-ussd.h"
+#include "mm-iface-modem-location.h"
+#include "mm-iface-modem-time.h"
+#include "mm-iface-modem-messaging.h"
+#include "mm-broadband-bearer-gct.h"
+#include "mm-modem-helpers.h"
+#include "mm-log.h"
+#include "mm-iface-modem.h"
+#include "mm-sim-gct.h"
+#include "mm-bearer-list.h"
+#include "mm-sms-part.h"
+#include "mm-sms-list.h"
+
+
+static MMIfaceModem *iface_modem_parent;
+
+static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
+static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface);
+static void iface_modem_location_init(MMIfaceModemLocation *iface);
+static void iface_modem_time_init(MMIfaceModemTime *iface);
+static void iface_modem_messaging_init (MMIfaceModemMessaging *iface);
+
+
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemGct, mm_broadband_modem_gct, MM_TYPE_BROADBAND_MODEM, 0,
+                        G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
+                        G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
+                        G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_USSD, iface_modem_3gpp_ussd_init)
+                        G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
+                        G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)
+                        G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init)
+                        );
+
+/*****************************************************************************/
+/* Setup ports (Broadband modem class) */
+
+static void
+setup_ports (MMBroadbandModem *self)
+{
+    /* Call parent's setup ports first always */
+    MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_gct_parent_class)->setup_ports (self);
+}
+
+/*****************************************************************************/
+/* Create SIM (Modem interface) */
+
+static MMSim *
+modem_create_sim_finish (MMIfaceModem *self,
+                         GAsyncResult *res,
+                         GError **error)
+{
+    return mm_sim_gct_new_finish (res, error);
+}
+
+static void
+modem_create_sim (MMIfaceModem *self,
+                  GAsyncReadyCallback callback,
+                  gpointer user_data)
+{
+    /* New GCT SIM */
+    mm_sim_gct_new (MM_BASE_MODEM (self),
+                    NULL, /* cancellable */
+                    callback,
+                    user_data);
+}
+
+/*****************************************************************************/
+/* Bearer Information */
+
+typedef struct {
+    gboolean connected;
+} BearerListReportStatusForeachContext;
+
+static void
+bearer_list_info_foreach (MMBearer *bearer,
+                          BearerListReportStatusForeachContext *ctx)
+{
+    guint status;
+    guint cid;
+
+    if (!MM_IS_BROADBAND_BEARER_GCT (bearer))
+        return;
+
+    status = mm_bearer_get_status(MM_BEARER(bearer));
+    cid = mm_broadband_bearer_get_3gpp_cid(MM_BROADBAND_BEARER(bearer));
+
+    mm_dbg("bearer with cid(%d) status(%d)", cid, status);
+
+    if (status != MM_BEARER_STATUS_DISCONNECTED)
+        ctx->connected ++;
+}
+
+void
+modem_bearer_get_info(MMBroadbandModemGct *self,
+                      guint8 *connected)
+{
+    MMBearerList *list = NULL;
+    BearerListReportStatusForeachContext ctx;
+
+    ctx.connected = 0;
+
+    g_object_get (self,
+                  MM_IFACE_MODEM_BEARER_LIST, &list,
+                  NULL);
+
+    mm_bearer_list_foreach (list,
+                            (MMBearerListForeachFunc)bearer_list_info_foreach,
+                            &ctx);
+
+    mm_dbg("number of bearers active = %d", ctx.connected);
+
+    *connected = ctx.connected;
+
+    g_object_unref (list);
+}
+
+/*****************************************************************************/
+/* Create Bearer (Modem interface) */
+
+static MMBearer *
+modem_create_bearer_finish (MMIfaceModem *self,
+                            GAsyncResult *res,
+                            GError **error)
+{
+    MMBearer *bearer;
+
+    bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+    mm_dbg ("New GCT bearer created at DBus path '%s'", mm_bearer_get_path (bearer));
+
+    return g_object_ref (bearer);
+}
+
+static void
+broadband_bearer_gct_new_ready (GObject *source,
+                                GAsyncResult *res,
+                                GSimpleAsyncResult *simple)
+{
+    MMBearer *bearer = NULL;
+    GError *error = NULL;
+
+    bearer = mm_broadband_bearer_gct_new_finish (res, &error);
+    if (!bearer)
+        g_simple_async_result_take_error (simple, error);
+    else
+        g_simple_async_result_set_op_res_gpointer (simple,
+                                                   bearer,
+                                                   (GDestroyNotify)g_object_unref);
+    g_simple_async_result_complete (simple);
+    g_object_unref (simple);
+}
+
+static void
+modem_create_bearer (MMIfaceModem *self,
+                     MMBearerProperties *properties,
+                     GAsyncReadyCallback callback,
+                     gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        modem_create_bearer);
+
+    mm_broadband_bearer_gct_new (MM_BROADBAND_MODEM_GCT (self),
+                                 properties,
+                                 NULL, /* cancellable */
+                                 (GAsyncReadyCallback)broadband_bearer_gct_new_ready,
+                                 result);
+}
+
+/*****************************************************************************/
+/* Setup_format */
+
+static gboolean
+messaging_setup_sms_format_finish (MMIfaceModemMessaging *self,
+                                            GAsyncResult *res,
+                                            GError **error)
+{
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+cmgf_format_check_ready (MMBroadbandModem *self,
+                         GAsyncResult *res,
+                         GSimpleAsyncResult *simple)
+{
+    GError *error = NULL;
+    const gchar *response;
+
+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+    if (error) {
+        mm_dbg ("Failed to query supported SMS modes: '%s'",
+                error->message);
+        g_error_free (error);
+    }
+
+    mm_dbg ("Use Text mode");
+
+    g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+    g_simple_async_result_complete (simple);
+    g_object_unref (simple);
+}
+
+static void
+messaging_setup_sms_format (MMIfaceModemMessaging *self,
+                                     GAsyncReadyCallback callback,
+                                     gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        messaging_setup_sms_format);
+
+    /* Setup Format SMS formats */
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "+CMGF=1",
+                              3,
+                              TRUE,
+                              (GAsyncReadyCallback)cmgf_format_check_ready,
+                              result);
+}
+
+/*****************************************************************************/
+/* Setup/cleanup messaging related unsolicited events (Messaging interface) */
+
+static gboolean
+modem_messaging_setup_cleanup_unsolicited_events_finish (MMIfaceModemMessaging *self,
+                                                         GAsyncResult *res,
+                                                         GError **error)
+{
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+typedef struct {
+    MMBroadbandModem *self;
+    GSimpleAsyncResult *result;
+    guint idx;
+} SmsPartContext;
+
+static void
+sms_part_context_complete_and_free (SmsPartContext *ctx)
+{
+    g_simple_async_result_complete (ctx->result);
+    g_object_unref (ctx->result);
+    g_object_unref (ctx->self);
+    g_free (ctx);
+}
+
+static void
+sms_part_ready (MMBroadbandModem *self,
+                GAsyncResult *res,
+                SmsPartContext *ctx)
+{
+    MMSmsPart *part;
+    gint rv, status, tpdu_len;
+    gchar pdu[SMS_MAX_PDU_LEN + 1];
+    const gchar *response;
+    GError *error = NULL;
+
+    /* Always always always unlock mem1 storage. Warned you've been. */
+    mm_broadband_modem_unlock_sms_storages (self, TRUE, FALSE);
+
+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+    if (error) {
+        /* We're really ignoring this error afterwards, as we don't have a callback
+         * passed to the async operation, so just log the error here. */
+        mm_warn ("Couldn't retrieve SMS part: '%s'",
+                 error->message);
+        g_simple_async_result_take_error (ctx->result, error);
+        sms_part_context_complete_and_free (ctx);
+        return;
+    }
+
+    rv = sscanf (response, "+CMGR: %d,,%d %" G_STRINGIFY (SMS_MAX_PDU_LEN) "s",
+                 &status, &tpdu_len, pdu);
+    if (rv != 3) {
+        error = g_error_new (MM_CORE_ERROR,
+                             MM_CORE_ERROR_FAILED,
+                             "Failed to parse CMGR response (parsed %d items)", rv);
+        mm_warn ("Couldn't retrieve SMS part: '%s'", error->message);
+        g_simple_async_result_take_error (ctx->result, error);
+        sms_part_context_complete_and_free (ctx);
+        return;
+    }
+
+    part = mm_sms_part_new_from_pdu (ctx->idx, pdu, &error);
+    if (part) {
+        mm_dbg ("Correctly parsed PDU (%d)", ctx->idx);
+        mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self),
+                                            part,
+                                            MM_SMS_STATE_RECEIVED,
+                                            MM_SMS_STORAGE_ME);
+    } else {
+        /* Don't treat the error as critical */
+        mm_dbg ("Error parsing PDU (%d): %s", ctx->idx, error->message);
+        g_error_free (error);
+    }
+
+    /* All done */
+    g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+    sms_part_context_complete_and_free (ctx);
+}
+
+static void
+indication_lock_storages_ready (MMBroadbandModem *self,
+                                GAsyncResult *res,
+                                SmsPartContext *ctx)
+{
+    gchar *command;
+    /* Storage now set and locked */
+
+    /* Retrieve the message */
+    command = g_strdup_printf ("+CMGR=%d", ctx->idx);
+    mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+                              command,
+                              10,
+                              FALSE,
+                              (GAsyncReadyCallback)sms_part_ready,
+                              ctx);
+    g_free (command);
+}
+
+static void
+cmt_received (MMAtSerialPort *port,
+               GMatchInfo *info,
+               MMBroadbandModem *self)
+{
+    SmsPartContext *ctx;
+    MMSmsStorage storage;
+
+    storage = MM_SMS_STORAGE_ME;
+
+    ctx = g_new0 (SmsPartContext, 1);
+    ctx->self = g_object_ref (self);
+    ctx->result = g_simple_async_result_new (G_OBJECT (self), NULL, NULL, cmt_received);
+    ctx->idx = 1;
+
+    /* First, request to set the proper storage to read from */
+    mm_broadband_modem_lock_sms_storages (ctx->self,
+                                          storage,
+                                          MM_SMS_STORAGE_UNKNOWN,
+                                          (GAsyncReadyCallback)indication_lock_storages_ready,
+                                          ctx);
+}
+
+static GRegex *
+mm_3gpp_cmt_regex_get (void)
+{
+    return g_regex_new ("\\r\\n\\+CMT.*",
+                    G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW | G_REGEX_OPTIMIZE,
+                    0,
+                    NULL);
+}
+
+static void
+set_messaging_unsolicited_events_handlers (MMIfaceModemMessaging *self,
+                                           gboolean enable,
+                                           GAsyncReadyCallback callback,
+                                           gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+    MMAtSerialPort *ports[1];
+    GRegex *cmt_regex;
+
+    mm_dbg("set_messaging_unsolicited_events_handlers enable(%d)", enable);
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        set_messaging_unsolicited_events_handlers);
+
+    cmt_regex = mm_3gpp_cmt_regex_get ();
+    ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+
+    /* Set/unset unsolicited CMT event handler */
+    mm_dbg ("(%s) %s messaging unsolicited events handlers",
+            mm_port_get_device (MM_PORT (ports[0])),
+            enable ? "Setting" : "Removing");
+    mm_at_serial_port_add_unsolicited_msg_handler (
+        ports[0],
+        cmt_regex,
+        enable ? (MMAtSerialUnsolicitedMsgFn) cmt_received : NULL,
+        enable ? self : NULL,
+        NULL);
+
+    g_regex_unref (cmt_regex);
+    g_simple_async_result_set_op_res_gboolean (result, TRUE);
+    g_simple_async_result_complete_in_idle (result);
+    g_object_unref (result);
+}
+
+static void
+modem_messaging_setup_unsolicited_events (MMIfaceModemMessaging *self,
+                                          GAsyncReadyCallback callback,
+                                          gpointer user_data)
+{
+    set_messaging_unsolicited_events_handlers (self, TRUE, callback, user_data);
+}
+
+/*****************************************************************************/
+/* Enable unsolicited events (SMS indications) (Messaging interface) */
+
+static gboolean
+messaging_enable_unsolicited_events_finish (MMIfaceModemMessaging *self,
+                                            GAsyncResult *res,
+                                            GError **error)
+{
+    return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+}
+
+static void
+messaging_enable_unsolicited_events (MMIfaceModemMessaging *self,
+                                     GAsyncReadyCallback callback,
+                                     gpointer user_data)
+{
+    /* AT+CNMI=<mode>,[<mt>[,<bm>[,<ds>[,<bfr>]]]]
+     *  but <bfr> should be either not set, or equal to 1;
+     *  and <ds> can be only either 0 or 2 (EGS5)
+     */
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "+CNMI=1,3,3,2,1",
+                              3,
+                              FALSE,
+                              callback,
+                              user_data);
+}
+
+/*****************************************************************************/
+/* Reset (Modem interface) */
+
+static gboolean
+modem_reset_finish (MMIfaceModem *self,
+                    GAsyncResult *res,
+                    GError **error)
+{
+    return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+}
+
+static void
+modem_reset (MMIfaceModem *self,
+             GAsyncReadyCallback callback,
+             gpointer user_data)
+{
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "+SYSRESET",
+                              3,
+                              FALSE,
+                              callback,
+                              user_data);
+}
+
+/*****************************************************************************/
+/* Modem power down (Modem interface) */
+
+static gboolean
+modem_power_down_finish (MMIfaceModem *self,
+                         GAsyncResult *res,
+                         GError **error)
+{
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+modem_power_down_ready (MMBaseModem *self,
+                       GAsyncResult *res,
+                       GSimpleAsyncResult *simple)
+{
+    mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL);
+
+    g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+    g_simple_async_result_complete (simple);
+    g_object_unref (simple);
+}
+
+static void
+modem_power_down (MMIfaceModem *self,
+                  GAsyncReadyCallback callback,
+                  gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        modem_power_down);
+
+    /* run AT+CFUN=4 (power save) */
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "+CFUN=1",
+                              3,
+                              FALSE,
+                              (GAsyncReadyCallback)modem_power_down_ready,
+                              result);
+}
+
+/*****************************************************************************/
+/* Register in network (3GPP interface) */
+typedef enum {
+    REGISTRATION_STEP_FIRST,
+    REGISTRATION_STEP_SECOND
+} RegistrationStep;
+
+typedef struct {
+    MMBroadbandModem *self;
+    GSimpleAsyncResult *result;
+    RegistrationStep step;
+} RunRegistrationChecksContext;
+
+static void
+run_registration_checks_context_complete_and_free (RunRegistrationChecksContext *ctx)
+{
+    g_simple_async_result_complete_in_idle (ctx->result);
+    g_object_unref (ctx->result);
+    g_object_unref (ctx->self);
+    g_free (ctx);
+}
+
+static gboolean
+modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self,
+                                           GAsyncResult *res,
+                                           GError **error)
+{
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void run_registration_checks_context_step (RunRegistrationChecksContext *ctx);
+
+#define GCTREG "\\+(COPS):\\s*0*([0-9])\\s*,\\s*0*([0-9])\\s*,([^,\\)]*),\\s*0*([0-9])"
+
+static void
+registration_status_check_ready (MMBroadbandModem *self,
+                                 GAsyncResult *res,
+                                 RunRegistrationChecksContext *ctx)
+{
+    GRegex *r;
+    GMatchInfo *match_info;
+    const gchar *response;
+    GError *error;
+    gchar * reply;
+    gchar * cops_mode;
+    gchar * cops_format;
+    gchar * cops_operator = NULL;
+    gchar * cops_access_technology;
+
+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+
+    if(!response) {
+        g_simple_async_result_take_error (ctx->result, error);
+        run_registration_checks_context_complete_and_free (ctx);
+    }
+
+    reply = strstr (response, "+COPS: ");
+
+    r = g_regex_new (GCTREG, G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+
+    g_assert (r != NULL);
+
+    if (g_regex_match (r, reply, 0, &match_info)) {
+        cops_mode = g_match_info_fetch (match_info, 2);
+        cops_format = g_match_info_fetch (match_info, 3);
+        cops_operator = g_match_info_fetch (match_info, 4);
+        cops_access_technology = g_match_info_fetch (match_info, 5);
+
+        if (strncmp (cops_operator+1, "FFFFF", 5) != 0)
+        {
+            mm_iface_modem_3gpp_update_access_technologies (MM_IFACE_MODEM_3GPP (self), atoi(cops_access_technology));
+            mm_iface_modem_3gpp_update_eps_registration_state( MM_IFACE_MODEM_3GPP(self), MM_MODEM_3GPP_REGISTRATION_STATE_HOME );
+
+            mm_dbg("modem is registered to network %s", cops_operator);
+            g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+        } else {
+            mm_dbg("modem is not registered to network");
+        }
+    }
+
+    g_match_info_free (match_info);
+    g_regex_unref (r);
+
+    ctx->step++;
+
+    run_registration_checks_context_step(ctx);
+}
+
+
+static void
+run_registration_checks_context_step (RunRegistrationChecksContext *ctx)
+{
+    switch (ctx->step) {
+        case REGISTRATION_STEP_FIRST:
+            mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+                                      "+COPS=3,2;+COPS?",
+                                      10,
+                                      FALSE,
+                                      (GAsyncReadyCallback)registration_status_check_ready,
+                                      ctx);
+            break;
+        case REGISTRATION_STEP_SECOND:
+            g_simple_async_result_set_op_res_gpointer (ctx->result,
+                                                       NULL,
+                                                       NULL);
+            run_registration_checks_context_complete_and_free (ctx);
+            break;
+    }
+}
+
+static void
+modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self,
+                                    gboolean cs_supported,
+                                    gboolean ps_supported,
+                                    gboolean eps_supported,
+                                    GAsyncReadyCallback callback,
+                                    gpointer user_data)
+{
+    RunRegistrationChecksContext *ctx;
+
+    ctx = g_new0 (RunRegistrationChecksContext, 1);
+    ctx->self = g_object_ref (self);
+    ctx->result = g_simple_async_result_new (G_OBJECT (self),
+                                             callback,
+                                             user_data,
+                                             modem_3gpp_run_registration_checks);
+
+    ctx->step = REGISTRATION_STEP_FIRST;
+
+    run_registration_checks_context_step (ctx);
+}
+
+/*****************************************************************************/
+/* Capabilities loading (Modem interface) */
+
+static MMModemCapability
+modem_load_current_capabilities_finish (MMIfaceModem *self,
+                                        GAsyncResult *res,
+                                        GError **error)
+{
+    return (MMModemCapability)GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
+                                              G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+static void
+modem_load_current_capabilities (MMIfaceModem *self,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+    MMModemCapability cap;
+
+    /* With nm-applet, only GSM or CDMA devices are treated as compatible devices  
+     * Add the GSM_UMTS flag for nm-applet */
+    cap = MM_MODEM_CAPABILITY_LTE | MM_MODEM_CAPABILITY_GSM_UMTS;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        modem_load_current_capabilities);
+
+    g_simple_async_result_set_op_res_gpointer (result,
+                                               GUINT_TO_POINTER (cap),
+                                               NULL);
+    g_simple_async_result_complete_in_idle (result);
+    g_object_unref (result);
+}
+
+/*****************************************************************************/
+/* Check if unlock required (Modem interface) */
+
+typedef struct {
+    const gchar *result;
+    MMModemLock code;
+} CPinResult;
+
+static CPinResult unlock_results[] = {
+    /* Longer entries first so we catch the correct one with strcmp() */
+    { "READY",         MM_MODEM_LOCK_NONE           },
+    { "SIM PIN2",      MM_MODEM_LOCK_SIM_PIN2       },
+    { "SIM PUK2",      MM_MODEM_LOCK_SIM_PUK2       },
+    { "SIM PIN",       MM_MODEM_LOCK_SIM_PIN        },
+    { "SIM PUK",       MM_MODEM_LOCK_SIM_PUK        },
+    { "PH-NETSUB PIN", MM_MODEM_LOCK_PH_NETSUB_PIN  },
+    { "PH-NETSUB PUK", MM_MODEM_LOCK_PH_NETSUB_PUK  },
+    { "PH-FSIM PIN",   MM_MODEM_LOCK_PH_FSIM_PIN    },
+    { "PH-FSIM PUK",   MM_MODEM_LOCK_PH_FSIM_PUK    },
+    { "PH-CORP PIN",   MM_MODEM_LOCK_PH_CORP_PIN    },
+    { "PH-CORP PUK",   MM_MODEM_LOCK_PH_CORP_PUK    },
+    { "PH-SIM PIN",    MM_MODEM_LOCK_PH_SIM_PIN     },
+    { "PH-NET PIN",    MM_MODEM_LOCK_PH_NET_PIN     },
+    { "PH-NET PUK",    MM_MODEM_LOCK_PH_NET_PUK     },
+    { "PH-SP PIN",     MM_MODEM_LOCK_PH_SP_PIN      },
+    { "PH-SP PUK",     MM_MODEM_LOCK_PH_SP_PUK      },
+    { NULL }
+};
+
+static MMModemLock
+modem_load_unlock_required_finish (MMIfaceModem *self,
+                                   GAsyncResult *res,
+                                   GError **error)
+{
+    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+        return MM_MODEM_LOCK_UNKNOWN;
+
+    return (MMModemLock) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
+                                           G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+static void
+cpin_query_ready (MMIfaceModem *self,
+                  GAsyncResult *res,
+                  GSimpleAsyncResult *simple)
+{
+
+    MMModemLock lock = MM_MODEM_LOCK_UNKNOWN;
+    const gchar *result;
+    GError *error = NULL;
+
+    result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+    if (error) {
+        g_simple_async_result_take_error (simple, error);
+        g_simple_async_result_complete (simple);
+        g_object_unref (simple);
+        return;
+    }
+
+    if (result &&
+        strstr (result, "+CPIN:")) {
+        CPinResult *iter = &unlock_results[0];
+        const gchar *str;
+
+        str = strstr (result, "+CPIN:") + 6;
+        /* Skip possible whitespaces after '+CPIN:' and before the response */
+        while (*str == ' ')
+            str++;
+
+        if (str[0] == '"')
+            str++;
+
+        /* Translate the reply */
+        while (iter->result) {
+            if (g_str_has_prefix (str, iter->result)) {
+                lock = iter->code;
+                break;
+            }
+            iter++;
+        }
+    }
+
+    g_simple_async_result_set_op_res_gpointer (simple,
+                                               GUINT_TO_POINTER (lock),
+                                               NULL);
+    g_simple_async_result_complete (simple);
+    g_object_unref (simple);
+}
+
+static void
+modem_load_unlock_required (MMIfaceModem *self,
+                            GAsyncReadyCallback callback,
+                            gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        modem_load_unlock_required);
+
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "+CPIN?",
+                              9, /* When initialzed for the first time, the SIM related operations can take a while */
+                              FALSE,
+                              (GAsyncReadyCallback)cpin_query_ready,
+                              result);
+}
+
+/*****************************************************************************/
+/* Location capabilities loading (Location interface) */
+
+static MMModemLocationSource
+location_load_capabilities_finish (MMIfaceModemLocation *self,
+                                   GAsyncResult *res,
+                                   GError **error)
+{
+    return (MMModemLocationSource) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
+                                                     G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+static void
+location_load_capabilities (MMIfaceModemLocation *self,
+                            GAsyncReadyCallback callback,
+                            gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+    MMModemLocationSource sources;
+
+    /* By GCT modem alone, no location source available */
+    sources = MM_MODEM_LOCATION_SOURCE_NONE;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        location_load_capabilities);
+
+    g_simple_async_result_set_op_res_gpointer (result,
+                                               GUINT_TO_POINTER (sources),
+                                               NULL);
+    g_simple_async_result_complete_in_idle (result);
+    g_object_unref (result);
+}
+
+
+/*****************************************************************************/
+/* Load network timezone (Time interface) */
+
+static gboolean
+parse_cclk_query_reply (const gchar *response,
+                        gchar **iso8601,
+                        MMNetworkTimezone **tz,
+                        GError **error)
+{
+    gint year;
+    gint month;
+    gint day;
+    gint hour;
+    gint minute;
+    gint second;
+    gchar sign;
+    gint offset;
+
+    /* Example response would be something like +CCLK: 13/04/12,07:41:59+36 */
+    response = mm_strip_tag (response, "+CCLK: ");
+    if (sscanf (response,
+                "%02d/%02d/%02d,%02d:%02d:%02d%c%02d\"",
+                &year,
+                &month,
+                &day,
+                &hour,
+                &minute,
+                &second,
+                &sign,
+                &offset) == 8) {
+        /* Offset comes in 15-min intervals */
+        offset *= 15;
+        /* Apply sign to offset */
+        if (sign == '-')
+            offset *= -1;
+
+        /* If asked for it, build timezone information */
+        if (tz) {
+            *tz = mm_network_timezone_new ();
+            mm_network_timezone_set_offset (*tz, offset);
+        }
+
+        if (iso8601) {
+            /* GCT modems only report a 2-digit year, while ISO-8601 requires
+             * a 4-digit year.  Assume 2000.
+             */
+            if (year < 100)
+                year += 2000;
+
+            /* don't give tz info in the date/time string, we have another
+             * property for that */
+            *iso8601 = g_strdup_printf ("%04d/%02d/%02d %02d:%02d:%02d",
+                                        year, month, day,
+                                        hour, minute, second);
+        }
+
+        return TRUE;
+    }
+
+    g_set_error (error,
+                 MM_CORE_ERROR,
+                 MM_CORE_ERROR_FAILED,
+                 "Unknown CCLK response: %s",
+                 response);
+    return FALSE;
+}
+
+static MMNetworkTimezone *
+modem_time_load_network_timezone_finish (MMIfaceModemTime *self,
+                                         GAsyncResult *res,
+                                         GError **error)
+{
+    const gchar *response;
+    MMNetworkTimezone *tz;
+
+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL);
+    if (!response) {
+        /* We'll assume we can retry a bit later */
+        g_set_error (error,
+                     MM_CORE_ERROR,
+                     MM_CORE_ERROR_RETRY,
+                     "Retry");
+        return NULL;
+    }
+
+    return (parse_cclk_query_reply (response, NULL, &tz, error) ? tz : NULL);
+}
+
+static void
+modem_time_load_network_timezone (MMIfaceModemTime *self,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data)
+{
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "AT+CCLK?",
+                              3,
+                              FALSE,
+                              callback,
+                              user_data);
+}
+
+/*****************************************************************************/
+/* Load network time (Time interface) */
+
+static gchar *
+modem_time_load_network_time_finish (MMIfaceModemTime *self,
+                                     GAsyncResult *res,
+                                     GError **error)
+{
+    const gchar *response;
+    gchar *iso8601;
+
+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+    if (!response)
+        return NULL;
+
+    return (parse_cclk_query_reply (response, &iso8601, NULL, error) ? iso8601 : NULL);
+}
+
+static void
+modem_time_load_network_time (MMIfaceModemTime *self,
+                              GAsyncReadyCallback callback,
+                              gpointer user_data)
+{
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "AT+CCLK?",
+                              3,
+                              FALSE,
+                              callback,
+                              user_data);
+}
+
+/*****************************************************************************/
+/* Check support (Time interface) */
+
+static gboolean
+modem_time_check_support_finish (MMIfaceModemTime *self,
+                                 GAsyncResult *res,
+                                 GError **error)
+{
+    return TRUE;
+}
+
+static void
+modem_time_check_support (MMIfaceModemTime *self,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        modem_time_check_support);
+
+    g_simple_async_result_complete_in_idle (result);
+    g_object_unref (result);
+}
+
+
+/*****************************************************************************/
+/* Load supported modes (Modem interface) */
+
+static GArray *
+modem_load_supported_modes_finish (MMIfaceModem *self,
+                                   GAsyncResult *res,
+                                   GError **error)
+{
+    return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+static void
+modem_load_supported_modes (MMIfaceModem *self,
+                            GAsyncReadyCallback callback,
+                            gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+    GArray *combinations;
+    MMModemModeCombination mode;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        modem_load_supported_modes);
+
+    combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
+
+    mode.allowed = MM_MODEM_MODE_4G;
+    mode.preferred = MM_MODEM_MODE_NONE;
+    g_array_append_val (combinations, mode);
+
+
+    g_simple_async_result_set_op_res_gpointer (result, combinations, (GDestroyNotify) g_array_unref);
+    g_simple_async_result_complete_in_idle (result);
+    g_object_unref (result);
+}
+
+
+/*****************************************************************************/
+/* Load access technologies (Modem interface) */
+
+static gboolean
+load_access_technologies_finish (MMIfaceModem *self,
+                                 GAsyncResult *res,
+                                 MMModemAccessTechnology *access_technologies,
+                                 guint *mask,
+                                 GError **error)
+{
+    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+        return FALSE;
+
+    *access_technologies = (MMModemAccessTechnology) GPOINTER_TO_UINT (
+        g_simple_async_result_get_op_res_gpointer (
+            G_SIMPLE_ASYNC_RESULT (res)));
+    *mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY;
+    return TRUE;
+}
+static void
+load_access_technologies (MMIfaceModem *self,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        load_access_technologies);
+
+    g_simple_async_result_set_op_res_gpointer (
+        result,
+        GUINT_TO_POINTER (MM_MODEM_ACCESS_TECHNOLOGY_LTE),
+        NULL);
+    g_simple_async_result_complete_in_idle (result);
+    g_object_unref (result);
+}
+
+/*****************************************************************************/
+/* Load unlock retries (Modem interface) */
+
+static MMUnlockRetries *
+load_unlock_retries_finish (MMIfaceModem *self,
+                            GAsyncResult *res,
+                            GError **error)
+{
+    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+        return NULL;
+    return (MMUnlockRetries *) g_object_ref (g_simple_async_result_get_op_res_gpointer (
+                                             G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+static void
+load_unlock_retries_ready (MMBaseModem *self,
+                           GAsyncResult *res,
+                           GSimpleAsyncResult *operation_result)
+{
+    const gchar *response;
+    GError *error = NULL;
+    int pin1, pin2, puk1, puk2;
+
+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+    if (!response) {
+        mm_dbg ("Couldn't query unlock retries: '%s'", error->message);
+        g_simple_async_result_take_error (operation_result, error);
+        g_simple_async_result_complete (operation_result);
+        g_object_unref (operation_result);
+        return;
+    }
+
+    response = mm_strip_tag (response, "+CPINC:");
+    if (sscanf (response, " %d, %d, %d, %d", &pin1, &pin2, &puk1, &puk2) == 4) {
+        MMUnlockRetries *retries;
+        retries = mm_unlock_retries_new ();
+        mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, pin1);
+        mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN2, pin2);
+        mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk1);
+        mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, puk2);
+        g_simple_async_result_set_op_res_gpointer (operation_result,
+                                                   retries,
+                                                   (GDestroyNotify)g_object_unref);
+    } else {
+        g_simple_async_result_set_error (operation_result,
+                                         MM_CORE_ERROR,
+                                         MM_CORE_ERROR_FAILED,
+                                         "Invalid unlock retries response: '%s'",
+                                         response);
+    }
+    g_simple_async_result_complete (operation_result);
+    g_object_unref (operation_result);
+}
+
+static void
+load_unlock_retries (MMIfaceModem *self,
+                     GAsyncReadyCallback callback,
+                     gpointer user_data)
+{
+    mm_base_modem_at_command (
+        MM_BASE_MODEM (self),
+        "+CPINC?",
+        3,
+        FALSE,
+        (GAsyncReadyCallback)load_unlock_retries_ready,
+        g_simple_async_result_new (G_OBJECT (self),
+                                   callback,
+                                   user_data,
+                                   load_unlock_retries));
+}
+
+/*****************************************************************************/
+/* First enabling step */
+
+static void
+auto_register_in_network (MMBroadbandModem *self,
+                          GAsyncResult *res,
+                          void* ctx)
+{
+    GError *error;
+    const gchar *response;
+
+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+    if (!response)
+        mm_dbg("Failed to get Auto Register result during enabling state");
+}
+
+static gboolean
+enabling_started_finish (MMBroadbandModem *self,
+                         GAsyncResult *res,
+                         GError **error)
+{
+    mm_dbg("Auto Register during the enabling state");
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "AT+COPS=0",
+                              120,
+                              FALSE,
+                              (GAsyncReadyCallback)auto_register_in_network,
+                              NULL);
+
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+parent_enabling_started_ready (MMBroadbandModem *self,
+                               GAsyncResult *res,
+                               GSimpleAsyncResult *simple)
+{
+    GError *error = NULL;
+
+    if (!MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_gct_parent_class)->enabling_started_finish (
+            self,
+            res,
+            &error)) {
+        mm_dbg ("Couldn't start parent enabling: %s", error->message);
+        g_error_free (error);
+    }
+
+    g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+    g_simple_async_result_complete (simple);
+    g_object_unref (simple);
+}
+
+static void
+enabling_started (MMBroadbandModem *self,
+                  GAsyncReadyCallback callback,
+                  gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        enabling_started);
+    MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_gct_parent_class)->enabling_started (
+        self,
+        (GAsyncReadyCallback)parent_enabling_started_ready,
+        result);
+}
+
+/*****************************************************************************/
+
+MMBroadbandModemGct *
+mm_broadband_modem_gct_new (const gchar *device,
+                            const gchar **drivers,
+                            const gchar *plugin,
+                            guint16 vendor_id,
+                            guint16 product_id)
+{
+    return g_object_new (MM_TYPE_BROADBAND_MODEM_GCT,
+                         MM_BASE_MODEM_DEVICE, device,
+                         MM_BASE_MODEM_DRIVERS, drivers,
+                         MM_BASE_MODEM_PLUGIN, plugin,
+                         MM_BASE_MODEM_VENDOR_ID, vendor_id,
+                         MM_BASE_MODEM_PRODUCT_ID, product_id,
+                         MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, FALSE,
+                         MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, FALSE,
+                         MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, TRUE,
+                         NULL);
+}
+
+static void
+mm_broadband_modem_gct_init (MMBroadbandModemGct *self)
+{
+    mm_dbg("GCT modem manager plugin built on %s, at %s", __DATE__, __TIME__);
+}
+
+static void
+iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
+{
+    /* Other actions */
+    iface->run_registration_checks = modem_3gpp_run_registration_checks;
+    iface->run_registration_checks_finish = modem_3gpp_run_registration_checks_finish;
+    iface->setup_unsolicited_events = NULL;
+    iface->setup_unsolicited_events_finish = NULL;
+}
+
+static void
+iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface)
+{
+    /* Assume we don't have USSD support */
+    iface->check_support = NULL;
+    iface->check_support_finish = NULL;
+}
+
+static void
+iface_modem_location_init (MMIfaceModemLocation *iface)
+{
+    iface->load_capabilities = location_load_capabilities;
+    iface->load_capabilities_finish = location_load_capabilities_finish;
+}
+
+static void
+iface_modem_time_init (MMIfaceModemTime *iface)
+{
+    iface->check_support = modem_time_check_support;
+    iface->check_support_finish = modem_time_check_support_finish;
+    iface->load_network_time = modem_time_load_network_time;
+    iface->load_network_time_finish = modem_time_load_network_time_finish;
+    iface->load_network_timezone = modem_time_load_network_timezone;
+    iface->load_network_timezone_finish = modem_time_load_network_timezone_finish;
+}
+
+static void
+iface_modem_messaging_init (MMIfaceModemMessaging *iface)
+{
+    iface->check_support = NULL;
+    iface->check_support_finish = NULL;
+    iface->setup_sms_format = messaging_setup_sms_format;
+    iface->setup_sms_format_finish = messaging_setup_sms_format_finish;
+    iface->enable_unsolicited_events = messaging_enable_unsolicited_events;
+    iface->enable_unsolicited_events_finish = messaging_enable_unsolicited_events_finish;
+    iface->setup_unsolicited_events = modem_messaging_setup_unsolicited_events;
+    iface->setup_unsolicited_events_finish = modem_messaging_setup_cleanup_unsolicited_events_finish;
+}
+
+static void
+iface_modem_init (MMIfaceModem *iface)
+{
+    iface_modem_parent = g_type_interface_peek_parent (iface);
+
+    iface->create_bearer = modem_create_bearer;
+    iface->create_bearer_finish = modem_create_bearer_finish;
+    iface->load_current_capabilities = modem_load_current_capabilities;
+    iface->load_current_capabilities_finish = modem_load_current_capabilities_finish;
+    iface->load_unlock_required = modem_load_unlock_required;
+    iface->load_unlock_required_finish = modem_load_unlock_required_finish;
+
+    iface->create_sim = modem_create_sim;
+    iface->create_sim_finish = modem_create_sim_finish;
+    iface->load_supported_modes = modem_load_supported_modes;
+    iface->load_supported_modes_finish = modem_load_supported_modes_finish;
+    iface->load_access_technologies = load_access_technologies;
+    iface->load_access_technologies_finish = load_access_technologies_finish;
+    iface->load_unlock_retries = load_unlock_retries;
+    iface->load_unlock_retries_finish = load_unlock_retries_finish;
+    iface->setup_flow_control = NULL;
+    iface->setup_flow_control_finish = NULL;
+
+    iface->modem_power_down = modem_power_down;
+    iface->modem_power_down_finish = modem_power_down_finish;
+    iface->reset = modem_reset;
+    iface->reset_finish = modem_reset_finish;
+}
+
+static void
+mm_broadband_modem_gct_class_init (MMBroadbandModemGctClass *klass)
+{
+    MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
+
+    broadband_modem_class->setup_ports = setup_ports;
+    broadband_modem_class->enabling_started = enabling_started;
+    broadband_modem_class->enabling_started_finish = enabling_started_finish;
+    broadband_modem_class->enabling_modem_init = NULL;
+    broadband_modem_class->enabling_modem_init_finish = NULL;
+}
diff --git a/plugins/gct/mm-broadband-modem-gct.h b/plugins/gct/mm-broadband-modem-gct.h
new file mode 100644
index 0000000..6fe698c
--- /dev/null
+++ b/plugins/gct/mm-broadband-modem-gct.h
@@ -0,0 +1,54 @@
+/* -*- 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) 2013 GCT Semiconductor, Inc.
+ * Author: Glen Lee <glenlee at gctsemi.com>
+ */
+
+#ifndef MM_BROADBAND_MODEM_GCT_H
+#define MM_BROADBAND_MODEM_GCT_H
+
+#include "mm-broadband-modem.h"
+
+#define MM_TYPE_BROADBAND_MODEM_GCT            (mm_broadband_modem_gct_get_type ())
+#define MM_BROADBAND_MODEM_GCT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_GCT, MMBroadbandModemGct))
+#define MM_BROADBAND_MODEM_GCT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  MM_TYPE_BROADBAND_MODEM_GCT, MMBroadbandModemGctClass))
+#define MM_IS_BROADBAND_MODEM_GCT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_GCT))
+#define MM_IS_BROADBAND_MODEM_GCT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_BROADBAND_MODEM_GCT))
+#define MM_BROADBAND_MODEM_GCT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_BROADBAND_MODEM_GCT, MMBroadbandModemGctClass))
+
+typedef struct _MMBroadbandModemGct MMBroadbandModemGct;
+typedef struct _MMBroadbandModemGctClass MMBroadbandModemGctClass;
+typedef struct _MMBroadbandModemGctPrivate MMBroadbandModemGctPrivate;
+
+struct _MMBroadbandModemGct {
+    MMBroadbandModem parent;
+    MMBroadbandModemGctPrivate *priv;
+};
+
+struct _MMBroadbandModemGctClass{
+    MMBroadbandModemClass parent;
+};
+
+GType mm_broadband_modem_gct_get_type (void);
+
+MMBroadbandModemGct *mm_broadband_modem_gct_new (const gchar *device,
+                                                 const gchar **drivers,
+                                                 const gchar *plugin,
+                                                 guint16 vendor_id,
+                                                 guint16 product_id);
+
+void modem_bearer_get_info(MMBroadbandModemGct *self,
+                           guint8 *connected);
+
+
+#endif /* MM_BROADBAND_MODEM_GCT_H */
diff --git a/plugins/gct/mm-plugin-gct.c b/plugins/gct/mm-plugin-gct.c
new file mode 100644
index 0000000..99e1d84
--- /dev/null
+++ b/plugins/gct/mm-plugin-gct.c
@@ -0,0 +1,224 @@
+/* -*- 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) 2013 GCT Semiconductor, Inc.
+ * Author: Glen Lee <glenlee at gctsemi.com>
+ */
+
+#include <string.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-plugin-gct.h"
+#include "mm-private-boxed-types.h"
+#include "mm-broadband-modem-gct.h"
+#include "mm-log.h"
+
+G_DEFINE_TYPE (MMPluginGct, mm_plugin_gct, MM_TYPE_PLUGIN)
+
+int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
+int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+
+/*****************************************************************************/
+/* Custom init */
+
+typedef struct {
+    MMPortProbe *probe;
+    MMAtSerialPort *port;
+    GCancellable *cancellable;
+    GSimpleAsyncResult *result;
+    guint retries;
+} GctCustomInitContext;
+
+static void
+gct_custom_init_context_complete_and_free (GctCustomInitContext *ctx)
+{
+    g_simple_async_result_complete_in_idle (ctx->result);
+
+    if (ctx->cancellable)
+        g_object_unref (ctx->cancellable);
+    g_object_unref (ctx->port);
+    g_object_unref (ctx->probe);
+    g_object_unref (ctx->result);
+    g_slice_free (GctCustomInitContext, ctx);
+}
+
+static gboolean
+gct_custom_init_finish (MMPortProbe *probe,
+                           GAsyncResult *result,
+                           GError **error)
+{
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+}
+
+static void gct_custom_init_step (GctCustomInitContext *ctx);
+
+static void
+lteinit_ready (MMAtSerialPort *port,
+            GString *response,
+            GError *error,
+            GctCustomInitContext *ctx)
+{
+    if (error) {
+        /* If consumed all tries and the last error was a timeout, assume the
+         * port is not AT */
+        if (ctx->retries == 0 &&
+            g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
+            mm_port_probe_set_result_at (ctx->probe, FALSE);
+        }
+
+        /* Just retry... */
+        gct_custom_init_step (ctx);
+        return;
+    }
+
+    mm_port_probe_set_result_at (ctx->probe, TRUE);
+
+    g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+    gct_custom_init_context_complete_and_free (ctx);
+}
+
+static void
+gct_custom_init_step (GctCustomInitContext *ctx)
+{
+    if (g_cancellable_is_cancelled (ctx->cancellable)) {
+        g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+        gct_custom_init_context_complete_and_free (ctx);
+        return;
+    }
+
+    if (ctx->retries == 0) {
+        g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+        gct_custom_init_context_complete_and_free (ctx);
+        return;
+    }
+
+    ctx->retries--;
+    mm_at_serial_port_queue_command (
+        ctx->port,
+        "AT%LTEINIT",
+        2,
+        FALSE, /* raw */
+        ctx->cancellable,
+        (MMAtSerialResponseFn)lteinit_ready,
+        ctx);
+}
+
+static void
+gct_custom_init (MMPortProbe *probe,
+                    MMAtSerialPort *port,
+                    GCancellable *cancellable,
+                    GAsyncReadyCallback callback,
+                    gpointer user_data)
+{
+    GctCustomInitContext *ctx;
+
+    ctx = g_slice_new (GctCustomInitContext);
+    ctx->result = g_simple_async_result_new (G_OBJECT (probe),
+                                             callback,
+                                             user_data,
+                                             gct_custom_init);
+    ctx->probe = g_object_ref (probe);
+    ctx->port = g_object_ref (port);
+    ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+    ctx->retries = 2;
+
+    gct_custom_init_step (ctx);
+}
+
+/*****************************************************************************/
+
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+              const gchar *sysfs_path,
+              const gchar **drivers,
+              guint16 vendor,
+              guint16 product,
+              GList *probes,
+              GError **error)
+{
+    return MM_BASE_MODEM (mm_broadband_modem_gct_new (sysfs_path,
+                                                          drivers,
+                                                          mm_plugin_get_name (self),
+                                                          vendor,
+                                                          product));
+}
+
+static gboolean
+grab_port (MMPlugin *self,
+           MMBaseModem *modem,
+           MMPortProbe *probe,
+           GError **error)
+{
+    MMPortType ptype;
+    MMAtPortFlag pflags = MM_AT_PORT_FLAG_NONE;
+
+    ptype = mm_port_probe_get_port_type (probe);
+
+    /* Always prefer the ttyACM port as PRIMARY AT port */
+    if (ptype == MM_PORT_TYPE_AT &&
+        g_str_has_prefix (mm_port_probe_get_port_name (probe), "GCT-ATC")) {
+        pflags = MM_AT_PORT_FLAG_PRIMARY;
+    }
+
+    return mm_base_modem_grab_port (modem,
+                                    mm_port_probe_get_port_subsys (probe),
+                                    mm_port_probe_get_port_name (probe),
+                                    ptype,
+                                    pflags,
+                                    error);
+}
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+    static const gchar *subsystems[] = { "tty", "net", NULL };
+    static const mm_uint16_pair product_ids[] =  {
+        { 0x1076, 0x8000 },
+        { 0, 0 }
+    };
+    static const MMAsyncMethod custom_init = {
+        .async  = G_CALLBACK (gct_custom_init),
+        .finish = G_CALLBACK (gct_custom_init_finish),
+    };
+    return MM_PLUGIN (
+        g_object_new (MM_TYPE_PLUGIN_GCT,
+                      MM_PLUGIN_NAME,               "Gct",
+                      MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+                      MM_PLUGIN_ALLOWED_PRODUCT_IDS, product_ids,
+                      MM_PLUGIN_CUSTOM_INIT,        &custom_init,
+                      MM_PLUGIN_ALLOWED_AT,         TRUE,
+                      NULL));
+}
+
+static void
+mm_plugin_gct_init (MMPluginGct *self)
+{
+}
+
+static void
+mm_plugin_gct_class_init (MMPluginGctClass *klass)
+{
+    MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+    plugin_class->create_modem = create_modem;
+    plugin_class->grab_port = grab_port;
+}
diff --git a/plugins/gct/mm-plugin-gct.h b/plugins/gct/mm-plugin-gct.h
new file mode 100644
index 0000000..f404c3b
--- /dev/null
+++ b/plugins/gct/mm-plugin-gct.h
@@ -0,0 +1,47 @@
+/* -*- 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) 2013 GCT Semiconductor, Inc.
+ * Author: Glen Lee <glenlee at gctsemi.com>
+ */
+
+#ifndef MM_PLUGIN_GCT_H
+#define MM_PLUGIN_GCT_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_GCT            (mm_plugin_gct_get_type ())
+#define MM_PLUGIN_GCT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_GCT, MMPluginGct))
+#define MM_PLUGIN_GCT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  MM_TYPE_PLUGIN_GCT, MMPluginGctClass))
+#define MM_IS_PLUGIN_GCT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_GCT))
+#define MM_IS_PLUGIN_GCT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_PLUGIN_GCT))
+#define MM_PLUGIN_GCT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_PLUGIN_GCT, MMPluginGctClass))
+
+typedef struct {
+    MMPlugin parent;
+} MMPluginGct;
+
+typedef struct {
+    MMPluginClass parent;
+} MMPluginGctClass;
+
+GType mm_plugin_gct_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_GCT_H */
diff --git a/plugins/gct/mm-sim-gct.c b/plugins/gct/mm-sim-gct.c
new file mode 100644
index 0000000..81b5ffc
--- /dev/null
+++ b/plugins/gct/mm-sim-gct.c
@@ -0,0 +1,131 @@
+/* -*- 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) 2013 GCT Semiconductor, Inc.
+ * Author: Glen Lee <glenlee at gctsemi.com>
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+#include "mm-log.h"
+#include "mm-modem-helpers.h"
+#include "mm-base-modem-at.h"
+
+#include "mm-sim-gct.h"
+
+G_DEFINE_TYPE (MMSimGct, mm_sim_gct, MM_TYPE_SIM);
+
+
+static void
+change_pin_ready (MMBaseModem *modem,
+                  GAsyncResult *res,
+                  GSimpleAsyncResult *simple)
+{
+    GError *error = NULL;
+
+    mm_base_modem_at_command_finish (modem, res, &error);
+    if (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
+change_pin (MMSim *self,
+           const gchar *old_pin,
+           const gchar *new_pin,
+           GAsyncReadyCallback callback,
+           gpointer user_data)
+{
+    MMBaseModem *modem = NULL;
+    gchar *command;
+
+    g_object_get (self,
+                  MM_SIM_MODEM, &modem,
+                  NULL);
+
+    command = g_strdup_printf ("+CPWD=\"P2\",\"%s\",\"%s\"",
+                           old_pin,
+                           new_pin);
+    mm_base_modem_at_command (
+        MM_BASE_MODEM (modem),
+        command,
+        3,
+        FALSE,
+        (GAsyncReadyCallback)change_pin_ready,
+        g_simple_async_result_new (G_OBJECT (self),
+                                   callback,
+                                   user_data,
+                                   change_pin));
+    g_object_unref (modem);
+}
+
+
+/*****************************************************************************/
+
+MMSim *
+mm_sim_gct_new_finish (GAsyncResult *res,
+                               GError **error)
+{
+    GObject *source;
+    GObject *sim;
+
+    source = g_async_result_get_source_object (res);
+    sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error);
+    g_object_unref (source);
+
+    if (!sim)
+        return NULL;
+
+    /* Only export valid SIMs */
+    mm_sim_export (MM_SIM (sim));
+
+    return MM_SIM (sim);
+}
+
+void
+mm_sim_gct_new (MMBaseModem *modem,
+                        GCancellable *cancellable,
+                        GAsyncReadyCallback callback,
+                        gpointer user_data)
+{
+    g_async_initable_new_async (MM_TYPE_SIM_GCT,
+                                G_PRIORITY_DEFAULT,
+                                cancellable,
+                                callback,
+                                user_data,
+                                MM_SIM_MODEM, modem,
+                                NULL);
+}
+
+static void
+mm_sim_gct_init (MMSimGct *self)
+{
+}
+
+static void
+mm_sim_gct_class_init (MMSimGctClass *klass)
+{
+    MMSimClass *sim_class = MM_SIM_CLASS (klass);
+
+    sim_class->change_pin = change_pin;
+}
diff --git a/plugins/gct/mm-sim-gct.h b/plugins/gct/mm-sim-gct.h
new file mode 100644
index 0000000..6bbb428
--- /dev/null
+++ b/plugins/gct/mm-sim-gct.h
@@ -0,0 +1,52 @@
+/* -*- 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) 2013 GCT Semiconductor, Inc.
+ * Author: Glen Lee <glenlee at gctsemi.com>
+ */
+
+#ifndef MM_SIM_GCT_H
+#define MM_SIM_GCT_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "mm-sim.h"
+
+#define MM_TYPE_SIM_GCT            (mm_sim_gct_get_type ())
+#define MM_SIM_GCT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_GCT, MMSimGct))
+#define MM_SIM_GCT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  MM_TYPE_SIM_GCT, MMSimGctClass))
+#define MM_IS_SIM_GCT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_GCT))
+#define MM_IS_SIM_GCT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_SIM_GCT))
+#define MM_SIM_GCT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_SIM_GCT, MMSimGctClass))
+
+typedef struct _MMSimGct MMSimGct;
+typedef struct _MMSimGctClass MMSimGctClass;
+
+struct _MMSimGct {
+    MMSim parent;
+};
+
+struct _MMSimGctClass {
+    MMSimClass parent;
+};
+
+GType mm_sim_gct_get_type (void);
+
+void mm_sim_gct_new (MMBaseModem *modem,
+                             GCancellable *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data);
+MMSim *mm_sim_gct_new_finish (GAsyncResult *res,
+                                      GError **error);
+
+#endif /* MM_SIM_GCT_H */
-- 
1.7.10.4



More information about the ModemManager-devel mailing list