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

Dan Williams dcbw at redhat.com
Mon Aug 12 11:46:37 PDT 2013


On Thu, 2013-08-08 at 20:34 +0900, Won Kang wrote:
> Signed-off-by: Won Kang <wonkang at gctsemi.com>

A couple generic comments...

1) the nic_type/PDN stuff is very fragile, and shouldn't be done this
way.  There has to be a better way of finding this stuff out, because
you cannot rely on the kernel device name of the modem port. eg:

  const gchar *port_name = mm_port_get_device (port);

  if (sscanf (port_name, "lte%dpdn%d", &device_id, &pdn_number) == 2) {

I'll assume each ethernet device (eg lteXpdnX) can support one EPS
bearer at a time, correct?  If that's the case, then is there any way to
tell the modem at attach time which port to use?  Or does the modem use
one port for IPv4 and one for IPv6 and not tell which port it's going to
use except through %GCTNETCFG?

What the code is currently relying on is the modem firmware *and kernel*
to always  map the same data port to the same USB interface number,
which is fine.  But then what the ModemManager plugin should do is to
read the USB interface number from the kernel, and use *that* to
determine which data port is which (and cache that number on each data
port at probe time, instead of doing it each dial).

More comments later, but in any case, thanks for the patch, I think we
can work out all the issues and get it in!

Dan

> ---
>  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 */




More information about the ModemManager-devel mailing list