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