[PATCH] Plugin for Thuraya XT

Thomas Sailer sailer at sailer.dynip.lugs.ch
Sun Jan 31 06:23:48 PST 2016


The Thuraya XT (http://www.thuraya.com/thuraya-xt) is a satellite
handset that can be connected to a computer using USB and that supports
a 64kBit/s packet switched IPv4 internet access service called GmPRS.

Like most satellite modems, it more or less supports the AT command
set, just enough to make older versions of Windows happy, but chokes on
some commands, like AT+COPS=0, which causes it to loose the signal and
behave strangely. But since there's only one possible operator, the
whole operator switching can safely be omitted. Hence the need for a
separate Thuraya plugin.

Thomas


 plugins/Makefile.am                          |  14 +
 plugins/thuraya/mm-bearer-thuraya.c          | 787 +++++++++++++++++++++++++++
 plugins/thuraya/mm-bearer-thuraya.h          |  61 +++
 plugins/thuraya/mm-broadband-modem-thuraya.c | 389 +++++++++++++
 plugins/thuraya/mm-broadband-modem-thuraya.h |  50 ++
 plugins/thuraya/mm-plugin-thuraya.c          |  93 ++++
 plugins/thuraya/mm-plugin-thuraya.h          |  49 ++
 plugins/thuraya/mm-sim-thuraya.c             |  90 +++
 plugins/thuraya/mm-sim-thuraya.h             |  54 ++
 src/mm-modem-helpers.c                       |  13 +-
 10 files changed, 1598 insertions(+), 2 deletions(-)

diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 3cc5e2f..52b4de6 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -125,6 +125,7 @@ pkglib_LTLIBRARIES = \
 	libmm-plugin-nokia-icera.la \
 	libmm-plugin-cinterion.la \
 	libmm-plugin-iridium.la \
+	libmm-plugin-thuraya.la \
 	libmm-plugin-motorola.la \
 	libmm-plugin-novatel.la \
 	libmm-plugin-novatel-lte.la \
@@ -461,6 +462,19 @@ libmm_plugin_iridium_la_SOURCES = \
 libmm_plugin_iridium_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
 libmm_plugin_iridium_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
 
+# Thuraya XT
+libmm_plugin_thuraya_la_SOURCES = \
+	thuraya/mm-plugin-thuraya.c \
+	thuraya/mm-plugin-thuraya.h \
+	thuraya/mm-broadband-modem-thuraya.c \
+	thuraya/mm-broadband-modem-thuraya.h \
+	thuraya/mm-bearer-thuraya.c \
+	thuraya/mm-bearer-thuraya.h \
+	thuraya/mm-sim-thuraya.c \
+	thuraya/mm-sim-thuraya.h
+libmm_plugin_thuraya_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
+libmm_plugin_thuraya_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
 # Common Novatel modem support library
 noinst_LTLIBRARIES += libmm-utils-novatel.la
 libmm_utils_novatel_la_SOURCES = \
diff --git a/plugins/thuraya/mm-bearer-thuraya.c b/plugins/thuraya/mm-bearer-thuraya.c
new file mode 100644
index 0000000..813fc84
--- /dev/null
+++ b/plugins/thuraya/mm-bearer-thuraya.c
@@ -0,0 +1,787 @@
+/* -*- 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) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2012 Red Hat, Inc.
+ * Copyright (C) 2011 - 2012 Google, Inc.
+ * Copyright (C) 2011 - 2013 Aleksander Morgado <aleksander at gnu.org>
+ * Copyright (C) 2016 Thomas Sailer <t.sailer at alumni.ethz.ch>
+ *
+ * modelled after mm-broadband-bearer.c
+ */
+
+#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-bearer-thuraya.h"
+#include "mm-base-modem-at.h"
+#include "mm-log.h"
+
+/* Allow up to 200s to get a proper IP connection */
+#define BEARER_THURAYA_IP_TIMEOUT_DEFAULT 200
+
+G_DEFINE_TYPE (MMBearerThuraya, mm_bearer_thuraya, MM_TYPE_BASE_BEARER);
+
+struct _MMBearerThurayaPrivate {
+    /*-- Common stuff --*/
+    /* Data port used when modem is connected */
+    MMPort *port;
+};
+
+/*****************************************************************************/
+/* Detailed connect context */
+
+typedef struct {
+    MMBearerThuraya *self;
+    MMBaseModem *modem;
+    MMPortSerialAt *primary;
+    MMPortSerialAt *secondary;
+    GCancellable *cancellable;
+    GSimpleAsyncResult *result;
+
+    MMPort *data;
+    gboolean close_data_on_exit;
+} DetailedConnectContext;
+
+static MMBearerConnectResult *
+detailed_connect_finish (MMBearerThuraya *self,
+                         GAsyncResult *res,
+                         GError **error)
+{
+    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+        return NULL;
+
+    return mm_bearer_connect_result_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+static void
+detailed_connect_context_complete_and_free (DetailedConnectContext *ctx)
+{
+    g_simple_async_result_complete_in_idle (ctx->result);
+    g_object_unref (ctx->result);
+    g_object_unref (ctx->cancellable);
+    g_object_unref (ctx->primary);
+    if (ctx->secondary)
+        g_object_unref (ctx->secondary);
+    if (ctx->data) {
+        if (ctx->close_data_on_exit)
+            mm_port_serial_close (MM_PORT_SERIAL (ctx->data));
+        g_object_unref (ctx->data);
+    }
+    g_object_unref (ctx->self);
+    g_object_unref (ctx->modem);
+    g_slice_free (DetailedConnectContext, ctx);
+}
+
+static DetailedConnectContext *
+detailed_connect_context_new (MMBearerThuraya *self,
+                              MMBroadbandModem *modem,
+                              MMPortSerialAt *primary,
+                              MMPortSerialAt *secondary,
+                              GCancellable *cancellable,
+                              GAsyncReadyCallback callback,
+                              gpointer user_data)
+{
+    DetailedConnectContext *ctx;
+
+    ctx = g_slice_new0 (DetailedConnectContext);
+    ctx->self = g_object_ref (self);
+    ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
+    ctx->primary = g_object_ref (primary);
+    ctx->secondary = (secondary ? g_object_ref (secondary) : NULL);
+    ctx->result = g_simple_async_result_new (G_OBJECT (self),
+                                             callback,
+                                             user_data,
+                                             detailed_connect_context_new);
+
+    /* NOTE:
+     * We don't currently support cancelling AT commands, so we'll just check
+     * whether the operation is to be cancelled at each step. */
+    ctx->cancellable = g_object_ref (cancellable);
+    return ctx;
+}
+
+/*****************************************************************************/
+/* Generic implementations are always AT-port based */
+
+static MMPortSerialAt *
+common_get_at_data_port (MMBaseModem *modem,
+                         GError **error)
+{
+    MMPort *data;
+
+    /* Look for best data port, NULL if none available. */
+    data = mm_base_modem_peek_best_data_port (modem, MM_PORT_TYPE_AT);
+    if (!data) {
+        /* It may happen that the desired data port grabbed during probing was
+         * actually a 'net' port, which the generic logic cannot handle, so if
+         * that is the case, and we have no AT data ports specified, just
+         fallback to the primary AT port. */
+        data = (MMPort *) mm_base_modem_peek_port_primary (modem);
+    }
+
+    g_assert (MM_IS_PORT_SERIAL_AT (data));
+
+    if (!mm_port_serial_open (MM_PORT_SERIAL (data), error)) {
+        g_prefix_error (error, "Couldn't connect: cannot keep data port open.");
+        return NULL;
+    }
+
+    mm_dbg ("Connection through a plain serial AT port (%s)", mm_port_get_device (data));
+
+    return MM_PORT_SERIAL_AT (g_object_ref (data));
+}
+
+/*****************************************************************************/
+/* CONNECT Thuraya
+ *
+ * Thuraya connection procedure of a bearer involves several steps:
+ * 1) Get data port from the modem. Default implementation will have only
+ *    one single possible data port, but plugins may have more.
+ * 2) Initiate call.
+ */
+
+static void
+dial_ready (MMBaseModem *modem,
+            GAsyncResult *res,
+            DetailedConnectContext *ctx)
+{
+    GError *error = NULL;
+    MMBearerIpConfig *config;
+
+    /* DO NOT check for cancellable here. If we got here without errors, the
+     * bearer is really connected and therefore we need to reflect that in
+     * the state machine. */
+    mm_base_modem_at_command_full_finish (modem, res, &error);
+    if (error) {
+        mm_warn ("Couldn't connect: '%s'", error->message);
+        g_simple_async_result_take_error (ctx->result, error);
+        detailed_connect_context_complete_and_free (ctx);
+        return;
+    }
+
+    /* else... Yuhu! */
+
+    /* Keep port open during connection */
+    ctx->close_data_on_exit = FALSE;
+
+    /* Generic CDMA connections are done over PPP always */
+    g_assert (MM_IS_PORT_SERIAL_AT (ctx->data));
+    config = mm_bearer_ip_config_new ();
+    mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_PPP);
+
+    /* Assume only IPv4 is given */
+    g_simple_async_result_set_op_res_gpointer (
+        ctx->result,
+        mm_bearer_connect_result_new (ctx->data, config, NULL),
+        (GDestroyNotify)mm_bearer_connect_result_unref);
+    detailed_connect_context_complete_and_free (ctx);
+
+    g_object_unref (config);
+}
+
+static void
+apn_ready (MMBaseModem *modem,
+            GAsyncResult *res,
+            DetailedConnectContext *ctx)
+{
+    GError *error = NULL;
+    gchar *command;
+    const gchar *number;
+
+    mm_base_modem_at_command_full_finish (modem, res, &error);
+    if (error) {
+        mm_warn ("Couldn't connect: '%s'", error->message);
+        g_simple_async_result_take_error (ctx->result, error);
+        detailed_connect_context_complete_and_free (ctx);
+        return;
+    }
+
+    /* start dialling */
+    number = mm_bearer_properties_get_number (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
+
+    /* If a number was given when creating the bearer, use that one.
+     * Otherwise, use the default one, *99#
+     */
+    if (number)
+        command = g_strconcat ("DT", number, NULL);
+    else
+        command = g_strdup ("DT*99#");
+
+    mm_base_modem_at_command_full (ctx->modem,
+                                   MM_PORT_SERIAL_AT (ctx->data),
+                                   command,
+                                   90,
+                                   FALSE,
+                                   FALSE,
+                                   NULL,
+                                   (GAsyncReadyCallback)dial_ready,
+                                   ctx);
+    g_free (command);
+}
+
+static void
+connect_thuraya (MMBearerThuraya *self,
+                 MMBroadbandModem *modem,
+                 MMPortSerialAt *primary,
+                 MMPortSerialAt *secondary, /* unused by us */
+                 GCancellable *cancellable,
+                 GAsyncReadyCallback callback,
+                 gpointer user_data)
+{
+    DetailedConnectContext *ctx;
+    GError *error = NULL;
+
+    g_assert (primary != NULL);
+
+    ctx = detailed_connect_context_new (self,
+                                        modem,
+                                        primary,
+                                        NULL,
+                                        cancellable,
+                                        callback,
+                                        user_data);
+
+    /* Grab dial port. This gets a reference to the dial port and OPENs it.
+     * If we fail, we'll need to close it ourselves. */
+    ctx->data = (MMPort *)common_get_at_data_port (ctx->modem, &error);
+    if (!ctx->data) {
+        g_simple_async_result_take_error (ctx->result, error);
+        detailed_connect_context_complete_and_free (ctx);
+        return;
+    }
+    ctx->close_data_on_exit = TRUE;
+
+    /* need to set the APN even though the phone already has it */
+    mm_base_modem_at_command_full (ctx->modem,
+                                   MM_PORT_SERIAL_AT (ctx->data),
+                                   "+CGDCONT=1,\"IP\",\"get\"",
+                                   90,
+                                   FALSE,
+                                   FALSE,
+                                   NULL,
+                                   (GAsyncReadyCallback)apn_ready,
+                                   ctx);
+}
+
+/*****************************************************************************/
+/* CONNECT */
+
+typedef struct {
+    MMBearerThuraya *self;
+    GSimpleAsyncResult *result;
+} ConnectContext;
+
+static void
+connect_context_complete_and_free (ConnectContext *ctx)
+{
+    g_simple_async_result_complete_in_idle (ctx->result);
+    g_object_unref (ctx->result);
+    g_object_unref (ctx->self);
+    g_slice_free (ConnectContext, ctx);
+}
+
+static MMBearerConnectResult *
+connect_finish (MMBaseBearer *self,
+                GAsyncResult *res,
+                GError **error)
+{
+    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+        return NULL;
+
+    return mm_bearer_connect_result_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+static void
+connect_succeeded (ConnectContext *ctx,
+                   MMBearerConnectResult *result)
+{
+    /* Keep connected port and type of connection */
+    ctx->self->priv->port = g_object_ref (mm_bearer_connect_result_peek_data (result));
+
+    /* Port is connected; update the state */
+    mm_port_set_connected (ctx->self->priv->port, TRUE);
+
+    /* Set operation result */
+    g_simple_async_result_set_op_res_gpointer (ctx->result,
+                                               result,
+                                               (GDestroyNotify)mm_bearer_connect_result_unref);
+    connect_context_complete_and_free (ctx);
+}
+
+static void
+connect_ready (MMBearerThuraya *self,
+                    GAsyncResult *res,
+                    ConnectContext *ctx)
+{
+    MMBearerConnectResult *result;
+    GError *error = NULL;
+
+    result = detailed_connect_finish (self, res, &error);
+    if (!result) {
+        g_simple_async_result_take_error (ctx->result, error);
+        connect_context_complete_and_free (ctx);
+        return;
+    }
+
+    /* take result */
+    connect_succeeded (ctx, result);
+}
+
+static void
+connect (MMBaseBearer *self,
+         GCancellable *cancellable,
+         GAsyncReadyCallback callback,
+         gpointer user_data)
+{
+    MMBaseModem *modem = NULL;
+    MMPortSerialAt *primary;
+    ConnectContext *ctx;
+
+    /* Don't try to connect if already connected */
+    if (MM_BEARER_THURAYA (self)->priv->port) {
+        g_simple_async_report_error_in_idle (
+            G_OBJECT (self),
+            callback,
+            user_data,
+            MM_CORE_ERROR,
+            MM_CORE_ERROR_CONNECTED,
+            "Couldn't connect: this bearer is already connected");
+        return;
+    }
+
+    /* Get the owner modem object */
+    g_object_get (self,
+                  MM_BASE_BEARER_MODEM, &modem,
+                  NULL);
+    g_assert (modem != NULL);
+
+    /* We will launch the ATD call in the primary port... */
+    primary = mm_base_modem_peek_port_primary (modem);
+    if (!primary) {
+        g_simple_async_report_error_in_idle (
+            G_OBJECT (self),
+            callback,
+            user_data,
+            MM_CORE_ERROR,
+            MM_CORE_ERROR_CONNECTED,
+            "Couldn't connect: couldn't get primary port");
+        g_object_unref (modem);
+        return;
+    }
+
+    /* ...only if not already connected */
+    if (mm_port_get_connected (MM_PORT (primary))) {
+        g_simple_async_report_error_in_idle (
+            G_OBJECT (self),
+            callback,
+            user_data,
+            MM_CORE_ERROR,
+            MM_CORE_ERROR_CONNECTED,
+            "Couldn't connect: primary AT port is already connected");
+        g_object_unref (modem);
+        return;
+    }
+
+    /* In this context, we only keep the stuff we'll need later */
+    ctx = g_slice_new0 (ConnectContext);
+    ctx->self = g_object_ref (self);
+    ctx->result = g_simple_async_result_new (G_OBJECT (self),
+                                             callback,
+                                             user_data,
+                                             connect);
+
+    mm_dbg ("Launching Thuraya connection attempt");
+    connect_thuraya (
+        MM_BEARER_THURAYA (self),
+        MM_BROADBAND_MODEM (modem),
+        primary,
+        mm_base_modem_peek_port_secondary (modem),
+        cancellable,
+        (GAsyncReadyCallback) connect_ready,
+        ctx);
+    g_object_unref (modem);
+}
+
+/*****************************************************************************/
+/* Detailed disconnect context */
+
+typedef struct {
+    MMBearerThuraya *self;
+    MMBaseModem *modem;
+    MMPortSerialAt *primary;
+    MMPortSerialAt *secondary;
+    MMPort *data;
+    GSimpleAsyncResult *result;
+} DetailedDisconnectContext;
+
+static gboolean
+detailed_disconnect_finish (MMBearerThuraya *self,
+                            GAsyncResult *res,
+                            GError **error)
+{
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+detailed_disconnect_context_complete_and_free (DetailedDisconnectContext *ctx)
+{
+    g_simple_async_result_complete_in_idle (ctx->result);
+    g_object_unref (ctx->result);
+    g_object_unref (ctx->data);
+    g_object_unref (ctx->primary);
+    if (ctx->secondary)
+        g_object_unref (ctx->secondary);
+    g_object_unref (ctx->self);
+    g_object_unref (ctx->modem);
+    g_free (ctx);
+}
+
+static DetailedDisconnectContext *
+detailed_disconnect_context_new (MMBearerThuraya *self,
+                                 MMBroadbandModem *modem,
+                                 MMPortSerialAt *primary,
+                                 MMPortSerialAt *secondary,
+                                 MMPort *data,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
+{
+    DetailedDisconnectContext *ctx;
+
+    ctx = g_new0 (DetailedDisconnectContext, 1);
+    ctx->self = g_object_ref (self);
+    ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
+    ctx->primary = g_object_ref (primary);
+    ctx->secondary = (secondary ? g_object_ref (secondary) : NULL);
+    ctx->data = g_object_ref (data);
+    ctx->result = g_simple_async_result_new (G_OBJECT (self),
+                                             callback,
+                                             user_data,
+                                             detailed_disconnect_context_new);
+    return ctx;
+}
+
+/*****************************************************************************/
+/* Thuraya disconnect */
+
+static void
+data_flash_ready (MMPortSerial *data,
+                  GAsyncResult *res,
+                  DetailedDisconnectContext *ctx)
+{
+    GError *error = NULL;
+
+    mm_port_serial_flash_finish (data, res, &error);
+
+    /* We kept the serial port open during connection, now we close that open
+     * count */
+    mm_port_serial_close (data);
+
+    /* Port is disconnected; update the state */
+    mm_port_set_connected (MM_PORT (data), FALSE);
+
+    if (error) {
+        /* Ignore "NO CARRIER" response when modem disconnects and any flash
+         * failures we might encounter. Other errors are hard errors.
+         */
+        if (!g_error_matches (error,
+                              MM_CONNECTION_ERROR,
+                              MM_CONNECTION_ERROR_NO_CARRIER) &&
+            !g_error_matches (error,
+                              MM_SERIAL_ERROR,
+                              MM_SERIAL_ERROR_FLASH_FAILED)) {
+            /* Fatal */
+            g_simple_async_result_take_error (ctx->result, error);
+            detailed_disconnect_context_complete_and_free (ctx);
+            return;
+        }
+
+        mm_dbg ("Port flashing failed (not fatal): %s", error->message);
+        g_error_free (error);
+    }
+
+    g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+    detailed_disconnect_context_complete_and_free (ctx);
+}
+
+static void
+data_reopen_ready (MMPortSerial *data,
+                   GAsyncResult *res,
+                   DetailedDisconnectContext *ctx)
+{
+    GError *error = NULL;
+
+    if (!mm_port_serial_reopen_finish (data, res, &error)) {
+        /* Fatal */
+        g_simple_async_result_take_error (ctx->result, error);
+        detailed_disconnect_context_complete_and_free (ctx);
+        return;
+    }
+
+    /* Just flash the data port */
+    mm_dbg ("Flashing data port (%s)...", mm_port_get_device (MM_PORT (ctx->data)));
+    mm_port_serial_flash (MM_PORT_SERIAL (ctx->data),
+                          1000,
+                          TRUE,
+                          (GAsyncReadyCallback)data_flash_ready,
+                          ctx);
+}
+
+static void
+disconnect_thuraya (MMBearerThuraya *self,
+                    MMBroadbandModem *modem,
+                    MMPortSerialAt *primary,
+                    MMPortSerialAt *secondary,
+                    MMPort *data,
+                    GAsyncReadyCallback callback,
+                    gpointer user_data)
+{
+    DetailedDisconnectContext *ctx;
+
+    g_assert (primary != NULL);
+
+    /* Generic Thuraya plays only with SERIAL data ports */
+    g_assert (MM_IS_PORT_SERIAL (data));
+
+    ctx = detailed_disconnect_context_new (self,
+                                           modem,
+                                           primary,
+                                           secondary,
+                                           data,
+                                           callback,
+                                           user_data);
+
+    /* Fully reopen the port before flashing */
+    mm_dbg ("Reopening data port (%s)...", mm_port_get_device (MM_PORT (ctx->data)));
+    mm_port_serial_reopen (MM_PORT_SERIAL (ctx->data),
+                           1000,
+                           (GAsyncReadyCallback)data_reopen_ready,
+                           ctx);
+}
+
+/*****************************************************************************/
+/* DISCONNECT */
+
+typedef struct {
+    MMBearerThuraya *self;
+    GSimpleAsyncResult *result;
+    MMPort *data;
+} DisconnectContext;
+
+static void
+disconnect_context_complete_and_free (DisconnectContext *ctx)
+{
+    g_simple_async_result_complete_in_idle (ctx->result);
+    g_object_unref (ctx->result);
+    g_object_unref (ctx->data);
+    g_object_unref (ctx->self);
+    g_free (ctx);
+}
+
+static gboolean
+disconnect_finish (MMBaseBearer *self,
+                   GAsyncResult *res,
+                   GError **error)
+{
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+reset_bearer_connection (MMBearerThuraya *self)
+{
+    if (self->priv->port) {
+        /* Port is disconnected; update the state. Note: implementations may
+         * already have set the port as disconnected (e.g the 3GPP one) */
+        mm_port_set_connected (self->priv->port, FALSE);
+
+        /* Clear data port */
+        g_clear_object (&self->priv->port);
+    }
+}
+
+static void
+disconnect_succeeded (DisconnectContext *ctx)
+{
+    /* Cleanup all connection related data */
+    reset_bearer_connection (ctx->self);
+
+    /* Set operation result */
+    g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+    disconnect_context_complete_and_free (ctx);
+}
+
+static void
+disconnect_failed (DisconnectContext *ctx,
+                   GError *error)
+{
+    g_simple_async_result_take_error (ctx->result, error);
+    disconnect_context_complete_and_free (ctx);
+}
+
+static void
+disconnect_ready (MMBearerThuraya *self,
+                  GAsyncResult *res,
+                  DisconnectContext *ctx)
+{
+    GError *error = NULL;
+
+    if (!detailed_disconnect_finish (self,
+                                     res,
+                                     &error))
+        disconnect_failed (ctx, error);
+    else
+        disconnect_succeeded (ctx);
+}
+
+static void
+disconnect (MMBaseBearer *self,
+            GAsyncReadyCallback callback,
+            gpointer user_data)
+{
+    MMPortSerialAt *primary;
+    MMBaseModem *modem = NULL;
+    DisconnectContext *ctx;
+
+    if (!MM_BEARER_THURAYA (self)->priv->port) {
+        g_simple_async_report_error_in_idle (
+            G_OBJECT (self),
+            callback,
+            user_data,
+            MM_CORE_ERROR,
+            MM_CORE_ERROR_FAILED,
+            "Couldn't disconnect: this bearer is not connected");
+        return;
+    }
+
+    g_object_get (self,
+                  MM_BASE_BEARER_MODEM, &modem,
+                  NULL);
+    g_assert (modem != NULL);
+
+    /* We need the primary port to disconnect... */
+    primary = mm_base_modem_peek_port_primary (modem);
+    if (!primary) {
+        g_simple_async_report_error_in_idle (
+            G_OBJECT (self),
+            callback,
+            user_data,
+            MM_CORE_ERROR,
+            MM_CORE_ERROR_FAILED,
+            "Couldn't disconnect: couldn't get primary port");
+        g_object_unref (modem);
+        return;
+    }
+
+    /* In this context, we only keep the stuff we'll need later */
+    ctx = g_new0 (DisconnectContext, 1);
+    ctx->self = g_object_ref (self);
+    ctx->data = g_object_ref (MM_BEARER_THURAYA (self)->priv->port);
+    ctx->result = g_simple_async_result_new (G_OBJECT (self),
+                                             callback,
+                                             user_data,
+                                             disconnect);
+
+    disconnect_thuraya (
+        MM_BEARER_THURAYA (self),
+        MM_BROADBAND_MODEM (modem),
+        primary,
+        mm_base_modem_peek_port_secondary (modem),
+        MM_BEARER_THURAYA (self)->priv->port,
+        (GAsyncReadyCallback) disconnect_ready,
+        ctx);
+
+    g_object_unref (modem);
+}
+
+/*****************************************************************************/
+
+static void
+report_connection_status (MMBaseBearer *self,
+                          MMBearerConnectionStatus status)
+{
+    if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED)
+        /* Cleanup all connection related data */
+        reset_bearer_connection (MM_BEARER_THURAYA (self));
+
+    /* Chain up parent's report_connection_status() */
+    MM_BASE_BEARER_CLASS (mm_bearer_thuraya_parent_class)->report_connection_status (
+        self,
+        status);
+}
+
+/*****************************************************************************/
+
+MMBaseBearer *
+mm_bearer_thuraya_new (MMBroadbandModemThuraya *modem,
+                       MMBearerProperties *config)
+{
+    MMBaseBearer *bearer;
+
+    /* The Thuraya bearer inherits from MMBaseBearer (so it's not a MMBearerThuraya)
+     * and that means that the object is not async-initable, so we just use
+     * g_object_get() here */
+    bearer = g_object_new (MM_TYPE_BEARER_THURAYA,
+                           MM_BASE_BEARER_MODEM, modem,
+                           MM_BASE_BEARER_CONFIG, config,
+                           "ip-timeout", BEARER_THURAYA_IP_TIMEOUT_DEFAULT,
+                           NULL);
+
+    /* Only export valid bearers */
+    mm_base_bearer_export (bearer);
+
+    return bearer;
+}
+
+static void
+mm_bearer_thuraya_init (MMBearerThuraya *self)
+{
+    /* Initialize private data */
+    self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+                                              MM_TYPE_BEARER_THURAYA,
+                                              MMBearerThurayaPrivate);
+}
+
+static void
+dispose (GObject *object)
+{
+    MMBearerThuraya *self = MM_BEARER_THURAYA (object);
+
+    reset_bearer_connection (self);
+
+    G_OBJECT_CLASS (mm_bearer_thuraya_parent_class)->dispose (object);
+}
+
+static void
+mm_bearer_thuraya_class_init (MMBearerThurayaClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass);
+
+    g_type_class_add_private (object_class, sizeof (MMBearerThurayaPrivate));
+
+    /* Virtual methods */
+    object_class->dispose = dispose;
+
+    base_bearer_class->connect = connect;
+    base_bearer_class->connect_finish = connect_finish;
+    base_bearer_class->disconnect = disconnect;
+    base_bearer_class->disconnect_finish = disconnect_finish;
+    base_bearer_class->report_connection_status = report_connection_status;
+}
diff --git a/plugins/thuraya/mm-bearer-thuraya.h b/plugins/thuraya/mm-bearer-thuraya.h
new file mode 100644
index 0000000..6d9388c
--- /dev/null
+++ b/plugins/thuraya/mm-bearer-thuraya.h
@@ -0,0 +1,61 @@
+/* -*- 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) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2012 Red Hat, Inc.
+ * Copyright (C) 2011 - 2012 Google, Inc.
+ * Copyright (C) 2011 - 2013 Aleksander Morgado <aleksander at gnu.org>
+ * Copyright (C) 2016 Thomas Sailer <t.sailer at alumni.ethz.ch>
+ *
+ * modelled after mm-broadband-bearer.h
+ */
+
+#ifndef MM_BEARER_THURAYA_H
+#define MM_BEARER_THURAYA_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-base-bearer.h"
+#include "mm-broadband-modem-thuraya.h"
+
+#define MM_TYPE_BEARER_THURAYA            (mm_bearer_thuraya_get_type ())
+#define MM_BEARER_THURAYA(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BEARER_THURAYA, MMBearerThuraya))
+#define MM_BEARER_THURAYA_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  MM_TYPE_BEARER_THURAYA, MMBearerThurayaClass))
+#define MM_IS_BEARER_THURAYA(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BEARER_THURAYA))
+#define MM_IS_BEARER_THURAYA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_BEARER_THURAYA))
+#define MM_BEARER_THURAYA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_BEARER_THURAYA, MMBearerThurayaClass))
+
+typedef struct _MMBearerThuraya MMBearerThuraya;
+typedef struct _MMBearerThurayaClass MMBearerThurayaClass;
+typedef struct _MMBearerThurayaPrivate MMBearerThurayaPrivate;
+
+struct _MMBearerThuraya {
+    MMBaseBearer parent;
+    MMBearerThurayaPrivate *priv;
+};
+
+struct _MMBearerThurayaClass {
+    MMBaseBearerClass parent;
+};
+
+GType mm_bearer_thuraya_get_type (void);
+
+/* Thuraya bearer creation implementation.
+ * NOTE it is *not* a broadband bearer, so not async-initable */
+MMBaseBearer *mm_bearer_thuraya_new (MMBroadbandModemThuraya *modem,
+                                     MMBearerProperties *properties);
+
+#endif /* MM_BEARER_THURAYA_H */
diff --git a/plugins/thuraya/mm-broadband-modem-thuraya.c b/plugins/thuraya/mm-broadband-modem-thuraya.c
new file mode 100644
index 0000000..f5b657e
--- /dev/null
+++ b/plugins/thuraya/mm-broadband-modem-thuraya.c
@@ -0,0 +1,389 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2011 - 2012 Ammonit Measurement GmbH
+ * Author: Aleksander Morgado <aleksander at lanedo.com>
+ * Copyright (C) 2016
+ * Author: Thomas Sailer <t.sailer at alumni.ethz.ch>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "ModemManager.h"
+#include "mm-log.h"
+#include "mm-errors-types.h"
+#include "mm-base-modem-at.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-messaging.h"
+#include "mm-broadband-modem-thuraya.h"
+#include "mm-sim-thuraya.h"
+#include "mm-bearer-thuraya.h"
+#include "mm-modem-helpers.h"
+
+static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
+static void iface_modem_messaging_init (MMIfaceModemMessaging *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemThuraya, mm_broadband_modem_thuraya, 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_MESSAGING, iface_modem_messaging_init));
+
+/*****************************************************************************/
+/* Operator Code and Name loading (3GPP interface) */
+
+static gchar *
+load_operator_code_finish (MMIfaceModem3gpp *self,
+                           GAsyncResult *res,
+                           GError **error)
+{
+    /* Only "90103" operator code is assumed */
+    return g_strdup ("90106");
+}
+
+static gchar *
+load_operator_name_finish (MMIfaceModem3gpp *self,
+                           GAsyncResult *res,
+                           GError **error)
+{
+    /* Only "THURAYA" operator name is assumed */
+    return g_strdup ("THURAYA");
+}
+
+static void
+load_operator_name_or_code (MMIfaceModem3gpp *self,
+                            GAsyncReadyCallback callback,
+                            gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        load_operator_name_or_code);
+    g_simple_async_result_set_op_res_gboolean (result, TRUE);
+    g_simple_async_result_complete_in_idle (result);
+    g_object_unref (result);
+}
+
+/*****************************************************************************/
+/* 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 <bm> can only be 0,
+     *  and <ds> can only be either 0 or 1
+     *
+     * Note: Modem may return +CMS ERROR:322, which indicates Memory Full,
+     * not a big deal
+     */
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "+CNMI=2,1,0,0,1",
+                              3,
+                              FALSE,
+                              callback,
+                              user_data);
+}
+
+/*****************************************************************************/
+/* Flow control (Modem interface) */
+
+static gboolean
+setup_flow_control_finish (MMIfaceModem *self,
+                           GAsyncResult *res,
+                           GError **error)
+{
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+setup_flow_control_ready (MMBroadbandModemThuraya *self,
+                          GAsyncResult *res,
+                          GSimpleAsyncResult *operation_result)
+{
+    GError *error = NULL;
+
+    if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
+        /* Let the error be critical. We DO need RTS/CTS in order to have
+         * proper modem disabling. */
+        g_simple_async_result_take_error (operation_result, error);
+    else
+        g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
+
+    g_simple_async_result_complete (operation_result);
+    g_object_unref (operation_result);
+}
+
+static void
+setup_flow_control (MMIfaceModem *self,
+                    GAsyncReadyCallback callback,
+                    gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        setup_flow_control);
+
+    /* Enable RTS/CTS flow control.
+     * Other available values:
+     *   AT&K0: Disable flow control
+     *   AT&K3: RTS/CTS
+     *   AT&K4: XOFF/XON
+     *   AT&K6: Both RTS/CTS and XOFF/XON
+     */
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "&K3",
+                              3,
+                              FALSE,
+                              (GAsyncReadyCallback)setup_flow_control_ready,
+                              result);
+}
+
+/*****************************************************************************/
+/* Load supported modes (Modem inteface) */
+
+static GArray *
+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
+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,
+                                        load_supported_modes);
+
+    /* Build list of combinations */
+    combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
+
+    /* Report any, Thuraya connections are packet-switched */
+    mode.allowed = MM_MODEM_MODE_ANY;
+    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);
+}
+
+/*****************************************************************************/
+/* Create SIM (Modem inteface) */
+
+static MMBaseSim *
+create_sim_finish (MMIfaceModem *self,
+                   GAsyncResult *res,
+                   GError **error)
+{
+    return mm_sim_thuraya_new_finish (res, error);
+}
+
+static void
+create_sim (MMIfaceModem *self,
+            GAsyncReadyCallback callback,
+            gpointer user_data)
+{
+    /* New Thuraya SIM */
+    mm_sim_thuraya_new (MM_BASE_MODEM (self),
+                        NULL, /* cancellable */
+                        callback,
+                        user_data);
+}
+
+/*****************************************************************************/
+/* Create Bearer (Modem interface) */
+
+static MMBaseBearer *
+create_bearer_finish (MMIfaceModem *self,
+                      GAsyncResult *res,
+                      GError **error)
+{
+    MMBaseBearer *bearer;
+
+    bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+    mm_dbg ("New Thuraya bearer created at DBus path '%s'", mm_base_bearer_get_path (bearer));
+
+    return g_object_ref (bearer);
+}
+
+static void
+create_bearer (MMIfaceModem *self,
+               MMBearerProperties *properties,
+               GAsyncReadyCallback callback,
+               gpointer user_data)
+{
+    MMBaseBearer *bearer;
+    GSimpleAsyncResult *result;
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        create_bearer);
+    mm_dbg ("Creating Thuraya bearer...");
+    bearer = mm_bearer_thuraya_new (MM_BROADBAND_MODEM_THURAYA (self),
+                                    properties);
+    g_simple_async_result_set_op_res_gpointer (result,
+                                               bearer,
+                                               (GDestroyNotify)g_object_unref);
+    g_simple_async_result_complete_in_idle (result);
+    g_object_unref (result);
+}
+
+/*****************************************************************************/
+
+static const gchar *primary_init_sequence[] = {
+    /* Disable echo */
+    "E0",
+    /* Get word responses */
+    "V1",
+    /* Extended numeric codes */
+    "+CMEE=1",
+    NULL
+};
+
+static void
+setup_ports (MMBroadbandModem *self)
+{
+    MMPortSerialAt *primary;
+
+    /* Call parent's setup ports first always */
+    MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_thuraya_parent_class)->setup_ports (self);
+
+    /* Set 9600 baudrate by default in the AT port */
+    mm_dbg ("Baudrate will be set to 9600 bps...");
+    primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+    if (!primary)
+        return;
+
+    g_object_set (G_OBJECT (primary),
+                  MM_PORT_SERIAL_BAUD, 9600,
+                  MM_PORT_SERIAL_AT_INIT_SEQUENCE, primary_init_sequence,
+                  NULL);
+}
+
+/*****************************************************************************/
+
+MMBroadbandModemThuraya *
+mm_broadband_modem_thuraya_new (const gchar *device,
+                                const gchar **drivers,
+                                const gchar *plugin,
+                                guint16 vendor_id,
+                                guint16 product_id)
+{
+    return g_object_new (MM_TYPE_BROADBAND_MODEM_THURAYA,
+                         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,
+                         /* Allow only up to 3 consecutive timeouts in the serial port */
+                         MM_BASE_MODEM_MAX_TIMEOUTS, 3,
+                         /* Only PS network is supported by the Thuraya modem */
+                         MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, FALSE,
+                         NULL);
+}
+
+static void
+mm_broadband_modem_thuraya_init (MMBroadbandModemThuraya *self)
+{
+}
+
+static void
+iface_modem_init (MMIfaceModem *iface)
+{
+    /* Create Thuraya-specific SIM and bearer*/
+    iface->create_sim = create_sim;
+    iface->create_sim_finish = create_sim_finish;
+    iface->create_bearer = create_bearer;
+    iface->create_bearer_finish = create_bearer_finish;
+
+    /* RTS/CTS flow control */
+    iface->setup_flow_control = setup_flow_control;
+    iface->setup_flow_control_finish = setup_flow_control_finish;
+
+    /* No need to power-up/power-down the modem */
+    iface->load_power_state = NULL;
+    iface->load_power_state_finish = NULL;
+    iface->modem_power_up = NULL;
+    iface->modem_power_up_finish = NULL;
+    iface->modem_power_down = NULL;
+    iface->modem_power_down_finish = NULL;
+
+    /* Supported modes cannot be queried */
+    iface->load_supported_modes = load_supported_modes;
+    iface->load_supported_modes_finish = load_supported_modes_finish;
+}
+
+static void
+iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
+{
+    /* Fixed operator code and name to be reported */
+    iface->load_operator_name = load_operator_name_or_code;
+    iface->load_operator_name_finish = load_operator_name_finish;
+    iface->load_operator_code = load_operator_name_or_code;
+    iface->load_operator_code_finish = load_operator_code_finish;
+
+    /* Don't try to scan networks with AT+COPS=?.
+     * The Thuraya XT does not seem to properly support AT+COPS=?.
+     * When issuing this command, it seems to get sufficiently confused
+     * to drop the signal. Furthermore, it is useless anyway as there is only
+     * one network supported, Thuraya.
+     */
+    iface->scan_networks = NULL;
+    iface->scan_networks_finish = NULL;
+}
+
+static void
+iface_modem_messaging_init (MMIfaceModemMessaging *iface)
+{
+    iface->enable_unsolicited_events = messaging_enable_unsolicited_events;
+    iface->enable_unsolicited_events_finish = messaging_enable_unsolicited_events_finish;
+}
+
+static void
+mm_broadband_modem_thuraya_class_init (MMBroadbandModemThurayaClass *klass)
+{
+    MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
+
+    broadband_modem_class->setup_ports = setup_ports;
+}
diff --git a/plugins/thuraya/mm-broadband-modem-thuraya.h b/plugins/thuraya/mm-broadband-modem-thuraya.h
new file mode 100644
index 0000000..42df9b9
--- /dev/null
+++ b/plugins/thuraya/mm-broadband-modem-thuraya.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2011 Red Hat, Inc.
+ * Copyright (C) 2011 Google Inc.
+ * Copyright (C) 2016 Thomas Sailer <t.sailer at alumni.ethz.ch>
+ */
+
+#ifndef MM_BROADBAND_MODEM_THURAYA_H
+#define MM_BROADBAND_MODEM_THURAYA_H
+
+#include "mm-broadband-modem.h"
+
+#define MM_TYPE_BROADBAND_MODEM_THURAYA            (mm_broadband_modem_thuraya_get_type ())
+#define MM_BROADBAND_MODEM_THURAYA(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_THURAYA, MMBroadbandModemThuraya))
+#define MM_BROADBAND_MODEM_THURAYA_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  MM_TYPE_BROADBAND_MODEM_THURAYA, MMBroadbandModemThurayaClass))
+#define MM_IS_BROADBAND_MODEM_THURAYA(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_THURAYA))
+#define MM_IS_BROADBAND_MODEM_THURAYA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_BROADBAND_MODEM_THURAYA))
+#define MM_BROADBAND_MODEM_THURAYA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_BROADBAND_MODEM_THURAYA, MMBroadbandModemThurayaClass))
+
+typedef struct _MMBroadbandModemThuraya MMBroadbandModemThuraya;
+typedef struct _MMBroadbandModemThurayaClass MMBroadbandModemThurayaClass;
+
+struct _MMBroadbandModemThuraya {
+    MMBroadbandModem parent;
+};
+
+struct _MMBroadbandModemThurayaClass{
+    MMBroadbandModemClass parent;
+};
+
+GType mm_broadband_modem_thuraya_get_type (void);
+
+MMBroadbandModemThuraya *mm_broadband_modem_thuraya_new (const gchar *device,
+                                                         const gchar **drivers,
+                                                         const gchar *plugin,
+                                                         guint16 vendor_id,
+                                                         guint16 product_id);
+
+#endif /* MM_BROADBAND_MODEM_THURAYA_H */
diff --git a/plugins/thuraya/mm-plugin-thuraya.c b/plugins/thuraya/mm-plugin-thuraya.c
new file mode 100644
index 0000000..3b5ca46
--- /dev/null
+++ b/plugins/thuraya/mm-plugin-thuraya.c
@@ -0,0 +1,93 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2011 - 2012 Ammonit Measurement GmbH
+ * Author: Aleksander Morgado <aleksander at lanedo.com>
+ * Copyright (C) 2016
+ * Author: Thomas Sailer <t.sailer at alumni.ethz.ch>
+ */
+
+#include <string.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-plugin-thuraya.h"
+#include "mm-broadband-modem.h"
+#include "mm-broadband-modem-thuraya.h"
+#include "mm-private-boxed-types.h"
+#include "mm-log.h"
+
+G_DEFINE_TYPE (MMPluginThuraya, mm_plugin_thuraya, MM_TYPE_PLUGIN)
+
+int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
+int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+
+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_thuraya_new (sysfs_path,
+                                                          drivers,
+                                                          mm_plugin_get_name (self),
+                                                          vendor,
+                                                          product));
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+    static const gchar *subsystems[] = { "tty", NULL };
+    static const guint16 vendor_ids[] = { 0x1a26, 0 };
+    static const guint16 product_ids[] = { 0x09cf, 0 };
+    static const gchar *vendor_strings[] = { "manufacturer apsi", NULL };
+    static const mm_str_pair product_strings[] = {{"manufacturer apsi", "thuraya xt" },
+                                                  { NULL, NULL }};
+
+    return MM_PLUGIN (
+        g_object_new (MM_TYPE_PLUGIN_THURAYA,
+                      MM_PLUGIN_NAME,                    "Thuraya",
+                      MM_PLUGIN_ALLOWED_SUBSYSTEMS,      subsystems,
+                      MM_PLUGIN_ALLOWED_VENDOR_STRINGS,  vendor_strings,
+                      MM_PLUGIN_ALLOWED_PRODUCT_STRINGS, product_strings,
+                      MM_PLUGIN_ALLOWED_VENDOR_IDS,      vendor_ids,
+                      MM_PLUGIN_ALLOWED_PRODUCT_IDS,     product_ids,
+                      MM_PLUGIN_ALLOWED_AT,              TRUE,
+                      NULL));
+}
+
+static void
+mm_plugin_thuraya_init (MMPluginThuraya *self)
+{
+}
+
+static void
+mm_plugin_thuraya_class_init (MMPluginThurayaClass *klass)
+{
+    MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+    plugin_class->create_modem = create_modem;
+}
diff --git a/plugins/thuraya/mm-plugin-thuraya.h b/plugins/thuraya/mm-plugin-thuraya.h
new file mode 100644
index 0000000..d6ca2cd
--- /dev/null
+++ b/plugins/thuraya/mm-plugin-thuraya.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2011 - 2012 Ammonit Measurement GmbH
+ * Author: Aleksander Morgado <aleksander at lanedo.com>
+ * Copyright (C) 2016
+ * Author: Thomas Sailer <t.sailer at alumni.ethz.ch>
+ */
+
+#ifndef MM_PLUGIN_THURAYA_H
+#define MM_PLUGIN_THURAYA_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_THURAYA            (mm_plugin_thuraya_get_type ())
+#define MM_PLUGIN_THURAYA(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_THURAYA, MMPluginThuraya))
+#define MM_PLUGIN_THURAYA_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  MM_TYPE_PLUGIN_THURAYA, MMPluginThurayaClass))
+#define MM_IS_PLUGIN_THURAYA(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_THURAYA))
+#define MM_IS_PLUGIN_THURAYA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_PLUGIN_THURAYA))
+#define MM_PLUGIN_THURAYA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_PLUGIN_THURAYA, MMPluginThurayaClass))
+
+typedef struct {
+    MMPlugin parent;
+} MMPluginThuraya;
+
+typedef struct {
+    MMPluginClass parent;
+} MMPluginThurayaClass;
+
+GType mm_plugin_thuraya_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_THURAYA_H */
diff --git a/plugins/thuraya/mm-sim-thuraya.c b/plugins/thuraya/mm-sim-thuraya.c
new file mode 100644
index 0000000..4ed97d3
--- /dev/null
+++ b/plugins/thuraya/mm-sim-thuraya.c
@@ -0,0 +1,90 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2011 - 2012 Ammonit Measurement GmbH.
+ * Author: Aleksander Morgado <aleksander at lanedo.com>
+ * Copyright (C) 2016
+ * Author: Thomas Sailer <t.sailer at alumni.ethz.ch>
+ */
+
+#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-sim-thuraya.h"
+
+G_DEFINE_TYPE (MMSimThuraya, mm_sim_thuraya, MM_TYPE_BASE_SIM)
+
+/*****************************************************************************/
+
+MMBaseSim *
+mm_sim_thuraya_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_base_sim_export (MM_BASE_SIM (sim));
+
+    return MM_BASE_SIM (sim);
+}
+
+void
+mm_sim_thuraya_new (MMBaseModem *modem,
+                    GCancellable *cancellable,
+                    GAsyncReadyCallback callback,
+                    gpointer user_data)
+{
+    g_async_initable_new_async (MM_TYPE_SIM_THURAYA,
+                                G_PRIORITY_DEFAULT,
+                                cancellable,
+                                callback,
+                                user_data,
+                                MM_BASE_SIM_MODEM, modem,
+                                NULL);
+}
+
+static void
+mm_sim_thuraya_init (MMSimThuraya *self)
+{
+}
+
+static void
+mm_sim_thuraya_class_init (MMSimThurayaClass *klass)
+{
+    MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass);
+
+    /* Skip querying the SIM card info, not supported by Thuraya modems */
+    base_sim_class->load_sim_identifier = NULL;
+    base_sim_class->load_sim_identifier_finish = NULL;
+    base_sim_class->load_imsi = NULL;
+    base_sim_class->load_imsi_finish = NULL;
+    base_sim_class->load_operator_identifier = NULL;
+    base_sim_class->load_operator_identifier_finish = NULL;
+    base_sim_class->load_operator_name = NULL;
+    base_sim_class->load_operator_name_finish = NULL;
+}
diff --git a/plugins/thuraya/mm-sim-thuraya.h b/plugins/thuraya/mm-sim-thuraya.h
new file mode 100644
index 0000000..38e3237
--- /dev/null
+++ b/plugins/thuraya/mm-sim-thuraya.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) 2011 - 2012 Ammonit Measurement GmbH.
+ * Author: Aleksander Morgado <aleksander at lanedo.com>
+ * Copyright (C) 2016
+ * Author: Thomas Sailer <t.sailer at alumni.ethz.ch>
+ */
+
+#ifndef MM_SIM_THURAYA_H
+#define MM_SIM_THURAYA_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "mm-base-sim.h"
+
+#define MM_TYPE_SIM_THURAYA            (mm_sim_thuraya_get_type ())
+#define MM_SIM_THURAYA(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_THURAYA, MMSimThuraya))
+#define MM_SIM_THURAYA_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  MM_TYPE_SIM_THURAYA, MMSimThurayaClass))
+#define MM_IS_SIM_THURAYA(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_THURAYA))
+#define MM_IS_SIM_THURAYA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_SIM_THURAYA))
+#define MM_SIM_THURAYA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_SIM_THURAYA, MMSimThurayaClass))
+
+typedef struct _MMSimThuraya MMSimThuraya;
+typedef struct _MMSimThurayaClass MMSimThurayaClass;
+
+struct _MMSimThuraya {
+    MMBaseSim parent;
+};
+
+struct _MMSimThurayaClass {
+    MMBaseSimClass parent;
+};
+
+GType mm_sim_thuraya_get_type (void);
+
+void       mm_sim_thuraya_new        (MMBaseModem *modem,
+                                      GCancellable *cancellable,
+                                      GAsyncReadyCallback callback,
+                                      gpointer user_data);
+MMBaseSim *mm_sim_thuraya_new_finish (GAsyncResult  *res,
+                                      GError       **error);
+
+#endif /* MM_SIM_THURAYA_H */
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index 17ad8d2..0835468 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -434,6 +434,7 @@ mm_voice_clip_regex_get (void)
 
 /* +CREG: <stat>,<lac>,<ci>           (GSM 07.07 CREG=2 unsolicited) */
 #define CREG3 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)"
+#define CREG11 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*(\"[^\"\\s]*\")\\s*,\\s*(\"[^\"\\s]*\")"
 
 /* +CREG: <n>,<stat>,<lac>,<ci>       (GSM 07.07 solicited and some CREG=2 unsolicited) */
 #define CREG4 "\\+(CREG|CGREG|CEREG):\\s*([0-9]),\\s*([0-9])\\s*,\\s*([^,]*)\\s*,\\s*([^,\\s]*)"
@@ -489,6 +490,14 @@ mm_3gpp_creg_regex_get (gboolean solicited)
     g_assert (regex);
     g_ptr_array_add (array, regex);
 
+    /* #11 */
+    if (solicited)
+        regex = g_regex_new (CREG11 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+    else
+        regex = g_regex_new ("\\r\\n" CREG11 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+    g_assert (regex);
+    g_ptr_array_add (array, regex);
+
     /* #4 */
     if (solicited)
         regex = g_regex_new (CREG4 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
@@ -867,7 +876,7 @@ mm_3gpp_parse_cgdcont_test_response (const gchar *response,
         return NULL;
     }
 
-    r = g_regex_new ("\\+CGDCONT:\\s*\\((\\d+)-?(\\d+)?\\),\\(?\"(\\S+)\"",
+    r = g_regex_new ("\\+CGDCONT:\\s*\\(\\s*(\\d+)\\s*-?\\s*(\\d+)?\\s*\\)\\s*,\\s*\\(?\"(\\S+)\"",
                      G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
                      0, &inner_error);
     g_assert (r != NULL);
@@ -955,7 +964,7 @@ mm_3gpp_parse_cgdcont_read_response (const gchar *reply,
         return NULL;
 
     list = NULL;
-    r = g_regex_new ("\\+CGDCONT:\\s*(\\d+)\\s*,([^,\\)]*),([^,\\)]*),([^,\\)]*)",
+    r = g_regex_new ("\\+CGDCONT:\\s*(\\d+)\\s*,([^, \\)]*)\\s*,([^, \\)]*)\\s*,([^, \\)]*)",
                      G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
                      0, &inner_error);
     if (r) {


More information about the ModemManager-devel mailing list