<div dir="ltr">Hi,<div><br><div>This is my first open source commit! The patch is mainly to support the PLS8 modems via a SWWAN connection. Please give it a good look over and give me feedback on what needs to be changed. It's my first time using glib so please call me out on stupid things I may have done :). This was branched off 59f57befa4be81b562f9adfbac39fbe19d88c111. Patch is below:<br></div><div><br></div><div><div>From 281c7c38d1edbf85d2a318b7a5eb7bf8d56e2269 Mon Sep 17 00:00:00 2001</div><div>From: Matthew Stanger <<a href="mailto:matthew_stanger@trimble.com">matthew_stanger@trimble.com</a>></div><div>Date: Fri, 26 Aug 2016 16:35:49 -0600</div><div>Subject: [PATCH] Features:  Added SWWAN support in Cinterion plugin.  Updated</div><div> GPS support for Cinterion modems that use new SGPSC commands.  Added LTE</div><div> status for Cinterion plugin.  Made general Cinterion plugin improvements to</div><div> better support PLS8-X & PLS8-E modems.</div><div><br></div><div>Known issues:</div><div> SWWAN connection not tied into mmcli status updates.</div><div> Unknown logic flow for APN's which require User & Password.</div><div> No support for IP version.</div><div> Does not pull IP address from SWWAN into mmcli.</div><div> Does not auto preform DHCP for SWWAN connection.</div><div> Dual pdp context connections not yet supported.</div><div> Simple connect/disconnect does not clean up bearer.</div><div>---</div><div> plugins/Makefile.am                                |    2 +</div><div> plugins/cinterion/77-mm-cinterion-port-types.rules |    2 +-</div><div> plugins/cinterion/mm-broadband-bearer-cinterion.c  | 1126 ++++++++++++++++++++</div><div> plugins/cinterion/mm-broadband-bearer-cinterion.h  |   56 +</div><div> plugins/cinterion/mm-broadband-modem-cinterion.c   |  281 ++++-</div><div> plugins/cinterion/mm-broadband-modem-cinterion.h   |    3 +</div><div> plugins/cinterion/mm-common-cinterion.c            |  402 ++++++-</div><div> plugins/cinterion/mm-modem-helpers-cinterion.c     |  188 ++++</div><div> plugins/cinterion/mm-modem-helpers-cinterion.h     |   17 +</div><div> plugins/cinterion/mm-plugin-cinterion.c            |  141 ++-</div><div> src/mm-base-modem.c                                |   20 +</div><div> src/mm-base-modem.h                                |    1 +</div><div> 12 files changed, 2183 insertions(+), 56 deletions(-)</div><div> create mode 100644 plugins/cinterion/mm-broadband-bearer-cinterion.c</div><div> create mode 100644 plugins/cinterion/mm-broadband-bearer-cinterion.h</div><div><br></div><div>diff --git a/plugins/Makefile.am b/plugins/Makefile.am</div><div>index 018b696..744fd18 100644</div><div>--- a/plugins/Makefile.am</div><div>+++ b/plugins/Makefile.am</div><div>@@ -565,6 +565,8 @@ libmm_plugin_cinterion_la_SOURCES = \</div><div> <span class="" style="white-space:pre">   </span>cinterion/mm-common-cinterion.h \</div><div> <span class="" style="white-space:pre">        </span>cinterion/mm-broadband-modem-cinterion.c \</div><div> <span class="" style="white-space:pre">       </span>cinterion/mm-broadband-modem-cinterion.h \</div><div>+<span class="" style="white-space:pre">        </span>cinterion/mm-broadband-bearer-cinterion.c \</div><div>+<span class="" style="white-space:pre">       </span>cinterion/mm-broadband-bearer-cinterion.h \</div><div> <span class="" style="white-space:pre">      </span>$(NULL)</div><div> if WITH_QMI</div><div> libmm_plugin_cinterion_la_SOURCES += \</div><div>diff --git a/plugins/cinterion/77-mm-cinterion-port-types.rules b/plugins/cinterion/77-mm-cinterion-port-types.rules</div><div>index 09de742..ee386f0 100644</div><div>--- a/plugins/cinterion/77-mm-cinterion-port-types.rules</div><div>+++ b/plugins/cinterion/77-mm-cinterion-port-types.rules</div><div>@@ -8,4 +8,4 @@ SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInte</div><div><br></div><div> ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0053", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_CINTERION_PORT_TYPE_GPS}="1"</div><div><br></div><div>-LABEL="mm_cinterion_port_types_end"</div><div>+LABEL="mm_cinterion_port_types_end"</div><div>\ No newline at end of file</div><div>diff --git a/plugins/cinterion/mm-broadband-bearer-cinterion.c b/plugins/cinterion/mm-broadband-bearer-cinterion.c</div><div>new file mode 100644</div><div>index 0000000..eb73cf5</div><div>--- /dev/null</div><div>+++ b/plugins/cinterion/mm-broadband-bearer-cinterion.c</div><div>@@ -0,0 +1,1126 @@</div><div>+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */</div><div>+/*</div><div>+ * This program is free software; you can redistribute it and/or modify</div><div>+ * it under the terms of the GNU General Public License as published by</div><div>+ * the Free Software Foundation; either version 2 of the License, or</div><div>+ * (at your option) any later version.</div><div>+ *</div><div>+ * This program is distributed in the hope that it will be useful,</div><div>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</div><div>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</div><div>+ * GNU General Public License for more details:</div><div>+ *</div><div>+ * Copyright (C) 2016 Trimble Navigation Limited</div><div>+ * Author: Matthew Stanger <<a href="mailto:matthew_stanger@trimble.com">matthew_stanger@trimble.com</a>></div><div>+ */</div><div>+</div><div>+#include <config.h></div><div>+#include <stdio.h></div><div>+#include <stdlib.h></div><div>+#include <unistd.h></div><div>+#include <string.h></div><div>+#include <ctype.h></div><div>+#include <arpa/inet.h></div><div>+#include <ModemManager.h></div><div>+#include "mm-base-modem-at.h"</div><div>+#include "mm-broadband-bearer-cinterion.h"</div><div>+#include "mm-log.h"</div><div>+#include "mm-modem-helpers.h"</div><div>+#include "mm-modem-helpers-cinterion.h"</div><div>+#include "mm-daemon-enums-types.h"</div><div>+</div><div>+G_DEFINE_TYPE (MMBroadbandBearerCinterion, mm_broadband_bearer_cinterion, MM_TYPE_BROADBAND_BEARER);</div><div>+</div><div>+/*****************************************************************************/</div><div>+/* Common enums and structs */</div><div>+</div><div>+typedef enum {</div><div>+    MM_BEARER_CINTERION_AUTH_UNKNOWN   = -1,</div><div>+    MM_BEARER_CINTERION_AUTH_NONE      =  0,</div><div>+    MM_BEARER_CINTERION_AUTH_PAP       =  1,</div><div>+    MM_BEARER_CINTERION_AUTH_CHAP      =  2,</div><div>+    MM_BEARER_CINTERION_AUTH_MSCHAPV2  =  3,</div><div>+} MMBearerCinterionAuthPref;</div><div>+</div><div>+typedef enum {</div><div>+    CONNECTION_UNKNOWN = 0,</div><div>+    CONNECTION_ACTIVE,</div><div>+    CONNECTION_INACTIVE,</div><div>+} SWWAN_USB_CONNECTION_STATE;</div><div>+</div><div>+typedef enum {</div><div>+    CONNECT_3GPP_CONTEXT_STEP_INIT = 0,</div><div>+    CONNECT_3GPP_CONTEXT_STEP_VERIFY_SWWAN,</div><div>+    CONNECT_3GPP_CONTEXT_STEP_BEARER_PROPERTIES,</div><div>+    CONNECT_3GPP_CONTEXT_STEP_SET_SWWAN,</div><div>+    CONNECT_3GPP_CONTEXT_STEP_CONNECTION_STATUS,</div><div>+    CONNECT_3GPP_CONTEXT_STEP_DONE,</div><div>+} Connect3gppContextStep;</div><div>+</div><div>+typedef enum {</div><div>+    DISCONNECT_3GPP_CONTEXT_STEP_INIT = 0,</div><div>+    DISCONNECT_3GPP_CONTEXT_STEP_SWWAN_DETACH,</div><div>+    DISCONNECT_3GPP_CONTEXT_STEP_CONNECTION_STATUS,</div><div>+    DISCONNECT_3GPP_CONTEXT_STEP_DONE</div><div>+} Disconnect3gppContextStep;</div><div>+</div><div>+typedef struct {</div><div>+    MMBroadbandBearerCinterion *self;</div><div>+    MMBaseModem *modem;</div><div>+    MMPortSerialAt *primary;</div><div>+    MMPort *data;</div><div>+    Connect3gppContextStep connect;</div><div>+    Disconnect3gppContextStep disconnect;</div><div>+    SWWAN_USB_CONNECTION_STATE usb0_state;</div><div>+    SWWAN_USB_CONNECTION_STATE usb1_state;</div><div>+    MMBearerIpConfig *ipv4_config;</div><div>+    GCancellable *cancellable;</div><div>+    GSimpleAsyncResult *result;</div><div>+} Control3gppContext;</div><div>+</div><div>+struct _MMBroadbandBearerCinterionPrivate {</div><div>+    gpointer connect_pending;</div><div>+    gpointer disconnect_pending;</div><div>+    guint network_disconnect_pending_id;/* Tag for the post task for network-initiated disconnect */</div><div>+    const gchar *bearer_interface;</div><div>+    const gchar *pdp_cid;</div><div>+    guint retry_count;</div><div>+    guint swwan_read_write;</div><div>+};</div><div>+</div><div>+/*****************************************************************************/</div><div>+/* Common 3GPP Function Declarations */</div><div>+static void connect_3gpp_context_step (Control3gppContext *ctx);</div><div>+static void disconnect_3gpp_context_step (Control3gppContext *ctx);</div><div>+static void connect_3gpp_context_complete_and_free (Control3gppContext *ctx);</div><div>+static void disconnect_3gpp_context_complete_and_free (Control3gppContext *ctx);</div><div>+</div><div>+/*****************************************************************************/</div><div>+/* Common 3GPP */</div><div>+</div><div>+static MMPortSerialAt *</div><div>+get_dial_port (MMBroadbandModemCinterion *modem,</div><div>+               MMPort                 *data,</div><div>+               MMPortSerialAt         *primary)</div><div>+{</div><div>+   //Use only the primary port for sending AT commands.</div><div>+    return g_object_ref (primary);</div><div>+}</div><div>+</div><div>+static void</div><div>+cinterion_3gpp_state_machine_logic (MMBroadbandBearerCinterion *self)</div><div>+{</div><div>+    Control3gppContext *ctx;</div><div>+</div><div>+    //For connecting flows.</div><div>+    if (self->priv->connect_pending != NULL)</div><div>+    {</div><div>+        ctx = self->priv->connect_pending;</div><div>+</div><div>+        switch (ctx->connect) {</div><div>+</div><div>+        //Always go from INIT to VERIFY_SWWAN.</div><div>+        case CONNECT_3GPP_CONTEXT_STEP_INIT: {</div><div>+            ctx->connect = CONNECT_3GPP_CONTEXT_STEP_VERIFY_SWWAN;</div><div>+            break;</div><div>+        }</div><div>+</div><div>+        //Always go from VERIFY_SWWAN to BEARER_PROPERTIES.</div><div>+        case CONNECT_3GPP_CONTEXT_STEP_VERIFY_SWWAN: {</div><div>+            //Only try 5 times to check the connection then give up.</div><div>+            if (self->priv->retry_count > 5)</div><div>+            {</div><div>+                g_simple_async_result_set_error (ctx->result,</div><div>+                                                 MM_CORE_ERROR,</div><div>+                                                 MM_CORE_ERROR_TOO_MANY,</div><div>+                                                 "Unknown bearer state during connection attempt.");</div><div>+</div><div>+                connect_3gpp_context_complete_and_free (ctx);</div><div>+                return;</div><div>+            }</div><div>+            self->priv->retry_count++;</div><div>+            ctx->connect = CONNECT_3GPP_CONTEXT_STEP_BEARER_PROPERTIES;</div><div>+            break;</div><div>+        }</div><div>+</div><div>+        //When in this state we can be setting up a brand new connection or,</div><div>+        //have just torn down a connection to setup a new one.</div><div>+        case CONNECT_3GPP_CONTEXT_STEP_BEARER_PROPERTIES: {</div><div>+            //We were connected (flags still set) & just issued SWWAN disconnect.</div><div>+            //Now refresh our usb connection state & retry.</div><div>+            //TODO: Rework for dual sim connections.</div><div>+            if (ctx->usb0_state == CONNECTION_ACTIVE || ctx->usb1_state == CONNECTION_ACTIVE)</div><div>+            {</div><div>+                ctx->connect = CONNECT_3GPP_CONTEXT_STEP_VERIFY_SWWAN;</div><div>+            }</div><div>+            //There is no active connection and we set the context correctly so continue to active SWWAN.</div><div>+            else if (ctx->usb0_state == CONNECTION_INACTIVE || ctx->usb1_state == CONNECTION_INACTIVE)</div><div>+                ctx->connect = CONNECT_3GPP_CONTEXT_STEP_SET_SWWAN;</div><div>+            else</div><div>+            {</div><div>+                //Error, should be impossible to get here. State machine broken.</div><div>+                ctx->self->priv->connect_pending = NULL;</div><div>+                g_simple_async_result_set_error (ctx->result,</div><div>+                                                 MM_CORE_ERROR,</div><div>+                                                 MM_CORE_ERROR_WRONG_STATE,</div><div>+                                                 "Unknown bearer state during connection attempt.");</div><div>+</div><div>+                connect_3gpp_context_complete_and_free (ctx);</div><div>+                return;</div><div>+            }</div><div>+</div><div>+            break;</div><div>+        }</div><div>+</div><div>+        //Always verify that the SWWAN connection is active after trying to set it up.</div><div>+        //Same step as CONNECT_3GPP_CONTEXT_STEP_VERIFY_SWWAN but makes it easy to detect</div><div>+        //if we just came from a new or old connection.</div><div>+        case CONNECT_3GPP_CONTEXT_STEP_SET_SWWAN: {</div><div>+            ctx->connect = CONNECT_3GPP_CONTEXT_STEP_CONNECTION_STATUS;</div><div>+            break;</div><div>+        }</div><div>+</div><div>+        //If SWWAN was just set, we expect it should be on.</div><div>+        case CONNECT_3GPP_CONTEXT_STEP_CONNECTION_STATUS: {</div><div>+            if ((g_ascii_strncasecmp(ctx->self->priv->bearer_interface, "usb0", 4)==0 && ctx->usb0_state == CONNECTION_ACTIVE) ||</div><div>+                (g_ascii_strncasecmp(ctx->self->priv->bearer_interface, "usb1", 4)==0 && ctx->usb1_state == CONNECTION_ACTIVE))</div><div>+                ctx->connect = CONNECT_3GPP_CONTEXT_STEP_DONE;</div><div>+            else</div><div>+            {</div><div>+                g_simple_async_result_set_error (ctx->result,</div><div>+                                                 MM_CORE_ERROR,</div><div>+                                                 MM_CORE_ERROR_WRONG_STATE,</div><div>+                                                 "Unknown bearer state during connection attempt.");</div><div>+</div><div>+                connect_3gpp_context_complete_and_free (ctx);</div><div>+                return;</div><div>+            }</div><div>+</div><div>+            break;</div><div>+        }</div><div>+</div><div>+        case CONNECT_3GPP_CONTEXT_STEP_DONE: {</div><div>+            //Place holder, nothing should call this. Fall into defualt error.</div><div>+        }</div><div>+</div><div>+        default: {</div><div>+            mm_err ("Unexpected SWWAN connect state. Unable to advance.");</div><div>+            ctx->self->priv->connect_pending = NULL;</div><div>+            g_simple_async_result_set_error (ctx->result,</div><div>+                                             MM_CORE_ERROR,</div><div>+                                             MM_CORE_ERROR_WRONG_STATE,</div><div>+                                             "Unexpected SWWAN connect state. Unable to advance.");</div><div>+</div><div>+            connect_3gpp_context_complete_and_free (ctx);</div><div>+            return;</div><div>+        }</div><div>+</div><div>+</div><div>+        }</div><div>+</div><div>+        connect_3gpp_context_step (ctx);</div><div>+    }</div><div>+    //Assume disconnecting flow, assert protects assumption.</div><div>+    else</div><div>+    {</div><div>+        g_assert (self->priv->disconnect_pending != NULL);</div><div>+        ctx = self->priv->disconnect_pending;</div><div>+</div><div>+        switch (ctx->disconnect) {</div><div>+</div><div>+        case DISCONNECT_3GPP_CONTEXT_STEP_INIT: {</div><div>+            ctx->disconnect = DISCONNECT_3GPP_CONTEXT_STEP_SWWAN_DETACH;</div><div>+            break;</div><div>+        }</div><div>+</div><div>+        case DISCONNECT_3GPP_CONTEXT_STEP_SWWAN_DETACH: {</div><div>+            ctx->disconnect = DISCONNECT_3GPP_CONTEXT_STEP_CONNECTION_STATUS;</div><div>+            break;</div><div>+        }</div><div>+</div><div>+        case DISCONNECT_3GPP_CONTEXT_STEP_CONNECTION_STATUS: {</div><div>+            //We expect the bearer to be disconnected now. If it's not try again.</div><div>+            //TODO: Rework for dual sim connections.</div><div>+            if ((g_ascii_strncasecmp(ctx->self->priv->bearer_interface, "usb0", 4)==0 && ctx->usb0_state == CONNECTION_INACTIVE) ||</div><div>+                (g_ascii_strncasecmp(ctx->self->priv->bearer_interface, "usb1", 4)==0 && ctx->usb1_state == CONNECTION_INACTIVE))</div><div>+                ctx->disconnect = DISCONNECT_3GPP_CONTEXT_STEP_DONE;</div><div>+            else</div><div>+            {</div><div>+                ctx->disconnect = DISCONNECT_3GPP_CONTEXT_STEP_SWWAN_DETACH;</div><div>+                sleep (1); //Wait a second on disconnect retry's.</div><div>+            }</div><div>+            break;</div><div>+        }</div><div>+</div><div>+        case DISCONNECT_3GPP_CONTEXT_STEP_DONE: {</div><div>+            //Place holder, nothing should call this. Fall into defualt error.</div><div>+        }</div><div>+</div><div>+        default: {</div><div>+            mm_err ("Unexpected SWWAN disconnect state. Unable to advance.");</div><div>+            ctx->self->priv->connect_pending = NULL;</div><div>+            g_simple_async_result_set_error (ctx->result,</div><div>+                                             MM_CORE_ERROR,</div><div>+                                             MM_CORE_ERROR_WRONG_STATE,</div><div>+                                             "Unexpected SWWAN disconnect state. Unable to advance.");</div><div>+</div><div>+            connect_3gpp_context_complete_and_free (ctx);</div><div>+</div><div>+            return;</div><div>+        }</div><div>+</div><div>+        }</div><div>+</div><div>+        disconnect_3gpp_context_step (ctx);</div><div>+    }</div><div>+}</div><div>+</div><div>+static gboolean</div><div>+proccess_swwan_response(MMBroadbandBearerCinterion *self, GList *result)</div><div>+{</div><div>+    Control3gppContext *ctx;</div><div>+</div><div>+    if (self->priv->connect_pending != NULL)</div><div>+        ctx = self->priv->connect_pending;</div><div>+    else</div><div>+    {</div><div>+        g_assert (self->priv->disconnect_pending != NULL);</div><div>+        ctx = self->priv->disconnect_pending;</div><div>+    }</div><div>+</div><div>+</div><div>+    if (g_list_length(result) != 0) {</div><div>+        int first_result = GPOINTER_TO_INT(result->data);</div><div>+</div><div>+        //mm_serial_parser_v1_parse will catch CME Error. Parent function will then send</div><div>+        //that error to dbus out.</div><div>+        if(first_result == -1)</div><div>+            return FALSE;</div><div>+        //Recived an 'OK' response from a write command, place holder so we don't error below.</div><div>+       if (first_result == 0 && ctx->self->priv->swwan_read_write == 1)</div><div>+            NULL;</div><div>+        //Recived an 'OK'(0) response from an swwwan read command.</div><div>+        else if (first_result == 0 && ctx->self->priv->swwan_read_write == 0)</div><div>+        {</div><div>+            ctx->usb0_state = CONNECTION_INACTIVE;</div><div>+            ctx->usb1_state = CONNECTION_INACTIVE;</div><div>+        }</div><div>+        //1 || 3 result is the CID, given when that context is activated.</div><div>+       //TODO: Rework for dual sim connections.</div><div>+        else if (first_result == 1 && ctx->self->priv->swwan_read_write == 0)</div><div>+            ctx->usb1_state = CONNECTION_ACTIVE;</div><div>+        else if (first_result == 3 && ctx->self->priv->swwan_read_write == 0)</div><div>+            ctx->usb0_state = CONNECTION_ACTIVE;</div><div>+        else</div><div>+        {</div><div>+            for (; result; result = g_list_next (result))</div><div>+                mm_err ("Unknown SWWAN response data:%i", GPOINTER_TO_INT(result->data));</div><div>+</div><div>+            g_simple_async_result_set_error (ctx->result,</div><div>+                                             MM_CORE_ERROR,</div><div>+                                             MM_CORE_ERROR_FAILED,</div><div>+                                             "Internal error while processing SWWAN response.");</div><div>+</div><div>+            return FALSE;</div><div>+        }</div><div>+     }</div><div>+     else {</div><div>+        mm_err ("Unable to parse zero length SWWAN response.");</div><div>+        return FALSE;</div><div>+     }</div><div>+</div><div>+     return TRUE;</div><div>+}</div><div>+</div><div>+static void</div><div>+get_swwan_response (MMBaseModem *modem,</div><div>+                          GAsyncResult *res,</div><div>+                          MMBroadbandBearerCinterion *self)</div><div>+{</div><div>+    Control3gppContext *ctx;</div><div>+    const gchar *response;</div><div>+    GError *error = NULL;</div><div>+    GList *response_parsed = NULL;</div><div>+</div><div>+    if (self->priv->connect_pending != NULL)</div><div>+        ctx = self->priv->connect_pending;</div><div>+    else</div><div>+    {</div><div>+        g_assert (self->priv->disconnect_pending != NULL);</div><div>+        ctx = self->priv->disconnect_pending;</div><div>+    }</div><div>+</div><div>+    // Balance refcount</div><div>+    g_object_unref (self);</div><div>+</div><div>+    //Parse the swwan response.</div><div>+    response = mm_base_modem_at_command_finish (modem, res, &error);</div><div>+</div><div>+    //1st elem of response_parsed will be:</div><div>+    //1 or 3 for an active swwan, -1 for a valid error, 0 for assumed 'OK' (no conection)</div><div>+    if (error == NULL && !mm_cinterion_parse_swwan_response (response, &response_parsed, &error))</div><div>+            NULL;</div><div>+</div><div>+</div><div>+    if (error != NULL || !proccess_swwan_response(ctx->self, response_parsed)) {</div><div>+        ctx->self->priv->connect_pending = NULL;</div><div>+        ctx->self->priv->disconnect_pending = NULL;</div><div>+</div><div>+        g_simple_async_result_take_error (ctx->result, error);</div><div>+</div><div>+        if (ctx->connect)</div><div>+            connect_3gpp_context_complete_and_free (ctx);</div><div>+        else</div><div>+            disconnect_3gpp_context_complete_and_free (ctx);</div><div>+</div><div>+        return;</div><div>+</div><div>+    }</div><div>+</div><div>+    mm_dbg ("usb0-state:%i usb1-state:%i", ctx->usb0_state, ctx->usb1_state);</div><div>+</div><div>+    g_list_free(response_parsed);</div><div>+    g_clear_error (&error);</div><div>+</div><div>+    cinterion_3gpp_state_machine_logic(ctx->self);</div><div>+</div><div>+    return;</div><div>+}</div><div>+</div><div>+/*****************************************************************************/</div><div>+/* Cinterion AT Command Wrappers */</div><div>+</div><div>+static void</div><div>+send_swwan_read_command(Control3gppContext *ctx)</div><div>+{</div><div>+    ctx->self->priv->swwan_read_write = 0;</div><div>+</div><div>+    //Check for swwan connection. The next state will be preformed by the callback.</div><div>+    mm_base_modem_at_command_full (ctx->modem,</div><div>+                                   ctx->primary,</div><div>+                                   "^SWWAN?",</div><div>+                                   5,</div><div>+                                   FALSE,</div><div>+                                   FALSE,</div><div>+                                   NULL,</div><div>+                                   (GAsyncReadyCallback)get_swwan_response,</div><div>+                                   g_object_ref (ctx->self));</div><div>+}</div><div>+</div><div>+static void</div><div>+send_swwan_connect_command(Control3gppContext *ctx)</div><div>+{</div><div>+    //USB0(1st wwan adapt) -> 3rd context, USB1(2nd wwan adapt) -> 1st context</div><div>+    gchar               *command;</div><div>+    command = g_strdup_printf ("^SWWAN=%s,%s,%s",</div><div>+                               "1",</div><div>+                               ctx->self->priv->pdp_cid, //Expect 1 or 3</div><div>+                               g_ascii_strncasecmp(ctx->self->priv->pdp_cid, "3", 1)==0  ? "1" : "2");</div><div>+</div><div>+    ctx->self->priv->swwan_read_write = 1;</div><div>+</div><div>+    //Start the swwan connection. The next state will be preformed by the callback.</div><div>+    mm_base_modem_at_command_full (ctx->modem,</div><div>+                                   ctx->primary,</div><div>+                                   command,</div><div>+                                   10,/*Seen it take 5 seconds :0 */</div><div>+                                   FALSE,</div><div>+                                   FALSE,</div><div>+                                   NULL,</div><div>+                                   (GAsyncReadyCallback)get_swwan_response,</div><div>+                                   g_object_ref (ctx->self));</div><div>+</div><div>+    g_free (command);</div><div>+}</div><div>+</div><div>+static void</div><div>+send_swwan_disconnect_command(Control3gppContext *ctx)</div><div>+{</div><div>+    gchar               *command;</div><div>+    command = g_strdup_printf ("^SWWAN=%s,%s,%s",</div><div>+                               "0",</div><div>+                               ctx->self->priv->pdp_cid,</div><div>+                               g_ascii_strncasecmp(ctx->self->priv->pdp_cid, "3", 1)==0  ? "1" : "2");</div><div>+</div><div>+    ctx->self->priv->swwan_read_write = 1;</div><div>+</div><div>+    //Check for swwan connection. The next state will be preformed by the callback.</div><div>+    mm_base_modem_at_command_full (ctx->modem,</div><div>+                                   ctx->primary,</div><div>+                                   command,</div><div>+                                   10,</div><div>+                                   FALSE,</div><div>+                                   FALSE,</div><div>+                                   NULL,</div><div>+                                   (GAsyncReadyCallback)get_swwan_response,</div><div>+                                   g_object_ref (ctx->self));</div><div>+</div><div>+    g_free (command);</div><div>+}</div><div>+</div><div>+</div><div>+</div><div>+</div><div>+/*****************************************************************************/</div><div>+/* Connect 3GPP */</div><div>+</div><div>+static gint</div><div>+cinterion_parse_auth_type (MMBearerAllowedAuth mm_auth)</div><div>+{</div><div>+    switch (mm_auth) {</div><div>+    case MM_BEARER_ALLOWED_AUTH_NONE:</div><div>+        return MM_BEARER_CINTERION_AUTH_NONE;</div><div>+    case MM_BEARER_ALLOWED_AUTH_PAP:</div><div>+        return MM_BEARER_CINTERION_AUTH_PAP;</div><div>+    case MM_BEARER_ALLOWED_AUTH_CHAP:</div><div>+        return MM_BEARER_CINTERION_AUTH_CHAP;</div><div>+    case MM_BEARER_ALLOWED_AUTH_MSCHAPV2:</div><div>+        return MM_BEARER_CINTERION_AUTH_MSCHAPV2;</div><div>+    default:</div><div>+        return MM_BEARER_CINTERION_AUTH_UNKNOWN;</div><div>+    }</div><div>+}</div><div>+</div><div>+static void</div><div>+connect_3gpp_context_complete_and_free (Control3gppContext *ctx)</div><div>+{</div><div>+    g_simple_async_result_complete_in_idle (ctx->result);</div><div>+    g_object_unref (ctx->cancellable);</div><div>+    g_object_unref (ctx->result);</div><div>+    g_object_unref (ctx->modem);</div><div>+    g_object_unref (ctx->self);</div><div>+    g_clear_object (&ctx->ipv4_config);</div><div>+    g_clear_object (&ctx->data);</div><div>+    g_clear_object (&ctx->primary);</div><div>+</div><div>+    g_slice_free (Control3gppContext, ctx);</div><div>+}</div><div>+</div><div>+static MMBearerConnectResult *</div><div>+connect_3gpp_finish (MMBroadbandBearer *self,</div><div>+                     GAsyncResult *res,</div><div>+                     GError **error)</div><div>+{</div><div>+    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))</div><div>+        return NULL;</div><div>+</div><div>+    return mm_bearer_connect_result_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));</div><div>+}</div><div>+</div><div>+static gchar *</div><div>+build_cinterion_pdp_context_string(Control3gppContext *ctx)</div><div>+{</div><div>+    const gchar         *apn = NULL;</div><div>+    const gchar         *user = NULL;</div><div>+    const gchar         *passwd = NULL;</div><div>+    MMBearerAllowedAuth  auth;</div><div>+    gint                 encoded_auth = MM_BEARER_CINTERION_AUTH_UNKNOWN;</div><div>+    gchar               *command;</div><div>+</div><div>+    apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));</div><div>+    user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));</div><div>+    passwd = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));</div><div>+    auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));</div><div>+    encoded_auth = cinterion_parse_auth_type (auth);</div><div>+</div><div>+    /* Default to no authentication if not specified */</div><div>+    if (encoded_auth == MM_BEARER_CINTERION_AUTH_UNKNOWN) {</div><div>+        encoded_auth = MM_BEARER_CINTERION_AUTH_NONE;</div><div>+        mm_dbg ("Unable to detect authentication type. Defaulting to:%i", encoded_auth);</div><div>+    }</div><div>+</div><div>+    //TODO: Get IP type if specific protocol was specified. Hardcoded to IPV4 for now.</div><div>+</div><div>+    if (!user && !passwd)</div><div>+        command = g_strdup_printf ("+CGDCONT=%s,\"IP\",\"%s\",\"0.0.0.0\",0,0",</div><div>+                                   ctx->self->priv->pdp_cid,</div><div>+                                   apn == NULL ? "" : apn);</div><div>+</div><div>+    /*TODO: Can't test this as we can't get a hold of a SIM w/ this feature atm. This is a place</div><div>+    * holder. When we can test this then it should be refactored & split into another state so we</div><div>+    * can tell independently if the SGAUTH fails.</div><div>+    * Write Command</div><div>+    * AT^SGAUTH=<cid>[, <auth_type>[, <passwd>, <user>]]</div><div>+    * Response(s) </div><div>+    * OK</div><div>+    * ERROR</div><div>+    * +CME ERROR: <err></div><div>+    */</div><div>+    else</div><div>+        command = g_strdup_printf ("^SGAUTH=%s,%i,%s,%s; +CGDCONT=%s,\"IP\",\"%s\",\"0.0.0.0\",0,0 ",</div><div>+                                   ctx->self->priv->pdp_cid,</div><div>+                                   encoded_auth,</div><div>+                                   passwd == NULL ? "" : passwd,</div><div>+                                   user == NULL ? "" : user,</div><div>+                                   ctx->self->priv->pdp_cid,</div><div>+                                   apn == NULL ? "" : apn);</div><div>+    return command;</div><div>+}</div><div>+</div><div>+static gboolean</div><div>+set_pdp_cid(Control3gppContext *ctx)</div><div>+{</div><div>+    GUdevClient         *client;</div><div>+    GUdevDevice         *data_device;</div><div>+</div><div>+    //Need the data port to figure out what context to activate.</div><div>+    client = g_udev_client_new (NULL);</div><div>+    data_device = (g_udev_client_query_by_subsystem_and_name (</div><div>+                       client,</div><div>+                       "net",</div><div>+                       mm_port_get_device (ctx->data)));</div><div>+    ctx->self->priv->bearer_interface = g_udev_device_get_name(data_device);</div><div>+</div><div>+    //Map PDP context from the current Bearer. USB0 -> 3rd context, USB1 -> 1st context</div><div>+    if (g_ascii_strncasecmp(ctx->self->priv->bearer_interface, "usb0", 4) == 0)</div><div>+        ctx->self->priv->  pdp_cid = "3";</div><div>+    else if (g_ascii_strncasecmp(ctx->self->priv->bearer_interface, "usb1", 4) == 0)</div><div>+        ctx->self->priv->pdp_cid = "1";</div><div>+    else</div><div>+    {</div><div>+        mm_err ("Unable to map usb interface:(%s) to context", ctx->self->priv->bearer_interface);</div><div>+</div><div>+        g_simple_async_result_set_error (ctx->result,</div><div>+                                         MM_CORE_ERROR,</div><div>+                                         MM_CORE_ERROR_FAILED,</div><div>+                                         "Internal error while mapping USB network interface.");</div><div>+</div><div>+        ctx->self->priv->connect_pending = NULL;</div><div>+        ctx->self->priv->disconnect_pending = NULL;</div><div>+</div><div>+        if (ctx->connect)</div><div>+            connect_3gpp_context_complete_and_free (ctx);</div><div>+        else</div><div>+            disconnect_3gpp_context_complete_and_free (ctx);</div><div>+</div><div>+        return FALSE;</div><div>+    }</div><div>+</div><div>+    return TRUE;</div><div>+}</div><div>+</div><div>+static void</div><div>+handle_cancel_connect(Control3gppContext *ctx)</div><div>+{</div><div>+    gchar               *command;</div><div>+</div><div>+    // Clear context</div><div>+    ctx->self->priv->connect_pending = NULL;</div><div>+</div><div>+    command = g_strdup_printf ("^SWWAN=%s,%s,%s",</div><div>+                               "0",</div><div>+                               ctx->self->priv->pdp_cid, //Expect 1 or 3,</div><div>+                               g_ascii_strncasecmp(ctx->self->priv->pdp_cid, "3", 1)==0  ? "1" : "2");</div><div>+</div><div>+    //Disconnect, may not succeed. Will not check response on cancel.</div><div>+    mm_base_modem_at_command_full (ctx->modem,</div><div>+                                   ctx->primary,</div><div>+                                   command,</div><div>+                                   3,</div><div>+                                   FALSE,</div><div>+                                   FALSE,</div><div>+                                   NULL,</div><div>+                                   NULL, // Do not care the AT response</div><div>+                                   NULL);</div><div>+</div><div>+    g_simple_async_result_set_error (ctx->result,</div><div>+                                     MM_CORE_ERROR,</div><div>+                                     MM_CORE_ERROR_CANCELLED,</div><div>+                                     "Cinterion connection operation has been cancelled");</div><div>+    connect_3gpp_context_complete_and_free (ctx);</div><div>+</div><div>+}</div><div>+</div><div>+static void</div><div>+context_ready (MMBaseModem *modem,</div><div>+                       GAsyncResult *res,</div><div>+                       MMBroadbandBearerCinterion *self)</div><div>+{</div><div>+    Control3gppContext  *ctx;</div><div>+    GError              *error = NULL;</div><div>+    const gchar         *response;</div><div>+</div><div>+    ctx = self->priv->connect_pending;</div><div>+    g_assert (ctx != NULL);</div><div>+</div><div>+    /* Balance refcount */</div><div>+    g_object_unref (self);</div><div>+</div><div>+    //Get the cgdcont write response. We expect: 'OK'</div><div>+    //Responses: OK, ERROR</div><div>+    response = mm_base_modem_at_command_finish (modem, res, &error);</div><div>+</div><div>+    if (!response || error != NULL) {</div><div>+        mm_err ("CGDCONT read- Error:%d response:%s", error->code, response);</div><div>+</div><div>+        self->priv->connect_pending = NULL;</div><div>+        g_simple_async_result_take_error (ctx->result, error);</div><div>+        connect_3gpp_context_complete_and_free (ctx);</div><div>+</div><div>+        return;</div><div>+    }</div><div>+</div><div>+    /* Go to next step */</div><div>+    //ctx->connect = CONNECT_3GPP_CONTEXT_STEP_SET_SWWAN;</div><div>+    cinterion_3gpp_state_machine_logic(ctx->self);</div><div>+}</div><div>+</div><div>+static void</div><div>+connect_3gpp_context_step (Control3gppContext *ctx)</div><div>+{</div><div>+    /* Check for cancellation */</div><div>+    if (g_cancellable_is_cancelled (ctx->cancellable)) {</div><div>+        //Init the current context if not done already.</div><div>+        if (g_ascii_strncasecmp(ctx->self->priv->pdp_cid, "0", 1)==0)</div><div>+            if(set_pdp_cid(ctx)) //Don't care about failure case for cancel.</div><div>+</div><div>+        handle_cancel_connect(ctx);</div><div>+        return;</div><div>+    }</div><div>+</div><div>+    // Network-initiated disconnect should not be outstanding at this point,</div><div>+    // because it interferes with the connect attempt.</div><div>+    g_assert (ctx->self->priv->network_disconnect_pending_id == 0);</div><div>+    g_assert (ctx->self->priv->disconnect_pending == NULL);</div><div>+</div><div>+</div><div>+    switch (ctx->connect) {</div><div>+    case CONNECT_3GPP_CONTEXT_STEP_INIT: {</div><div>+        MMBearerIpFamily ip_family;</div><div>+</div><div>+        //Initialize variables, reminder!</div><div>+        ctx->usb0_state = CONNECTION_UNKNOWN;</div><div>+        ctx->usb1_state = CONNECTION_UNKNOWN;</div><div>+        ctx->self->priv->pdp_cid = "0"; //Cinterion doesn't have cid == 0</div><div>+        ctx->self->priv->swwan_read_write = -1;</div><div>+        ctx->self->priv->retry_count = 0;</div><div>+</div><div>+        if(!set_pdp_cid(ctx))</div><div>+            return;</div><div>+</div><div>+        ip_family = mm_bearer_properties_get_ip_type (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));</div><div>+</div><div>+        //TODO: Fix so more than IPV4 can be used.</div><div>+        if (ip_family == MM_BEARER_IP_FAMILY_NONE ||</div><div>+            ip_family == MM_BEARER_IP_FAMILY_ANY) {</div><div>+            gchar *ip_family_str;</div><div>+</div><div>+            ip_family = mm_base_bearer_get_default_ip_family (MM_BASE_BEARER (ctx->self));</div><div>+            ip_family_str = mm_bearer_ip_family_build_string_from_mask (ip_family);</div><div>+            mm_dbg ("No specific IP family requested, defaulting to %s",</div><div>+                    ip_family_str);</div><div>+            g_free (ip_family_str);</div><div>+        }</div><div>+</div><div>+        /* Default to automatic/DHCP addressing */</div><div>+        ctx->ipv4_config = mm_bearer_ip_config_new ();</div><div>+        mm_bearer_ip_config_set_method (ctx->ipv4_config, MM_BEARER_IP_METHOD_DHCP);</div><div>+</div><div>+        /* Store the context */</div><div>+        ctx->self->priv->connect_pending = ctx;</div><div>+</div><div>+        cinterion_3gpp_state_machine_logic(ctx->self);</div><div>+        return;</div><div>+    }</div><div>+    case CONNECT_3GPP_CONTEXT_STEP_VERIFY_SWWAN: {</div><div>+        send_swwan_read_command(ctx);</div><div>+        return;</div><div>+    }</div><div>+</div><div>+    case CONNECT_3GPP_CONTEXT_STEP_BEARER_PROPERTIES: {</div><div>+        gchar               *command = NULL;</div><div>+</div><div>+        //TODO: Rework for dual sim connections, don't disconnect the wrong one.</div><div>+        //If there is already an active SWWAN connection disconnect before trying to set it's context.</div><div>+        if ((ctx->usb0_state == CONNECTION_ACTIVE && g_ascii_strncasecmp(ctx->self->priv->pdp_cid, "3", 1)) || ctx->usb0_state == CONNECTION_UNKNOWN ||</div><div>+            (ctx->usb1_state == CONNECTION_ACTIVE && g_ascii_strncasecmp(ctx->self->priv->pdp_cid, "1", 1)) || ctx->usb1_state == CONNECTION_UNKNOWN)</div><div>+        {</div><div>+            send_swwan_disconnect_command(ctx);</div><div>+            g_free (command);</div><div>+            return;</div><div>+        }</div><div>+</div><div>+        command = build_cinterion_pdp_context_string(ctx);</div><div>+</div><div>+        //Set the PDP context with cgdcont.</div><div>+        mm_base_modem_at_command_full (ctx->modem,</div><div>+                                       ctx->primary,</div><div>+                                       command,</div><div>+                                       3,</div><div>+                                       FALSE,</div><div>+                                       FALSE,</div><div>+                                       NULL,</div><div>+                                       (GAsyncReadyCallback)context_ready,</div><div>+                                       g_object_ref (ctx->self));</div><div>+</div><div>+        g_free (command);</div><div>+        return;</div><div>+    }</div><div>+    case CONNECT_3GPP_CONTEXT_STEP_SET_SWWAN: {</div><div>+        send_swwan_connect_command(ctx);</div><div>+        return;</div><div>+    }</div><div>+    case CONNECT_3GPP_CONTEXT_STEP_CONNECTION_STATUS: {</div><div>+        send_swwan_read_command(ctx);</div><div>+        return;</div><div>+    }</div><div>+</div><div>+    case CONNECT_3GPP_CONTEXT_STEP_DONE: {</div><div>+        /* Clear context */</div><div>+        ctx->self->priv->connect_pending = NULL;</div><div>+        ctx->self->priv->bearer_interface = NULL;</div><div>+        ctx->self->priv->pdp_cid = NULL;</div><div>+        ctx->self->priv->swwan_read_write = -1;</div><div>+</div><div>+        /* Setup result */</div><div>+        {</div><div>+            if (ctx->ipv4_config) {</div><div>+                g_simple_async_result_set_op_res_gpointer (</div><div>+                    ctx->result,</div><div>+                    mm_bearer_connect_result_new (ctx->data, ctx->ipv4_config, NULL),</div><div>+                    (GDestroyNotify)mm_bearer_connect_result_unref);</div><div>+            }</div><div>+            else {</div><div>+                g_simple_async_result_set_error (ctx->result,</div><div>+                                                 MM_CORE_ERROR,</div><div>+                                                 MM_CORE_ERROR_WRONG_STATE,</div><div>+                                                 "Cinterion connection failed to set IP protocol");</div><div>+            }</div><div>+        }</div><div>+</div><div>+        connect_3gpp_context_complete_and_free (ctx);</div><div>+        return;</div><div>+    }</div><div>+    }</div><div>+}</div><div>+</div><div>+static void</div><div>+connect_3gpp (MMBroadbandBearer *self,</div><div>+              MMBroadbandModem *modem,</div><div>+              MMPortSerialAt *primary,</div><div>+              MMPortSerialAt *secondary,</div><div>+              GCancellable *cancellable,</div><div>+              GAsyncReadyCallback callback,</div><div>+              gpointer user_data)</div><div>+{</div><div>+    Control3gppContext  *ctx;</div><div>+    MMPort *port;</div><div>+</div><div>+    g_assert (primary != NULL);</div><div>+</div><div>+    // We need a net port</div><div>+    port = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET);</div><div>+    if (!port) {</div><div>+        g_simple_async_report_error_in_idle (G_OBJECT (self),</div><div>+                                             callback,</div><div>+                                             user_data,</div><div>+                                             MM_CORE_ERROR,</div><div>+                                             MM_CORE_ERROR_NOT_FOUND,</div><div>+                                             "No valid data port found to launch connection");</div><div>+        return;</div><div>+    }</div><div>+</div><div>+</div><div>+    /* Setup connection context */</div><div>+    ctx = g_slice_new0 (Control3gppContext);</div><div>+    ctx->self = g_object_ref (self);</div><div>+    ctx->modem = g_object_ref (modem);</div><div>+    ctx->data = g_object_ref (port);</div><div>+    ctx->result = g_simple_async_result_new (G_OBJECT (self),</div><div>+                                             callback,</div><div>+                                             user_data,</div><div>+                                             connect_3gpp);</div><div>+    ctx->cancellable = g_object_ref (cancellable);</div><div>+    ctx->connect = CONNECT_3GPP_CONTEXT_STEP_INIT;</div><div>+</div><div>+    g_assert (ctx->self->priv->connect_pending == NULL);</div><div>+    g_assert (ctx->self->priv->disconnect_pending == NULL);</div><div>+</div><div>+    ctx->primary = get_dial_port (MM_BROADBAND_MODEM_CINTERION (ctx->modem), ctx->data, primary);</div><div>+</div><div>+    /* Run! */</div><div>+    connect_3gpp_context_step (ctx);</div><div>+}</div><div>+</div><div>+/*****************************************************************************/</div><div>+/* Disconnect 3GPP */</div><div>+</div><div>+static void</div><div>+disconnect_3gpp_context_complete_and_free (Control3gppContext *ctx)</div><div>+{</div><div>+    g_simple_async_result_complete_in_idle (ctx->result);</div><div>+    g_object_unref (ctx->result);</div><div>+    g_object_unref (ctx->primary);</div><div>+    g_object_unref (ctx->self);</div><div>+    g_object_unref (ctx->modem);</div><div>+    g_slice_free (Control3gppContext, ctx);</div><div>+}</div><div>+</div><div>+static gboolean</div><div>+disconnect_3gpp_finish (MMBroadbandBearer *self,</div><div>+                        GAsyncResult *res,</div><div>+                        GError **error)</div><div>+{</div><div>+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);</div><div>+}</div><div>+</div><div>+static void</div><div>+disconnect_3gpp_context_step (Control3gppContext *ctx)</div><div>+{</div><div>+    //There is no cancel handling b/c the only thing we could do would</div><div>+    //be disconnect, which is already happening and bad things can happen</div><div>+    //if we abandon the modem in an unknown state.</div><div>+</div><div>+    //Don't allow disconnect while connect in progress.</div><div>+    g_assert (ctx->self->priv->connect_pending == NULL);</div><div>+</div><div>+    switch (ctx->disconnect) {</div><div>+    case DISCONNECT_3GPP_CONTEXT_STEP_INIT:</div><div>+        /* Store the context */</div><div>+        ctx->self->priv->disconnect_pending = ctx;</div><div>+</div><div>+        //Initialize variables, reminder!</div><div>+        ctx->self->priv->pdp_cid = "0";</div><div>+        ctx->self->priv->retry_count = 0;</div><div>+        ctx->self->priv->swwan_read_write = -1;</div><div>+</div><div>+        // We ignore any pending network-initiated disconnection in order to prevent it</div><div>+        // from interfering with the client-initiated disconnection, as we would like to</div><div>+        // proceed with the latter anyway.</div><div>+        if (ctx->self->priv->network_disconnect_pending_id != 0) {</div><div>+            g_source_remove (ctx->self->priv->network_disconnect_pending_id);</div><div>+            ctx->self->priv->network_disconnect_pending_id = 0;</div><div>+        }</div><div>+</div><div>+        if(!set_pdp_cid(ctx))</div><div>+            return;</div><div>+</div><div>+        cinterion_3gpp_state_machine_logic(ctx->self);</div><div>+        return;</div><div>+</div><div>+    case DISCONNECT_3GPP_CONTEXT_STEP_SWWAN_DETACH:</div><div>+        // If too many retries (1s of wait between the retries), failed</div><div>+        if ( ctx->self->priv->retry_count > 5) {</div><div>+            // Clear context</div><div>+            ctx->self->priv->disconnect_pending = NULL;</div><div>+            g_simple_async_result_set_error (ctx->result,</div><div>+                                             MM_CORE_ERROR,</div><div>+                                             MM_CORE_ERROR_TOO_MANY,</div><div>+                                             "Disconnection attempt timed out");</div><div>+</div><div>+            disconnect_3gpp_context_complete_and_free (ctx);</div><div>+            return;</div><div>+        }</div><div>+</div><div>+        //Has call back to next state.</div><div>+        send_swwan_disconnect_command(ctx);</div><div>+</div><div>+        //Only try to detach so many times.</div><div>+        ctx->self->priv->retry_count++;</div><div>+</div><div>+        return;</div><div>+    case DISCONNECT_3GPP_CONTEXT_STEP_CONNECTION_STATUS:</div><div>+         //Has call back to next state.</div><div>+         send_swwan_read_command(ctx);</div><div>+         return;</div><div>+</div><div>+    case DISCONNECT_3GPP_CONTEXT_STEP_DONE:</div><div>+        // Clear context</div><div>+        ctx->self->priv->disconnect_pending = NULL;</div><div>+        ctx->self->priv->pdp_cid = NULL;</div><div>+        ctx->self->priv->swwan_read_write = -1;</div><div>+        // Set data port as result</div><div>+        g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);</div><div>+        disconnect_3gpp_context_complete_and_free (ctx);</div><div>+</div><div>+        return;</div><div>+    }</div><div>+}</div><div>+</div><div>+static void</div><div>+disconnect_3gpp (MMBroadbandBearer *self,</div><div>+                 MMBroadbandModem *modem,</div><div>+                 MMPortSerialAt *primary,</div><div>+                 MMPortSerialAt *secondary,</div><div>+                 MMPort *data,</div><div>+                 guint cid,</div><div>+                 GAsyncReadyCallback callback,</div><div>+                 gpointer user_data)</div><div>+{</div><div>+    Control3gppContext *ctx;</div><div>+    MMPort *port;</div><div>+</div><div>+    g_assert (primary != NULL);</div><div>+</div><div>+    ctx = g_slice_new0 (Control3gppContext);</div><div>+    ctx->self = g_object_ref (self);</div><div>+    ctx->modem = MM_BASE_MODEM (g_object_ref (modem));</div><div>+    ctx->result = g_simple_async_result_new (G_OBJECT (self),</div><div>+                                             callback,</div><div>+                                             user_data,</div><div>+                                             disconnect_3gpp);</div><div>+    ctx->disconnect = DISCONNECT_3GPP_CONTEXT_STEP_INIT;</div><div>+</div><div>+    g_assert (ctx->self->priv->connect_pending == NULL);</div><div>+    g_assert (ctx->self->priv->disconnect_pending == NULL);</div><div>+</div><div>+    //TODO: Not sure how else to get active data port? Can this be done without adding this</div><div>+    //function to mm-base-modem.c?</div><div>+    //TODO: Dual SIM how do we know which interface to grab/disconnect if two are active?</div><div>+    port = mm_base_modem_peek_current_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET);</div><div>+    if (!port) {</div><div>+        g_simple_async_report_error_in_idle (G_OBJECT (self),</div><div>+                                             callback,</div><div>+                                             user_data,</div><div>+                                             MM_CORE_ERROR,</div><div>+                                             MM_CORE_ERROR_NOT_FOUND,</div><div>+                                             "No valid data port found to tear down.");</div><div>+        return;</div><div>+    }</div><div>+</div><div>+    ctx->data = g_object_ref (port);</div><div>+</div><div>+    ctx->primary = get_dial_port (MM_BROADBAND_MODEM_CINTERION (ctx->modem), data, primary);</div><div>+</div><div>+    /* Start! */</div><div>+    disconnect_3gpp_context_step (ctx);</div><div>+}</div><div>+</div><div>+/*****************************************************************************/</div><div>+</div><div>+static gboolean</div><div>+network_disconnect_3gpp_delayed (MMBroadbandBearerCinterion *self)</div><div>+{</div><div>+    mm_dbg ("Disconnect bearer '%s' on network request.",</div><div>+            mm_base_bearer_get_path (MM_BASE_BEARER (self)));</div><div>+</div><div>+    self->priv->network_disconnect_pending_id = 0;</div><div>+    mm_base_bearer_report_connection_status (MM_BASE_BEARER (self),</div><div>+                                             MM_BEARER_CONNECTION_STATUS_DISCONNECTED);</div><div>+    return G_SOURCE_REMOVE;</div><div>+}</div><div>+</div><div>+//TODO: Test this.</div><div>+static void</div><div>+report_connection_status (MMBaseBearer *bearer,</div><div>+                          MMBearerConnectionStatus status)</div><div>+{</div><div>+    MMBroadbandBearerCinterion *self = MM_BROADBAND_BEARER_CINTERION (bearer);</div><div>+</div><div>+    g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED ||</div><div>+              status == MM_BEARER_CONNECTION_STATUS_DISCONNECTING ||</div><div>+              status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);</div><div>+</div><div>+    /* When a pending connection / disconnection attempt is in progress, we use</div><div>+     * ^SWWAN? to check the connection status and thus temporarily ignore</div><div>+     * unsolicited messages */</div><div>+    if (self->priv->connect_pending || self->priv->disconnect_pending)</div><div>+        return;</div><div>+</div><div>+    /* Ignore 'CONNECTED' */</div><div>+    if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED)</div><div>+        return;</div><div>+</div><div>+    /* We already use ^SWWAN? to poll the connection status, so only</div><div>+     * handle network-initiated disconnection here. */</div><div>+    if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTING) {</div><div>+        /* MM_BEARER_CONNECTION_STATUS_DISCONNECTING is used to indicate that the</div><div>+         * reporting of disconnection should be delayed. See MMBroadbandModemCinterion's</div><div>+         * bearer_report_connection_status for details. */</div><div>+        if (mm_base_bearer_get_status (bearer) == MM_BEARER_STATUS_CONNECTED &&</div><div>+            self->priv->network_disconnect_pending_id == 0) {</div><div>+            mm_dbg ("Delay network-initiated disconnection of bearer '%s'",</div><div>+                    mm_base_bearer_get_path (MM_BASE_BEARER (self)));</div><div>+            self->priv->network_disconnect_pending_id = (g_timeout_add_seconds (</div><div>+                                                             4,</div><div>+                                                             (GSourceFunc) network_disconnect_3gpp_delayed,</div><div>+                                                             self));</div><div>+        }</div><div>+        return;</div><div>+    }</div><div>+</div><div>+    /* Report disconnected right away */</div><div>+    MM_BASE_BEARER_CLASS (mm_broadband_bearer_cinterion_parent_class)->report_connection_status (</div><div>+        bearer,</div><div>+        MM_BEARER_CONNECTION_STATUS_DISCONNECTED);</div><div>+}</div><div>+</div><div>+/*****************************************************************************/</div><div>+</div><div>+MMBaseBearer *</div><div>+mm_broadband_bearer_cinterion_new_finish (GAsyncResult *res,</div><div>+                                       GError **error)</div><div>+{</div><div>+    GObject *bearer;</div><div>+    GObject *source;</div><div>+</div><div>+    source = g_async_result_get_source_object (res);</div><div>+    bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error);</div><div>+    g_object_unref (source);</div><div>+</div><div>+    if (!bearer)</div><div>+        return NULL;</div><div>+</div><div>+    /* Only export valid bearers */</div><div>+    mm_base_bearer_export (MM_BASE_BEARER (bearer));</div><div>+</div><div>+    return MM_BASE_BEARER (bearer);</div><div>+}</div><div>+</div><div>+static void</div><div>+dispose (GObject *object)</div><div>+{</div><div>+    MMBroadbandBearerCinterion *self = MM_BROADBAND_BEARER_CINTERION (object);</div><div>+</div><div>+    if (self->priv->network_disconnect_pending_id != 0) {</div><div>+        g_source_remove (self->priv->network_disconnect_pending_id);</div><div>+        self->priv->network_disconnect_pending_id = 0;</div><div>+    }</div><div>+</div><div>+    G_OBJECT_CLASS (mm_broadband_bearer_cinterion_parent_class)->dispose (object);</div><div>+}</div><div>+</div><div>+void</div><div>+mm_broadband_bearer_cinterion_new (MMBroadbandModemCinterion *modem,</div><div>+                                MMBearerProperties *config,</div><div>+                                GCancellable *cancellable,</div><div>+                                GAsyncReadyCallback callback,</div><div>+                                gpointer user_data)</div><div>+{</div><div>+    g_async_initable_new_async (</div><div>+        MM_TYPE_BROADBAND_BEARER_CINTERION,</div><div>+        G_PRIORITY_DEFAULT,</div><div>+        cancellable,</div><div>+        callback,</div><div>+        user_data,</div><div>+        MM_BASE_BEARER_MODEM, modem,</div><div>+        MM_BASE_BEARER_CONFIG, config,</div><div>+        NULL);</div><div>+}</div><div>+</div><div>+static void</div><div>+mm_broadband_bearer_cinterion_init (MMBroadbandBearerCinterion *self)</div><div>+{</div><div>+    // Initialize private data</div><div>+    self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,</div><div>+                                              MM_TYPE_BROADBAND_BEARER_CINTERION,</div><div>+                                              MMBroadbandBearerCinterionPrivate);</div><div>+}</div><div>+</div><div>+static void</div><div>+mm_broadband_bearer_cinterion_class_init (MMBroadbandBearerCinterionClass *klass)</div><div>+{</div><div>+    GObjectClass *object_class = G_OBJECT_CLASS (klass);</div><div>+    MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass);</div><div>+    MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);</div><div>+</div><div>+    g_type_class_add_private (object_class, sizeof (MMBroadbandBearerCinterionPrivate));</div><div>+</div><div>+    object_class->dispose = dispose;</div><div>+    base_bearer_class->report_connection_status = report_connection_status; //TODO:What triggers this?</div><div>+</div><div>+    broadband_bearer_class->connect_3gpp = connect_3gpp;</div><div>+    broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish;</div><div>+    broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;</div><div>+    broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish;</div><div>+}</div><div>diff --git a/plugins/cinterion/mm-broadband-bearer-cinterion.h b/plugins/cinterion/mm-broadband-bearer-cinterion.h</div><div>new file mode 100644</div><div>index 0000000..8f315db</div><div>--- /dev/null</div><div>+++ b/plugins/cinterion/mm-broadband-bearer-cinterion.h</div><div>@@ -0,0 +1,56 @@</div><div>+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */</div><div>+/*</div><div>+ * This program is free software; you can redistribute it and/or modify</div><div>+ * it under the terms of the GNU General Public License as published by</div><div>+ * the Free Software Foundation; either version 2 of the License, or</div><div>+ * (at your option) any later version.</div><div>+ *</div><div>+ * This program is distributed in the hope that it will be useful,</div><div>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</div><div>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</div><div>+ * GNU General Public License for more details:</div><div>+ *</div><div>+ * Copyright (C) 2016 Trimble Navigation Limited</div><div>+ * Author: Matthew Stanger <<a href="mailto:Matthew_Stanger@trimble.com">Matthew_Stanger@trimble.com</a>></div><div>+ */</div><div>+</div><div>+#ifndef MM_BROADBAND_BEARER_CINTERION_H</div><div>+#define MM_BROADBAND_BEARER_CINTERION_H</div><div>+</div><div>+#include <glib.h></div><div>+#include <glib-object.h></div><div>+</div><div>+#include "mm-broadband-bearer.h"</div><div>+#include "mm-broadband-modem-cinterion.h"</div><div>+</div><div>+#define MM_TYPE_BROADBAND_BEARER_CINTERION                (mm_broadband_bearer_cinterion_get_type ())</div><div>+#define MM_BROADBAND_BEARER_CINTERION(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_CINTERION, MMBroadbandBearerCinterion))</div><div>+#define MM_BROADBAND_BEARER_CINTERION_CLASS(klass)        (G_TYPE_CHECK_CLASS_CAST ((klass),  MM_TYPE_BROADBAND_BEARER_CINTERION, MMBroadbandBearerCinterionClass))</div><div>+#define MM_IS_BROADBAND_BEARER_CINTERION(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_CINTERION))</div><div>+#define MM_IS_BROADBAND_BEARER_CINTERION_CLASS(klass)     (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_BROADBAND_BEARER_CINTERION))</div><div>+#define MM_BROADBAND_BEARER_CINTERION_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_BROADBAND_BEARER_CINTERION, MMBroadbandBearerCinterionClass))</div><div>+</div><div>+typedef struct _MMBroadbandBearerCinterion MMBroadbandBearerCinterion;</div><div>+typedef struct _MMBroadbandBearerCinterionClass MMBroadbandBearerCinterionClass;</div><div>+typedef struct _MMBroadbandBearerCinterionPrivate MMBroadbandBearerCinterionPrivate;</div><div>+</div><div>+struct _MMBroadbandBearerCinterion {</div><div>+    MMBroadbandBearer parent;</div><div>+    MMBroadbandBearerCinterionPrivate *priv;</div><div>+};</div><div>+</div><div>+struct _MMBroadbandBearerCinterionClass {</div><div>+    MMBroadbandBearerClass parent;</div><div>+};</div><div>+</div><div>+GType mm_broadband_bearer_cinterion_get_type (void);</div><div>+</div><div>+void          mm_broadband_bearer_cinterion_new        (MMBroadbandModemCinterion *modem,</div><div>+                                                     MMBearerProperties *config,</div><div>+                                                     GCancellable *cancellable,</div><div>+                                                     GAsyncReadyCallback callback,</div><div>+                                                     gpointer user_data);</div><div>+MMBaseBearer *mm_broadband_bearer_cinterion_new_finish (GAsyncResult *res,</div><div>+                                                     GError **error);</div><div>+</div><div>+#endif /* MM_BROADBAND_BEARER_CINTERION_H */</div><div>diff --git a/plugins/cinterion/mm-broadband-modem-cinterion.c b/plugins/cinterion/mm-broadband-modem-cinterion.c</div><div>index 4882a41..ac1c5f5 100644</div><div>--- a/plugins/cinterion/mm-broadband-modem-cinterion.c</div><div>+++ b/plugins/cinterion/mm-broadband-modem-cinterion.c</div><div>@@ -12,7 +12,9 @@</div><div>  *</div><div>  * Copyright (C) 2011 Ammonit Measurement GmbH</div><div>  * Copyright (C) 2011 Google Inc.</div><div>+ * Copyright (C) 2016 Trimble Navigation Limited</div><div>  * Author: Aleksander Morgado <<a href="mailto:aleksander@lanedo.com">aleksander@lanedo.com</a>></div><div>+ * Contributor: Matthew Stanger <<a href="mailto:matthew_stanger@trimble.com">matthew_stanger@trimble.com</a>></div><div>  */</div><div><br></div><div> #include <config.h></div><div>@@ -22,6 +24,8 @@</div><div> #include <string.h></div><div> #include <unistd.h></div><div> #include <ctype.h></div><div>+#include <gudev/gudev.h></div><div>+</div><div><br></div><div> #include "ModemManager.h"</div><div> #include "mm-modem-helpers.h"</div><div>@@ -36,6 +40,7 @@</div><div> #include "mm-broadband-modem-cinterion.h"</div><div> #include "mm-modem-helpers-cinterion.h"</div><div> #include "mm-common-cinterion.h"</div><div>+#include "mm-broadband-bearer-cinterion.h"</div><div><br></div><div> static void iface_modem_init      (MMIfaceModem *iface);</div><div> static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);</div><div>@@ -50,6 +55,12 @@ G_DEFINE_TYPE_EXTENDED (MMBroadbandModemCinterion, mm_broadband_modem_cinterion,</div><div>                         G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init)</div><div>                         G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init))</div><div><br></div><div>+typedef enum {</div><div>+    FEATURE_SUPPORT_UNKNOWN,</div><div>+    FEATURE_NOT_SUPPORTED,</div><div>+    FEATURE_SUPPORTED</div><div>+} FeatureSupport;</div><div>+</div><div> struct _MMBroadbandModemCinterionPrivate {</div><div>     /* Flag to know if we should try AT^SIND or not to get psinfo */</div><div>     gboolean sind_psinfo;</div><div>@@ -69,6 +80,9 @@ struct _MMBroadbandModemCinterionPrivate {</div><div>     GArray *cnmi_supported_bm;</div><div>     GArray *cnmi_supported_ds;</div><div>     GArray *cnmi_supported_bfr;</div><div>+</div><div>+    FeatureSupport swwan_support;</div><div>+</div><div> };</div><div><br></div><div> /*****************************************************************************/</div><div>@@ -117,7 +131,7 @@ cnmi_test_ready (MMBaseModem *self,</div><div> {</div><div>     GError *error = NULL;</div><div><br></div><div>-    mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);</div><div>+     mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);</div><div>     if (error)</div><div>         g_simple_async_result_take_error (simple, error);</div><div>     else</div><div>@@ -727,10 +741,16 @@ get_access_technology_from_psinfo (const gchar *psinfo,</div><div>         case 9:</div><div>         case 10:</div><div>             return (MM_MODEM_ACCESS_TECHNOLOGY_HSDPA | MM_MODEM_ACCESS_TECHNOLOGY_HSUPA);</div><div>+        case 16:</div><div>+        case 17:</div><div>+            return MM_MODEM_ACCESS_TECHNOLOGY_LTE;</div><div>         default:</div><div>+            mm_dbg ("Unable to identify access technology in case:%i", psinfoval);</div><div>             break;</div><div>         }</div><div>     }</div><div>+    else</div><div>+        mm_err ("FAILED get_access_technology_from_psinfo-int");</div><div><br></div><div>     g_set_error (error,</div><div>                  MM_CORE_ERROR,</div><div>@@ -1648,6 +1668,259 @@ setup_ports (MMBroadbandModem *self)</div><div> }</div><div><br></div><div> /*****************************************************************************/</div><div>+/* Create Bearer (Modem interface) */</div><div>+</div><div>+typedef struct {</div><div>+    MMBroadbandModemCinterion *self;</div><div>+    GSimpleAsyncResult *result;</div><div>+    MMBearerProperties *properties;</div><div>+} CreateBearerContext;</div><div>+</div><div>+static void</div><div>+create_bearer_context_complete_and_free (CreateBearerContext *ctx)</div><div>+{</div><div>+    g_simple_async_result_complete (ctx->result);</div><div>+    g_object_unref (ctx->result);</div><div>+    g_object_unref (ctx->self);</div><div>+    g_object_unref (ctx->properties);</div><div>+    g_slice_free (CreateBearerContext, ctx);</div><div>+}</div><div>+</div><div>+static MMBaseBearer *</div><div>+cinterion_modem_create_bearer_finish (MMIfaceModem *self,</div><div>+                                   GAsyncResult *res,</div><div>+                                   GError **error)</div><div>+{</div><div>+    MMBaseBearer *bearer;</div><div>+</div><div>+    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))</div><div>+        return NULL;</div><div>+</div><div>+    bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));</div><div>+    mm_dbg ("New cinterion bearer created at DBus path '%s'", mm_base_bearer_get_path (bearer));</div><div>+    return g_object_ref (bearer);</div><div>+}</div><div>+</div><div>+static void</div><div>+broadband_bearer_cinterion_new_ready (GObject *source,</div><div>+                                   GAsyncResult *res,</div><div>+                                   CreateBearerContext *ctx)</div><div>+{</div><div>+    MMBaseBearer *bearer;</div><div>+    GError *error = NULL;</div><div>+</div><div>+    bearer = mm_broadband_bearer_cinterion_new_finish (res, &error);</div><div>+    if (!bearer)</div><div>+        g_simple_async_result_take_error (ctx->result, error);</div><div>+    else</div><div>+        g_simple_async_result_set_op_res_gpointer (ctx->result, bearer, (GDestroyNotify)g_object_unref);</div><div>+    create_bearer_context_complete_and_free (ctx);</div><div>+}</div><div>+</div><div>+static void</div><div>+broadband_bearer_new_ready (GObject *source,</div><div>+                            GAsyncResult *res,</div><div>+                            CreateBearerContext *ctx)</div><div>+{</div><div>+    MMBaseBearer *bearer;</div><div>+    GError *error = NULL;</div><div>+</div><div>+    bearer = mm_broadband_bearer_new_finish (res, &error);</div><div>+    if (!bearer)</div><div>+        g_simple_async_result_take_error (ctx->result, error);</div><div>+    else</div><div>+        g_simple_async_result_set_op_res_gpointer (ctx->result, bearer, (GDestroyNotify)g_object_unref);</div><div>+    create_bearer_context_complete_and_free (ctx);</div><div>+}</div><div>+</div><div>+static void</div><div>+create_bearer_for_net_port (CreateBearerContext *ctx)</div><div>+{</div><div>+    switch (ctx->self->priv->swwan_support) {</div><div>+    case FEATURE_SUPPORT_UNKNOWN:</div><div>+        g_assert_not_reached ();</div><div>+    case FEATURE_NOT_SUPPORTED:</div><div>+        mm_dbg ("^SWWAN not supported, creating default bearer...");</div><div>+        mm_broadband_bearer_new (MM_BROADBAND_MODEM (ctx->self),</div><div>+                                 ctx->properties,</div><div>+                                 NULL, /* cancellable */</div><div>+                                 (GAsyncReadyCallback)broadband_bearer_new_ready,</div><div>+                                 ctx);</div><div>+        return;</div><div>+    case FEATURE_SUPPORTED:</div><div>+        mm_dbg ("^SWWAN supported, creating cinterion bearer...");</div><div>+        mm_broadband_bearer_cinterion_new (MM_BROADBAND_MODEM_CINTERION (ctx->self),</div><div>+                                        ctx->properties,</div><div>+                                        NULL, /* cancellable */</div><div>+                                        (GAsyncReadyCallback)broadband_bearer_cinterion_new_ready,</div><div>+                                        ctx);</div><div>+        return;</div><div>+    }</div><div>+}</div><div>+</div><div>+static MMPortSerialAt *</div><div>+peek_port_at_for_data (MMBroadbandModemCinterion *self,</div><div>+                       MMPort *port)</div><div>+{</div><div>+    GList *cdc_wdm_at_ports, *l;</div><div>+    const gchar *net_port_parent_path;</div><div>+</div><div>+    g_warn_if_fail (mm_port_get_subsys (port) == MM_PORT_SUBSYS_NET);</div><div>+    net_port_parent_path = mm_port_get_parent_path (port);</div><div>+</div><div>+    if (!net_port_parent_path) {</div><div>+        g_warning ("(%s) no parent path for net port", mm_port_get_device (port));</div><div>+        return NULL;</div><div>+    }</div><div>+</div><div>+    //Find the port to send AT commands on...</div><div>+    cdc_wdm_at_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),</div><div>+                                                 MM_PORT_SUBSYS_USB,</div><div>+                                                 MM_PORT_TYPE_NET,</div><div>+                                                 NULL);</div><div>+</div><div>+</div><div>+    for (l = cdc_wdm_at_ports; l; l = l->next) {</div><div>+        const gchar  *wdm_port_parent_path;</div><div>+</div><div>+        g_assert (MM_IS_PORT_SERIAL_AT (l->data));</div><div>+        wdm_port_parent_path = mm_port_get_parent_path (MM_PORT (l->data));</div><div>+</div><div>+        if (wdm_port_parent_path && g_str_equal (wdm_port_parent_path, net_port_parent_path))</div><div>+           return MM_PORT_SERIAL_AT (l->data);</div><div>+    }</div><div>+</div><div>+    return NULL;</div><div>+}</div><div>+</div><div>+MMPortSerialAt *</div><div>+mm_broadband_modem_cinterion_peek_port_at_for_data (MMBroadbandModemCinterion *self,</div><div>+                                                 MMPort *port)</div><div>+{</div><div>+    MMPortSerialAt *found;</div><div>+</div><div>+    g_assert (self->priv->swwan_support == FEATURE_SUPPORTED);</div><div>+</div><div>+    found = peek_port_at_for_data (self, port);</div><div>+    if (!found)</div><div>+        mm_warn ("Couldn't find associated cdc-wdm port for 'net/%s'",</div><div>+                 mm_port_get_device (port));</div><div>+    return found;</div><div>+}</div><div>+</div><div>+static void</div><div>+ensure_swwan_support_checked (MMBroadbandModemCinterion *self,</div><div>+                                MMPort *port)</div><div>+{</div><div>+    GUdevClient *client;</div><div>+    GUdevDevice *data_device;</div><div>+</div><div>+    /* Initialize the swwan feature */</div><div>+    if (self->priv->swwan_support != FEATURE_SUPPORT_UNKNOWN)</div><div>+        return;</div><div>+</div><div>+    /* First, check for devices which could support SWWAN. They</div><div>+     * will be tagged by udev as net devices. */</div><div>+    client = g_udev_client_new (NULL);</div><div>+    data_device = (g_udev_client_query_by_subsystem_and_name (</div><div>+                       client,</div><div>+                       "net",</div><div>+                       mm_port_get_device (port)));</div><div>+</div><div>+    /* udevadm info for Cinterion PLS8-X v3.017 Modem</div><div>+        P: /devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.10/net/usb0</div><div>+        E: DEVPATH=/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.10/net/usb0</div><div>+        E: ID_BUS=usb</div><div>+        E: ID_MM_CANDIDATE=1</div><div>+        E: ID_MODEL=LTE_Modem</div><div>+        E: ID_MODEL_ENC=LTE\x20Modem</div><div>+        E: ID_MODEL_FROM_DATABASE=2.0 root hub</div><div>+        E: ID_MODEL_ID=0061</div><div>+        E: ID_NET_NAME_MAC=enxdeadbeef0000</div><div>+        E: ID_NET_NAME_PATH=enp0s20u2i10</div><div>+        E: ID_REVISION=0232</div><div>+        E: ID_SERIAL=Cinterion_LTE_Modem</div><div>+        E: ID_TYPE=generic</div><div>+            E: ID_USB_DRIVER=cdc_ether</div><div>+        E: ID_USB_INTERFACES=:020201:0a0000:020600:</div><div>+        E: ID_USB_INTERFACE_NUM=0a</div><div>+        E: ID_VENDOR=Cinterion</div><div>+        E: ID_VENDOR_ENC=Cinterion</div><div>+        E: ID_VENDOR_FROM_DATABASE=Linux Foundation</div><div>+        E: ID_VENDOR_ID=1e2d</div><div>+        E: IFINDEX=11</div><div>+        E: INTERFACE=usb0</div><div>+        E: SUBSYSTEM=net</div><div>+        E: USEC_INITIALIZED=4142603761</div><div>+</div><div>+        P: /devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.11</div><div>+        E: DEVPATH=/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.11</div><div>+        E: DEVTYPE=usb_interface</div><div>+        E: DRIVER=cdc_ether</div><div>+        E: ID_MODEL_FROM_DATABASE=2.0 root hub</div><div>+        E: ID_VENDOR_FROM_DATABASE=Linux Foundation</div><div>+        E: INTERFACE=10/0/0</div><div>+        E: MODALIAS=usb:v1E2Dp0061d0232dc00dsc00dp00ic0Aisc00ip00in0B</div><div>+        E: PRODUCT=1e2d/61/232</div><div>+        E: SUBSYSTEM=usb</div><div>+        E: TYPE=0/0/0</div><div>+        E: USEC_INITIALIZED=604142604196</div><div>+    */</div><div>+</div><div>+    //Assumption - Cinterion modems that use cdc_ether will support the swwan via usb ethernet.</div><div>+    //Cinterion alluded to this idea when I spoke with their engineering support but stopped short</div><div>+    //of being able to confirm if all future/current models will be this way.</div><div>+    if (data_device && g_str_equal(g_udev_device_get_property(data_device, "ID_USB_DRIVER"),"cdc_ether")) {</div><div>+        mm_dbg ("This device (%s) can support swwan feature", mm_port_get_device (port));</div><div>+        self->priv->swwan_support = FEATURE_SUPPORTED;</div><div>+    }</div><div>+    else</div><div>+    {</div><div>+        self->priv->swwan_support = FEATURE_NOT_SUPPORTED;</div><div>+        mm_dbg ("This device (%s) can not support swwan feature", mm_port_get_device (port));</div><div>+    }</div><div>+</div><div>+    /* Free the g_object*/</div><div>+    if (data_device)</div><div>+        g_object_unref (data_device);</div><div>+    if (client)</div><div>+        g_object_unref (client);</div><div>+}</div><div>+</div><div>+static void</div><div>+cinterion_modem_create_bearer (MMIfaceModem *self,</div><div>+                            MMBearerProperties *properties,</div><div>+                            GAsyncReadyCallback callback,</div><div>+                            gpointer user_data)</div><div>+{</div><div>+    CreateBearerContext *ctx;</div><div>+    MMPort *port;</div><div>+</div><div>+    ctx = g_slice_new0 (CreateBearerContext);</div><div>+    ctx->self = g_object_ref (self);</div><div>+    ctx->properties = g_object_ref (properties);</div><div>+    ctx->result = g_simple_async_result_new (G_OBJECT (self),</div><div>+                                             callback,</div><div>+                                             user_data,</div><div>+                                             cinterion_modem_create_bearer);</div><div>+</div><div>+    port = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (self), MM_PORT_TYPE_NET);</div><div>+</div><div>+    if (port) {</div><div>+        ensure_swwan_support_checked (ctx->self, port);</div><div>+        create_bearer_for_net_port (ctx);</div><div>+        return;</div><div>+    }</div><div>+</div><div>+    mm_dbg ("Creating default bearer...");</div><div>+    mm_broadband_bearer_new (MM_BROADBAND_MODEM (self),</div><div>+                             properties,</div><div>+                             NULL, /* cancellable */</div><div>+                             (GAsyncReadyCallback)broadband_bearer_new_ready,</div><div>+                             ctx);</div><div>+}</div><div>+</div><div>+/*****************************************************************************/</div><div><br></div><div> MMBroadbandModemCinterion *</div><div> mm_broadband_modem_cinterion_new (const gchar *device,</div><div>@@ -1675,6 +1948,7 @@ mm_broadband_modem_cinterion_init (MMBroadbandModemCinterion *self)</div><div><br></div><div>     /* Set defaults */</div><div>     self->priv->sind_psinfo = TRUE; /* Initially, always try to get psinfo */</div><div>+    self->priv->swwan_support = FEATURE_SUPPORT_UNKNOWN;</div><div> }</div><div><br></div><div> static void</div><div>@@ -1683,7 +1957,7 @@ finalize (GObject *object)</div><div>     MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (object);</div><div><br></div><div>     g_free (self->priv->sleep_mode_cmd);</div><div>-    g_free (self->priv->manual_operator_id);</div><div>+    g_free (self->priv->manual_operator_id);</div><div><br></div><div>     if (self->priv->cnmi_supported_mode)</div><div>         g_array_unref (self->priv->cnmi_supported_mode);</div><div>@@ -1704,6 +1978,8 @@ iface_modem_init (MMIfaceModem *iface)</div><div> {</div><div>     iface_modem_parent = g_type_interface_peek_parent (iface);</div><div><br></div><div>+    iface->create_bearer = cinterion_modem_create_bearer;</div><div>+    iface->create_bearer_finish = cinterion_modem_create_bearer_finish;</div><div>     iface->load_supported_modes = load_supported_modes;</div><div>     iface->load_supported_modes_finish = load_supported_modes_finish;</div><div>     iface->set_current_modes = set_current_modes;</div><div>@@ -1764,7 +2040,6 @@ mm_broadband_modem_cinterion_class_init (MMBroadbandModemCinterionClass *klass)</div><div> {</div><div>     GObjectClass *object_class = G_OBJECT_CLASS (klass);</div><div>     MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);</div><div>-</div><div>     g_type_class_add_private (object_class, sizeof (MMBroadbandModemCinterionPrivate));</div><div><br></div><div>     /* Virtual methods */</div><div>diff --git a/plugins/cinterion/mm-broadband-modem-cinterion.h b/plugins/cinterion/mm-broadband-modem-cinterion.h</div><div>index 47f0dcb..b675f3d 100644</div><div>--- a/plugins/cinterion/mm-broadband-modem-cinterion.h</div><div>+++ b/plugins/cinterion/mm-broadband-modem-cinterion.h</div><div>@@ -48,4 +48,7 @@ MMBroadbandModemCinterion *mm_broadband_modem_cinterion_new (const gchar *device</div><div>                                                              guint16 vendor_id,</div><div>                                                              guint16 product_id);</div><div><br></div><div>+MMPortSerialAt *mm_broadband_modem_cinterion_peek_port_at_for_data (MMBroadbandModemCinterion *self,</div><div>+                                                                 MMPort *port);</div><div>+</div><div> #endif /* MM_BROADBAND_MODEM_CINTERION_H */</div><div>diff --git a/plugins/cinterion/mm-common-cinterion.c b/plugins/cinterion/mm-common-cinterion.c</div><div>index 6e4da70..79f8384 100644</div><div>--- a/plugins/cinterion/mm-common-cinterion.c</div><div>+++ b/plugins/cinterion/mm-common-cinterion.c</div><div>@@ -11,13 +11,15 @@</div><div>  * GNU General Public License for more details:</div><div>  *</div><div>  * Copyright (C) 2014 Ammonit Measurement GmbH</div><div>+ * Copyright (C) 2016 Trimble Navigation Limited</div><div>  * Author: Aleksander Morgado <<a href="mailto:aleksander@aleksander.es">aleksander@aleksander.es</a>></div><div>+ * Contributor: Matthew Stanger <<a href="mailto:matthew_stanger@trimble.com">matthew_stanger@trimble.com</a>></div><div>  */</div><div><br></div><div> #include "mm-common-cinterion.h"</div><div> #include "mm-base-modem-at.h"</div><div>+#include "mm-log.h"</div><div><br></div><div>-static MMIfaceModemLocation *iface_modem_location_parent;</div><div><br></div><div> /*****************************************************************************/</div><div><br></div><div>@@ -25,11 +27,40 @@ static MMIfaceModemLocation *iface_modem_location_parent;</div><div> static GQuark cinterion_location_context_quark;</div><div><br></div><div> /*****************************************************************************/</div><div>+/* Def's and Enum's */</div><div>+</div><div>+typedef enum {</div><div>+    GPS_CONTEXT_STEP_SGPSS = 0,</div><div>+    GPS_CONTEXT_STEP_SGPSC_ANTENNA,</div><div>+    GPS_CONTEXT_STEP_SGPSC_ENGINE,</div><div>+    GPS_CONTEXT_STEP_SGPSC_OUTPUT,</div><div>+    GPS_CONTEXT_STEP_DONE,</div><div>+} ConnectGpsContextStep;</div><div>+</div><div>+typedef struct {</div><div>+    MMBaseModem *self;</div><div>+    GSimpleAsyncResult *result;</div><div>+    MMModemLocationSource source;</div><div>+    ConnectGpsContextStep enable_state;</div><div>+    ConnectGpsContextStep disable_state;</div><div>+    gpointer connect_pending;</div><div>+    gpointer disconnect_pending;</div><div>+    gint gps_retry;</div><div>+} LocationGatheringContext;</div><div><br></div><div> typedef struct {</div><div>     MMModemLocationSource enabled_sources;</div><div> } LocationContext;</div><div><br></div><div>+static MMIfaceModemLocation *iface_modem_location_parent;</div><div>+static void try_gps_enable (MMIfaceModemLocation *self,</div><div>+                LocationGatheringContext *ctx);</div><div>+static void try_gps_disable (MMIfaceModemLocation *self,</div><div>+                LocationGatheringContext *ctx);</div><div>+static void send_gps_command (MMIfaceModemLocation *self,</div><div>+                              LocationGatheringContext *ctx,</div><div>+                              gchar **command);</div><div>+</div><div> static void</div><div> location_context_free (LocationContext *ctx)</div><div> {</div><div>@@ -127,13 +158,7 @@ mm_common_cinterion_location_load_capabilities (MMIfaceModemLocation *self,</div><div> }</div><div><br></div><div> /*****************************************************************************/</div><div>-/* Enable/Disable location gathering (Location interface) */</div><div>-</div><div>-typedef struct {</div><div>-    MMBaseModem *self;</div><div>-    GSimpleAsyncResult *result;</div><div>-    MMModemLocationSource source;</div><div>-} LocationGatheringContext;</div><div>+/* Common location gathering (Location interface) */</div><div><br></div><div> static void</div><div> location_gathering_context_complete_and_free (LocationGatheringContext *ctx)</div><div>@@ -144,6 +169,145 @@ location_gathering_context_complete_and_free (LocationGatheringContext *ctx)</div><div>     g_slice_free (LocationGatheringContext, ctx);</div><div> }</div><div><br></div><div>+static void</div><div>+location_gathering_context_complete_and_free_full (LocationGatheringContext *ctx)</div><div>+{</div><div>+    //It's importiant that this parent function remains protected by calls where</div><div>+    //both context '*_pending' pointers could be active.</div><div>+    if (ctx->connect_pending != NULL)</div><div>+        ctx->connect_pending = NULL;</div><div>+    else</div><div>+        ctx->disconnect_pending = NULL;</div><div>+</div><div>+    location_gathering_context_complete_and_free(ctx);</div><div>+}</div><div>+</div><div>+static void</div><div>+promote_gps_state_from_response (MMBaseModem *self,</div><div>+                       GAsyncResult *res,</div><div>+                       LocationGatheringContext *ctx)</div><div>+{</div><div>+    GError *error = NULL;</div><div>+    ConnectGpsContextStep state;</div><div>+    gchar *result;</div><div>+</div><div>+    //Adjust for whether we're dis/en-ableing</div><div>+    if (ctx->connect_pending != NULL)</div><div>+        state = ctx->enable_state;</div><div>+    else</div><div>+    {</div><div>+        g_assert(ctx->disconnect_pending != NULL);</div><div>+        state = ctx->disable_state;</div><div>+    }</div><div>+</div><div>+    //We see that the 'Engine,1' command (PLS8) fails for no apparent reason</div><div>+    //on ATMEL AT91SAM9263 ARM & not x86. See 'mm_common_cinterion_setup_gps_port'</div><div>+    //comments.</div><div>+    result = g_strdup (mm_base_modem_at_command_full_finish (self, res, &error));</div><div>+</div><div>+    //If we get an error don't progress at first, try the same state again.</div><div>+    if ((!result && state != GPS_CONTEXT_STEP_SGPSS && ctx-> gps_retry < 3))</div><div>+    {</div><div>+        state = GPS_CONTEXT_STEP_SGPSS;</div><div>+        ctx->gps_retry++;</div><div>+    }</div><div>+    else {</div><div>+</div><div>+</div><div>+    switch (state) {</div><div>+</div><div>+    //First step try's the legacy SGPSS command. If it fails don't error out</div><div>+    //continue on to send new Cinterion GPS commands and see if they work.</div><div>+    case GPS_CONTEXT_STEP_SGPSS: {</div><div>+        if (!result) {</div><div>+            mm_info ("SGPSS command failed, will try another Cinterion GPS command.");</div><div>+            state = GPS_CONTEXT_STEP_SGPSC_ANTENNA;</div><div>+        }</div><div>+        else</div><div>+            state = GPS_CONTEXT_STEP_DONE;</div><div>+</div><div>+        break;</div><div>+    }</div><div>+    //If the new GPS enable comand fails then exit we are out of things to try.</div><div>+    //Only connect flow cares about errors.</div><div>+    case GPS_CONTEXT_STEP_SGPSC_ANTENNA: {</div><div>+        if (!result && ctx->disconnect_pending == NULL) {</div><div>+            mm_info ("SGPSC command failed, we are out of things to try.");</div><div>+            g_simple_async_result_take_error (ctx->result, error);</div><div>+            location_gathering_context_complete_and_free_full (ctx);</div><div>+            return;</div><div>+        }</div><div>+        else</div><div>+             state = GPS_CONTEXT_STEP_SGPSC_ENGINE;</div><div>+</div><div>+        break;</div><div>+    }</div><div>+    case GPS_CONTEXT_STEP_SGPSC_ENGINE: {</div><div>+        if (!result && ctx->disconnect_pending == NULL) {</div><div>+            g_simple_async_result_take_error (ctx->result, error);</div><div>+            location_gathering_context_complete_and_free_full (ctx);</div><div>+            return;</div><div>+        }</div><div>+        else</div><div>+            state = GPS_CONTEXT_STEP_SGPSC_OUTPUT;</div><div>+</div><div>+        break;</div><div>+    }</div><div>+    case GPS_CONTEXT_STEP_SGPSC_OUTPUT: {</div><div>+        if (!result && ctx->disconnect_pending == NULL) {</div><div>+            g_simple_async_result_take_error (ctx->result, error);</div><div>+            location_gathering_context_complete_and_free_full (ctx);</div><div>+            return;</div><div>+        }</div><div>+        else</div><div>+            state = GPS_CONTEXT_STEP_DONE;</div><div>+</div><div>+        break;</div><div>+    }</div><div>+    case GPS_CONTEXT_STEP_DONE: {</div><div>+        //Shouldn't get here, fall into default error.</div><div>+    }</div><div>+    default: {</div><div>+        g_simple_async_result_set_error (ctx->result,</div><div>+                                         MM_CORE_ERROR,</div><div>+                                         MM_CORE_ERROR_WRONG_STATE,</div><div>+                                         "Unknown gps state during setup.");</div><div>+</div><div>+        location_gathering_context_complete_and_free_full (ctx);</div><div>+        g_free(result);</div><div>+        return;</div><div>+    }</div><div>+    }</div><div>+    }</div><div>+</div><div>+    //Save state and go back to dis/en-ableing</div><div>+    if (ctx->connect_pending != NULL) {</div><div>+        ctx->enable_state = state;</div><div>+        try_gps_enable(ctx->connect_pending, ctx);</div><div>+    }</div><div>+    else {</div><div>+        ctx->disable_state = state;</div><div>+        try_gps_disable(ctx->disconnect_pending, ctx);</div><div>+    }</div><div>+    g_free(result);</div><div>+}</div><div>+</div><div>+static void</div><div>+send_gps_command (MMIfaceModemLocation *self,</div><div>+                  LocationGatheringContext *ctx,</div><div>+                  gchar **command)</div><div>+{</div><div>+    mm_base_modem_at_command_full (MM_BASE_MODEM (self),</div><div>+                                   mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL),</div><div>+                                   *command,</div><div>+                                   5,</div><div>+                                   FALSE,</div><div>+                                   FALSE, /* raw */</div><div>+                                   NULL, /* cancellable */</div><div>+                                   (GAsyncReadyCallback)promote_gps_state_from_response,</div><div>+                                   ctx);</div><div>+}</div><div>+</div><div> /******************************/</div><div> /* Disable location gathering */</div><div><br></div><div>@@ -157,15 +321,9 @@ mm_common_cinterion_disable_location_gathering_finish (MMIfaceModemLocation *sel</div><div><br></div><div> static void</div><div> gps_disabled_ready (MMBaseModem *self,</div><div>-                    GAsyncResult *res,</div><div>                     LocationGatheringContext *ctx)</div><div> {</div><div>-    GError *error = NULL;</div><div>-</div><div>-    if (!mm_base_modem_at_command_full_finish (self, res, &error))</div><div>-        g_simple_async_result_take_error (ctx->result, error);</div><div>-    else</div><div>-        g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);</div><div>+    g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);</div><div><br></div><div>     /* Only use the GPS port in NMEA/RAW setups */</div><div>     if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |</div><div>@@ -178,11 +336,57 @@ gps_disabled_ready (MMBaseModem *self,</div><div>             mm_port_serial_close (MM_PORT_SERIAL (gps_port));</div><div>     }</div><div><br></div><div>-    location_gathering_context_complete_and_free (ctx);</div><div>+    location_gathering_context_complete_and_free_full (ctx);</div><div>+}</div><div>+</div><div>+static void</div><div>+try_gps_disable (MMIfaceModemLocation *self,</div><div>+                LocationGatheringContext *ctx)</div><div>+{</div><div>+    gchar *gps_command;</div><div>+</div><div>+    switch (ctx->disable_state) {</div><div>+</div><div>+    //The old command to enable GPS, works on at least PSX8</div><div>+    case GPS_CONTEXT_STEP_SGPSS: {</div><div>+        gps_command = "^SGPSS=0";</div><div>+        break;</div><div>+    }</div><div>+    //Commands Power/Ant, Engine & Output w/ sgpsc are</div><div>+    //used by PLS8-X & E. Since around versions 2.0+</div><div>+    case GPS_CONTEXT_STEP_SGPSC_ANTENNA: {</div><div>+        gps_command = "^SGPSC=\"Power/Antenna\",\"off\"";</div><div>+        break;</div><div>+    }</div><div>+    case GPS_CONTEXT_STEP_SGPSC_ENGINE: {</div><div>+        gps_command = "^SGPSC=\"Engine\",\"0\"";</div><div>+        break;</div><div>+    }</div><div>+    case GPS_CONTEXT_STEP_SGPSC_OUTPUT: {</div><div>+        gps_command = "^SGPSC=\"NMEA/Output\",\"off\"";</div><div>+        break;</div><div>+    }</div><div>+    case GPS_CONTEXT_STEP_DONE: {</div><div>+        gps_disabled_ready(MM_BASE_MODEM (self), ctx);</div><div>+        return;</div><div>+    }</div><div>+    default: {</div><div>+        g_simple_async_result_set_error (ctx->result,</div><div>+                                         MM_CORE_ERROR,</div><div>+                                         MM_CORE_ERROR_WRONG_STATE,</div><div>+                                         "Unknown gps state during tear down.");</div><div>+</div><div>+        location_gathering_context_complete_and_free_full (ctx);</div><div>+        return;</div><div>+    }</div><div>+    }</div><div>+</div><div>+    send_gps_command(self, ctx, &gps_command);</div><div>+    return;</div><div> }</div><div><br></div><div> static void</div><div>-internal_disable_location_gathering (LocationGatheringContext *ctx)</div><div>+internal_disable_location_gathering (MMIfaceModemLocation *self, LocationGatheringContext *ctx)</div><div> {</div><div>     LocationContext *location_ctx;</div><div>     gboolean stop_gps = FALSE;</div><div>@@ -202,21 +406,30 @@ internal_disable_location_gathering (LocationGatheringContext *ctx)</div><div>     }</div><div><br></div><div>     if (stop_gps) {</div><div>-        /* We disable continuous GPS fixes */</div><div>-        mm_base_modem_at_command_full (MM_BASE_MODEM (ctx->self),</div><div>-                                       mm_base_modem_peek_best_at_port (MM_BASE_MODEM (ctx->self), NULL),</div><div>-                                       "AT^SGPSS=0",</div><div>-                                       3,</div><div>-                                       FALSE,</div><div>-                                       FALSE, /* raw */</div><div>-                                       NULL, /* cancellable */</div><div>-                                       (GAsyncReadyCallback)gps_disabled_ready,</div><div>-                                       ctx);</div><div>+        //Don't allow disconnects while trying to connect.</div><div>+        if (ctx->connect_pending != NULL)</div><div>+        {</div><div>+            g_simple_async_result_set_error (ctx->result,</div><div>+                                             MM_CORE_ERROR,</div><div>+                                             MM_CORE_ERROR_IN_PROGRESS,</div><div>+                                             "Can't disconnect GPS while another process is trying to connect it.");</div><div>+</div><div>+            //*_complete_and_free_full is safe to use for the disconnect flow after this point.</div><div>+            ctx->disconnect_pending = NULL;</div><div>+            location_gathering_context_complete_and_free (ctx);</div><div>+            return;</div><div>+        }</div><div>+</div><div>+        ctx->disable_state = GPS_CONTEXT_STEP_SGPSS;</div><div>+        ctx->disconnect_pending = self;</div><div>+        try_gps_disable(self, ctx);</div><div>+</div><div>         return;</div><div>     }</div><div><br></div><div>     /* For any other location (e.g. 3GPP), or if still some GPS needed, just return */</div><div>     g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);</div><div>+    ctx->disconnect_pending = NULL;</div><div>     location_gathering_context_complete_and_free (ctx);</div><div> }</div><div><br></div><div>@@ -236,12 +449,13 @@ parent_disable_location_gathering_ready (MMIfaceModemLocation *self,</div><div>         } else {</div><div>             /* Fatal */</div><div>             g_simple_async_result_take_error (ctx->result, error);</div><div>+            ctx->disconnect_pending = NULL;</div><div>             location_gathering_context_complete_and_free (ctx);</div><div>             return;</div><div>         }</div><div>     }</div><div><br></div><div>-    internal_disable_location_gathering (ctx);</div><div>+    internal_disable_location_gathering (self, ctx);</div><div> }</div><div><br></div><div> void</div><div>@@ -259,6 +473,9 @@ mm_common_cinterion_disable_location_gathering (MMIfaceModemLocation *self,</div><div>                                              user_data,</div><div>                                              mm_common_cinterion_disable_location_gathering);</div><div>     ctx->source = source;</div><div>+    ctx->connect_pending = NULL;</div><div>+    ctx->disconnect_pending = NULL;</div><div>+    ctx->gps_retry = 0;</div><div><br></div><div>     /* Chain up parent's gathering enable */</div><div>     if (iface_modem_location_parent->disable_location_gathering) {</div><div>@@ -270,7 +487,7 @@ mm_common_cinterion_disable_location_gathering (MMIfaceModemLocation *self,</div><div>         return;</div><div>     }</div><div><br></div><div>-    internal_disable_location_gathering (ctx);</div><div>+    internal_disable_location_gathering (self, ctx);</div><div> }</div><div><br></div><div> /*****************************************************************************/</div><div>@@ -286,16 +503,10 @@ mm_common_cinterion_enable_location_gathering_finish (MMIfaceModemLocation *self</div><div><br></div><div> static void</div><div> gps_enabled_ready (MMBaseModem *self,</div><div>-                   GAsyncResult *res,</div><div>                    LocationGatheringContext *ctx)</div><div> {</div><div>     GError *error = NULL;</div><div><br></div><div>-    if (!mm_base_modem_at_command_full_finish (self, res, &error)) {</div><div>-        g_simple_async_result_take_error (ctx->result, error);</div><div>-        location_gathering_context_complete_and_free (ctx);</div><div>-        return;</div><div>-    }</div><div><br></div><div>     /* Only use the GPS port in NMEA/RAW setups */</div><div>     if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |</div><div>@@ -312,12 +523,60 @@ gps_enabled_ready (MMBaseModem *self,</div><div>                                                  MM_CORE_ERROR,</div><div>                                                  MM_CORE_ERROR_FAILED,</div><div>                                                  "Couldn't open raw GPS serial port");</div><div>-        } else</div><div>+        }</div><div>+        else</div><div>             g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);</div><div>-    } else</div><div>+</div><div>+    }</div><div>+    else</div><div>         g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);</div><div><br></div><div>-    location_gathering_context_complete_and_free (ctx);</div><div>+    location_gathering_context_complete_and_free_full (ctx);</div><div>+}</div><div>+</div><div>+static void</div><div>+try_gps_enable (MMIfaceModemLocation *self,</div><div>+                LocationGatheringContext *ctx)</div><div>+{</div><div>+    gchar *gps_command;</div><div>+</div><div>+    switch (ctx->enable_state) {</div><div>+</div><div>+    //The old command to enable GPS, works on at least PSX8</div><div>+    case GPS_CONTEXT_STEP_SGPSS: {</div><div>+        gps_command = "^SGPSS=4";</div><div>+        break;</div><div>+    }</div><div>+    //Commands Power/Ant, Engine & Output w/ sgpsc are</div><div>+    //used by PLS8-X & E. Since versions 2.0+</div><div>+    case GPS_CONTEXT_STEP_SGPSC_ANTENNA: {</div><div>+        gps_command = "^SGPSC=\"Power/Antenna\",\"on\"";</div><div>+        break;</div><div>+    }</div><div>+    case GPS_CONTEXT_STEP_SGPSC_ENGINE: {</div><div>+        gps_command = "^SGPSC=\"Engine\",\"1\"";</div><div>+        break;</div><div>+    }</div><div>+    case GPS_CONTEXT_STEP_SGPSC_OUTPUT: {</div><div>+        gps_command = "^SGPSC=\"NMEA/Output\",\"on\"";</div><div>+        break;</div><div>+    }</div><div>+    case GPS_CONTEXT_STEP_DONE: {</div><div>+        gps_enabled_ready(MM_BASE_MODEM (self), ctx);</div><div>+        return;</div><div>+    }</div><div>+    default: {</div><div>+        g_simple_async_result_set_error (ctx->result,</div><div>+                                         MM_CORE_ERROR,</div><div>+                                         MM_CORE_ERROR_WRONG_STATE,</div><div>+                                         "Unknown gps state during setup.");</div><div>+        location_gathering_context_complete_and_free_full (ctx);</div><div>+        return;</div><div>+    }</div><div>+    }</div><div>+</div><div>+    send_gps_command(self, ctx, &gps_command);</div><div>+    return;</div><div> }</div><div><br></div><div> static void</div><div>@@ -338,13 +597,13 @@ parent_enable_location_gathering_ready (MMIfaceModemLocation *self,</div><div>         } else {</div><div>             /* Fatal */</div><div>             g_simple_async_result_take_error (ctx->result, error);</div><div>+            ctx->connect_pending = NULL;</div><div>             location_gathering_context_complete_and_free (ctx);</div><div>             return;</div><div>         }</div><div>     }</div><div><br></div><div>     /* Now our own enabling */</div><div>-</div><div>     location_ctx = get_location_context (MM_BASE_MODEM (self));</div><div><br></div><div>     /* NMEA and RAW are both enabled in the same way */</div><div>@@ -359,22 +618,33 @@ parent_enable_location_gathering_ready (MMIfaceModemLocation *self,</div><div>         location_ctx->enabled_sources |= ctx->source;</div><div>     }</div><div><br></div><div>+</div><div>     if (start_gps) {</div><div>-        /* We enable continuous GPS fixes */</div><div>-        mm_base_modem_at_command_full (MM_BASE_MODEM (self),</div><div>-                                       mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL),</div><div>-                                       "AT^SGPSS=4",</div><div>-                                       3,</div><div>-                                       FALSE,</div><div>-                                       FALSE, /* raw */</div><div>-                                       NULL, /* cancellable */</div><div>-                                       (GAsyncReadyCallback)gps_enabled_ready,</div><div>-                                       ctx);</div><div>+        //Can only setup/tear down one thing at a time.</div><div>+        if (ctx->disconnect_pending != NULL)</div><div>+        {</div><div>+            g_simple_async_result_set_error (ctx->result,</div><div>+                                             MM_CORE_ERROR,</div><div>+                                             MM_CORE_ERROR_IN_PROGRESS,</div><div>+                                             "Can't setup GPS while another process is trying to disconnect it.");</div><div>+</div><div>+            //*_complete_and_free_full is safe to use for the connect flow after this point.</div><div>+            ctx->connect_pending = NULL;</div><div>+            location_gathering_context_complete_and_free (ctx);</div><div>+            return;</div><div>+        }</div><div>+</div><div>+        ctx->enable_state = GPS_CONTEXT_STEP_SGPSS;</div><div>+        ctx->connect_pending = self;</div><div>+        ctx->gps_retry = 0;</div><div>+        try_gps_enable(self, ctx);</div><div>+</div><div>         return;</div><div>     }</div><div><br></div><div>     /* For any other location (e.g. 3GPP), or if GPS already running just return */</div><div>     g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);</div><div>+    ctx->connect_pending = NULL;</div><div>     location_gathering_context_complete_and_free (ctx);</div><div> }</div><div><br></div><div>@@ -393,6 +663,8 @@ mm_common_cinterion_enable_location_gathering (MMIfaceModemLocation *self,</div><div>                                              user_data,</div><div>                                              mm_common_cinterion_enable_location_gathering);</div><div>     ctx->source = source;</div><div>+    ctx->connect_pending = NULL;</div><div>+    ctx->disconnect_pending = NULL;</div><div><br></div><div>     /* Chain up parent's gathering enable */</div><div>     iface_modem_location_parent->enable_location_gathering (</div><div>@@ -443,11 +715,41 @@ mm_common_cinterion_setup_gps_port (MMBroadbandModem *self)</div><div>     if (gps_data_port) {</div><div>         /* It may happen that the modem was started with GPS already enabled, or</div><div>          * maybe ModemManager got rebooted and it was left enabled before. We'll make</div><div>-         * sure that it is disabled when we initialize the modem */</div><div>+         * sure that it is disabled when we initialize the modem.</div><div>+         */</div><div>+</div><div>+        //It may seem odd that we are turning Antenna & Engine on but it is critical for the PLS8</div><div>+        //that this happen here. There is a bug that keeps 'Engine on' from succeding on</div><div>+        //the first go around in the 'try_gps_enable' flow. Cinterion confirmed this a bug. Retry's in</div><div>+        //the connect flow won't help and the only way found so far is to enable the engine here,</div><div>+        //before the modem is enabled. This is not ideal and should be redone as soon as the root</div><div>+        //cause of the bug is identified. This bug does not appear on my x86 platform. -Matt Stanger</div><div>+        //TODO: Fix bug in 'try_gps_enable' where 'Engine on' command gets stuck returning 767 errors</div><div>+        //on ARM AT91SAM9263 processor then change these to default off/0.</div><div>         mm_base_modem_at_command_full (MM_BASE_MODEM (self),</div><div>                                        mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL),</div><div>                                        "AT^SGPSS=0",</div><div>                                        3, FALSE, FALSE, NULL, NULL, NULL);</div><div>+        g_usleep (100);</div><div>+</div><div>+        mm_base_modem_at_command_full (MM_BASE_MODEM (self),</div><div>+                                       mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL),</div><div>+                                       "^SGPSC=\"Power/Antenna\",\"on\"",</div><div>+                                       3, FALSE, FALSE, NULL, NULL, NULL);</div><div>+        g_usleep (100);</div><div>+</div><div>+        mm_base_modem_at_command_full (MM_BASE_MODEM (self),</div><div>+                                       mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL),</div><div>+                                       "^SGPSC=\"Engine\",\"1\"",</div><div>+                                       3, FALSE, FALSE, NULL, NULL, NULL);</div><div>+</div><div>+        g_usleep (100);</div><div>+</div><div>+        mm_base_modem_at_command_full (MM_BASE_MODEM (self),</div><div>+                                       mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL),</div><div>+                                       "^SGPSC=\"NMEA/Output\",\"off\"",</div><div>+                                       3, FALSE, FALSE, NULL, NULL, NULL);</div><div>+</div><div><br></div><div>         /* Add handler for the NMEA traces */</div><div>         mm_port_serial_gps_add_trace_handler (gps_data_port,</div><div>diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.c b/plugins/cinterion/mm-modem-helpers-cinterion.c</div><div>index bffe00a..14b18dd 100644</div><div>--- a/plugins/cinterion/mm-modem-helpers-cinterion.c</div><div>+++ b/plugins/cinterion/mm-modem-helpers-cinterion.c</div><div>@@ -10,12 +10,15 @@</div><div>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</div><div>  * GNU General Public License for more details:</div><div>  *</div><div>+ * Copyright (C) 2016 Trimble Navigation Limited</div><div>  * Copyright (C) 2014 Aleksander Morgado <<a href="mailto:aleksander@aleksander.es">aleksander@aleksander.es</a>></div><div>+ * Contributor: Matthew Stanger <<a href="mailto:matthew_stanger@trimble.com">matthew_stanger@trimble.com</a>></div><div>  */</div><div><br></div><div> #include <config.h></div><div> #include <string.h></div><div> #include <stdlib.h></div><div>+#include <stdio.h></div><div><br></div><div> #include "ModemManager.h"</div><div> #define _LIBMM_INSIDE_MM</div><div>@@ -24,6 +27,7 @@</div><div> #include "mm-charsets.h"</div><div> #include "mm-errors-types.h"</div><div> #include "mm-modem-helpers-cinterion.h"</div><div>+#include "mm-modem-helpers.h"</div><div><br></div><div> /* Setup relationship between the 3G band bitmask in the modem and the bitmask</div><div>  * in ModemManager. */</div><div>@@ -482,3 +486,187 @@ mm_cinterion_parse_sind_response (const gchar *response,</div><div><br></div><div>     return TRUE;</div><div> }</div><div>+</div><div>+/*****************************************************************************/</div><div>+/* ^DHCP response parser */</div><div>+</div><div>+static gboolean</div><div>+match_info_to_ip4_addr (GMatchInfo *match_info,</div><div>+                        guint match_index,</div><div>+                        guint *out_addr)</div><div>+{</div><div>+    gchar *s, *bin = NULL;</div><div>+    gchar buf[9];</div><div>+    gsize len, bin_len;</div><div>+    gboolean success = FALSE;</div><div>+</div><div>+    s = g_match_info_fetch (match_info, match_index);</div><div>+    g_return_val_if_fail (s != NULL, FALSE);</div><div>+</div><div>+    len = strlen (s);</div><div>+    if (len == 1 && s[0] == '0') {</div><div>+        *out_addr = 0;</div><div>+        success = TRUE;</div><div>+        goto done;</div><div>+    }</div><div>+    if (len < 7 || len > 8)</div><div>+        goto done;</div><div>+</div><div>+    /* Handle possibly missing leading zero */</div><div>+    memset (buf, 0, sizeof (buf));</div><div>+    if (len == 7) {</div><div>+        strcpy (&buf[1], s);</div><div>+        buf[0] = '0';</div><div>+    } else if (len == 8)</div><div>+        strcpy (buf, s);</div><div>+    else</div><div>+        g_assert_not_reached ();</div><div>+</div><div>+    bin = mm_utils_hexstr2bin (buf, &bin_len);</div><div>+    if (!bin || bin_len != 4)</div><div>+        goto done;</div><div>+</div><div>+    *out_addr = GUINT32_SWAP_LE_BE (*((guint32 *) bin));</div><div>+    success = TRUE;</div><div>+</div><div>+done:</div><div>+    g_free (s);</div><div>+    g_free (bin);</div><div>+    return success;</div><div>+}</div><div>+</div><div>+gboolean</div><div>+mm_cinterion_parse_dhcp_response (const char *reply,</div><div>+                               guint *out_address,</div><div>+                               guint *out_prefix,</div><div>+                               guint *out_gateway,</div><div>+                               guint *out_dns1,</div><div>+                               guint *out_dns2,</div><div>+                               GError **error)</div><div>+{</div><div>+    gboolean matched;</div><div>+    GRegex *r;</div><div>+    GMatchInfo *match_info = NULL;</div><div>+    GError *match_error = NULL;</div><div>+</div><div>+    g_assert (reply != NULL);</div><div>+    g_assert (out_address != NULL);</div><div>+    g_assert (out_prefix != NULL);</div><div>+    g_assert (out_gateway != NULL);</div><div>+    g_assert (out_dns1 != NULL);</div><div>+    g_assert (out_dns2 != NULL);</div><div>+</div><div>+    /* Format:</div><div>+     *</div><div>+     * ^DHCP: <address>,<netmask>,<gateway>,<?>,<dns1>,<dns2>,<uplink>,<downlink></div><div>+     *</div><div>+     * All numbers are hexadecimal representations of IPv4 addresses, with</div><div>+     * least-significant byte first.  eg, 192.168.50.32 is expressed as</div><div>+     * "2032A8C0".  Sometimes leading zeros are stripped, so "1010A0A" is</div><div>+     * actually 10.10.1.1.</div><div>+     */</div><div>+</div><div>+    r = g_regex_new ("\\^DHCP:\\s*([0-9a-fA-F]+),([0-9a-fA-F]+),([0-9a-fA-F]+),([0-9a-fA-F]+),([0-9a-fA-F]+),([0-9a-fA-F]+),.*$", 0, 0, NULL);</div><div>+    g_assert (r != NULL);</div><div>+</div><div>+    matched = g_regex_match_full (r, reply, -1, 0, 0, &match_info, &match_error);</div><div>+    if (!matched) {</div><div>+        if (match_error) {</div><div>+            g_propagate_error (error, match_error);</div><div>+            g_prefix_error (error, "Could not parse ^DHCP results: ");</div><div>+        } else {</div><div>+            g_set_error_literal (error,</div><div>+                                 MM_CORE_ERROR,</div><div>+                                 MM_CORE_ERROR_FAILED,</div><div>+                                 "Couldn't match ^DHCP reply");</div><div>+        }</div><div>+    } else {</div><div>+        guint netmask;</div><div>+</div><div>+        if (match_info_to_ip4_addr (match_info, 1, out_address) &&</div><div>+            match_info_to_ip4_addr (match_info, 2, &netmask) &&</div><div>+            match_info_to_ip4_addr (match_info, 3, out_gateway) &&</div><div>+            match_info_to_ip4_addr (match_info, 5, out_dns1) &&</div><div>+            match_info_to_ip4_addr (match_info, 6, out_dns2)) {</div><div>+            *out_prefix = mm_count_bits_set (netmask);</div><div>+            matched = TRUE;</div><div>+        }</div><div>+    }</div><div>+</div><div>+    if (match_info)</div><div>+        g_match_info_free (match_info);</div><div>+    g_regex_unref (r);</div><div>+    return matched;</div><div>+}</div><div>+</div><div>+/*****************************************************************************/</div><div>+/* ^SWWAN read parser</div><div>+ * Description: Parses <cid>, <state>[, <WWAN adapter>] or CME ERROR from SWWAN.</div><div>+ * Return: False if error occured while trying to parse SWWAN.</div><div>+ * Requires: Check for AT Error response before calling.</div><div>+     * Read Command</div><div>+     *     AT^SWWAN?</div><div>+     *     Response(s) </div><div>+     *     [^SWWAN: <cid>, <state>[, <WWAN adapter>]]</div><div>+     *     [^SWWAN: ...]</div><div>+     *     OK</div><div>+     *     ERROR</div><div>+     *     +CME ERROR: <err></div><div>+     *</div><div>+     * Examples:</div><div>+     *     OK              - If no WWAN connection is active, then read command just returns OK</div><div>+     *     ^SWWAN: 3,1,1   - 3rd PDP Context, Activated, First WWAN Adaptor</div><div>+     *     +CME ERROR: ?   -</div><div>+*/</div><div>+</div><div>+gboolean</div><div>+mm_cinterion_parse_swwan_response (const gchar *response,</div><div>+                               GList **result,</div><div>+                               GError **error)</div><div>+{</div><div>+    if (!response){</div><div>+        g_set_error (error,</div><div>+                     MM_CORE_ERROR,</div><div>+                     MM_CORE_ERROR_FAILED,</div><div>+                     "Recieved NULL from SWWAN response.");</div><div>+        return FALSE;</div><div>+    }</div><div>+</div><div>+    //Parse for [^SWWAN: <cid>, <state>[, <WWAN adapter>]]</div><div>+    if (strcasestr (response, "SWWAN"))</div><div>+    {</div><div>+        gint matched;</div><div>+        guint cid, state, wwan_adapter;</div><div>+        matched = sscanf (response, "^SWWAN: %d,%d,%d",</div><div>+                          &cid,</div><div>+                          &state,</div><div>+                          &wwan_adapter);</div><div>+</div><div>+        if (matched != 3) {</div><div>+            g_set_error (error,</div><div>+                         MM_CORE_ERROR,</div><div>+                         MM_CORE_ERROR_FAILED,</div><div>+                         "Could not parse SWWAN response: '%s'",</div><div>+                         response);</div><div>+            return FALSE;</div><div>+        }</div><div>+</div><div>+        *result = g_list_append (*result, GINT_TO_POINTER(cid));</div><div>+        *result = g_list_append (*result, GINT_TO_POINTER(state));</div><div>+        *result = g_list_append (*result, GINT_TO_POINTER(wwan_adapter));</div><div>+    }</div><div>+    //TODO: It'd be nice to get 'OK' from response so we don't have to assume that</div><div>+    //zero length response means 'OK' or am I doing it wrong...</div><div>+    else if (!g_utf8_strlen (response, 100))</div><div>+        *result = g_list_append (*result, GINT_TO_POINTER(0));</div><div>+    else {</div><div>+        g_set_error (error,</div><div>+                     MM_CORE_ERROR,</div><div>+                     MM_CORE_ERROR_FAILED,</div><div>+                     "Unknown SWWAN response, unable to parse.");</div><div>+        return FALSE;</div><div>+    }</div><div>+</div><div>+</div><div>+    return TRUE;</div><div>+}</div><div>diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.h b/plugins/cinterion/mm-modem-helpers-cinterion.h</div><div>index d37bcb0..11754ae 100644</div><div>--- a/plugins/cinterion/mm-modem-helpers-cinterion.h</div><div>+++ b/plugins/cinterion/mm-modem-helpers-cinterion.h</div><div>@@ -10,7 +10,9 @@</div><div>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</div><div>  * GNU General Public License for more details:</div><div>  *</div><div>+ * Copyright (C) 2016 Trimble Navigation Limited</div><div>  * Copyright (C) 2014 Aleksander Morgado <<a href="mailto:aleksander@aleksander.es">aleksander@aleksander.es</a>></div><div>+ * Contributor: Matthew Stanger <<a href="mailto:matthew_stanger@trimble.com">matthew_stanger@trimble.com</a>></div><div>  */</div><div><br></div><div> #ifndef MM_MODEM_HELPERS_CINTERION_H</div><div>@@ -63,4 +65,19 @@ gboolean mm_cinterion_parse_sind_response (const gchar *response,</div><div>                                            guint *value,</div><div>                                            GError **error);</div><div><br></div><div>+/*****************************************************************************/</div><div>+/* ^DHCP response parser */</div><div>+gboolean mm_cinterion_parse_dhcp_response (const char *reply,</div><div>+                                        guint *out_address,</div><div>+                                        guint *out_prefix,</div><div>+                                        guint *out_gateway,</div><div>+                                        guint *out_dns1,</div><div>+                                        guint *out_dns2,</div><div>+                                        GError **error);</div><div>+</div><div>+/*****************************************************************************/</div><div>+/* ^SWWAN response parser */</div><div>+gboolean mm_cinterion_parse_swwan_response (const gchar *response,</div><div>+                                        GList **result,</div><div>+                                        GError **error);</div><div> #endif  /* MM_MODEM_HELPERS_CINTERION_H */</div><div>diff --git a/plugins/cinterion/mm-plugin-cinterion.c b/plugins/cinterion/mm-plugin-cinterion.c</div><div>index 8af0a2a..d944c00 100644</div><div>--- a/plugins/cinterion/mm-plugin-cinterion.c</div><div>+++ b/plugins/cinterion/mm-plugin-cinterion.c</div><div>@@ -18,7 +18,9 @@</div><div>  *</div><div>  * Copyright (C) 2011 Ammonit Measurement GmbH</div><div>  * Copyright (C) 2011 - 2012 Google Inc.</div><div>+ * Copyright (C) 2016 Trimble Navigation Limited</div><div>  * Author: Aleksander Morgado <<a href="mailto:aleksander@lanedo.com">aleksander@lanedo.com</a>></div><div>+ * Contributor: Matthew Stanger <<a href="mailto:matthew_stanger@trimble.com">matthew_stanger@trimble.com</a>></div><div>  */</div><div><br></div><div> #include <string.h></div><div>@@ -45,6 +47,7 @@ int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;</div><div><br></div><div> #define TAG_CINTERION_APP_PORT   "cinterion-app-port"</div><div> #define TAG_CINTERION_MODEM_PORT "cinterion-modem-port"</div><div>+#define TAG_CINTERION_GPS_PORT "ID_MM_CINTERION_PORT_TYPE_GPS"</div><div><br></div><div> typedef struct {</div><div>     MMPortProbe *probe;</div><div>@@ -175,9 +178,14 @@ grab_port (MMPlugin *self,</div><div>         mm_dbg ("(%s/%s)' Port flagged as PPP",</div><div>                 mm_port_probe_get_port_subsys (probe),</div><div>                 mm_port_probe_get_port_name (probe));</div><div>-        pflags = MM_PORT_SERIAL_AT_FLAG_PPP;</div><div>+</div><div>+        //Assuming PLS8 will stay idProduct 0061 and for that product we'll use SWWAN, not PPP.</div><div>+        if ((g_strcmp0 (g_udev_device_get_property(mm_port_probe_peek_port (probe),"ID_MODEL_ID"),"0061") == 0))</div><div>+            pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;</div><div>+        else</div><div>+            pflags = MM_PORT_SERIAL_AT_FLAG_PPP;</div><div>     } else if (g_udev_device_get_property_as_boolean (mm_port_probe_peek_port (probe),</div><div>-                                                      "ID_MM_CINTERION_PORT_TYPE_GPS")) {</div><div>+                                                      TAG_CINTERION_GPS_PORT)) {</div><div>         mm_dbg ("(%s/%s)' Port flagged as GPS",</div><div>                 mm_port_probe_get_port_subsys (probe),</div><div>                 mm_port_probe_get_port_name (probe));</div><div>@@ -185,6 +193,135 @@ grab_port (MMPlugin *self,</div><div>         g_warn_if_fail (ptype == MM_PORT_TYPE_UNKNOWN);</div><div>         ptype = MM_PORT_TYPE_GPS;</div><div>     }</div><div>+    //TODO: This should be handled by uDev w/ cinterion-port-types.rules but I can't get</div><div>+    //it to work. Added the rule in there to set the ENV variable TAG_CINTERION_GPS_PORT.</div><div>+    //Once that is fixed, it will set the ENV var above & you can delete this else if.</div><div>+    else if ((g_strcmp0 (g_udev_device_get_property(mm_port_probe_peek_port (probe),"ID_USB_INTERFACE_NUM"),"04") == 0) &&</div><div>+             (g_strcmp0 (g_udev_device_get_property(mm_port_probe_peek_port (probe),"ID_MODEL_ID"),"0061") == 0)) {</div><div>+           mm_dbg ("(%s/%s)' Port flagged as GPS",</div><div>+                   mm_port_probe_get_port_subsys (probe),</div><div>+                   mm_port_probe_get_port_name (probe));</div><div>+           /* Not an AT port, but the port to grab GPS traces */</div><div>+           g_warn_if_fail (ptype == MM_PORT_TYPE_UNKNOWN);</div><div>+           ptype = MM_PORT_TYPE_GPS;</div><div>+       }</div><div>+</div><div>+    /* udev info for PLS8-X of GPS Port, for troubleshooting above</div><div>+      looking at device '/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.4/tty/ttyACM2':</div><div>+        KERNEL=="ttyACM2"</div><div>+        SUBSYSTEM=="tty"</div><div>+        DRIVER==""</div><div>+</div><div>+      looking at parent device '/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.4':</div><div>+        KERNELS=="3-2:1.4"</div><div>+        SUBSYSTEMS=="usb"</div><div>+        DRIVERS=="cdc_acm"</div><div>+        ATTRS{bInterfaceClass}=="02"</div><div>+        ATTRS{iad_bFunctionClass}=="02"</div><div>+        ATTRS{bmCapabilities}=="2"</div><div>+        ATTRS{iad_bFirstInterface}=="04"</div><div>+        ATTRS{bInterfaceSubClass}=="02"</div><div>+        ATTRS{bInterfaceProtocol}=="01"</div><div>+        ATTRS{bNumEndpoints}=="01"</div><div>+        ATTRS{iad_bFunctionSubClass}=="02"</div><div>+        ATTRS{iad_bFunctionProtocol}=="01"</div><div>+        ATTRS{supports_autosuspend}=="1"</div><div>+        ATTRS{iad_bInterfaceCount}=="02"</div><div>+        ATTRS{bAlternateSetting}==" 0"</div><div>+        ATTRS{bInterfaceNumber}=="04"</div><div>+        ATTRS{interface}=="CDC Abstract Control Model (ACM)"</div><div>+</div><div>+      looking at parent device '/devices/pci0000:00/0000:00:14.0/usb3/3-2':</div><div>+        KERNELS=="3-2"</div><div>+        SUBSYSTEMS=="usb"</div><div>+        DRIVERS=="usb"</div><div>+        ATTRS{bDeviceSubClass}=="00"</div><div>+        ATTRS{bDeviceProtocol}=="00"</div><div>+        ATTRS{devpath}=="2"</div><div>+        ATTRS{idVendor}=="1e2d"</div><div>+        ATTRS{speed}=="480"</div><div>+        ATTRS{bNumInterfaces}=="14"</div><div>+        ATTRS{bConfigurationValue}=="1"</div><div>+        ATTRS{bMaxPacketSize0}=="64"</div><div>+        ATTRS{busnum}=="3"</div><div>+        ATTRS{devnum}=="30"</div><div>+        ATTRS{configuration}==""</div><div>+        ATTRS{bMaxPower}=="20mA"</div><div>+        ATTRS{authorized}=="1"</div><div>+        ATTRS{bmAttributes}=="e0"</div><div>+        ATTRS{bNumConfigurations}=="1"</div><div>+        ATTRS{maxchild}=="0"</div><div>+        ATTRS{bcdDevice}=="0232"</div><div>+        ATTRS{avoid_reset_quirk}=="0"</div><div>+        ATTRS{quirks}=="0x0"</div><div>+        ATTRS{version}==" 2.00"</div><div>+        ATTRS{urbnum}=="3395"</div><div>+        ATTRS{ltm_capable}=="no"</div><div>+        ATTRS{manufacturer}=="Cinterion"</div><div>+        ATTRS{removable}=="removable"</div><div>+        ATTRS{idProduct}=="0061"</div><div>+        ATTRS{bDeviceClass}=="00"</div><div>+        ATTRS{product}=="LTE Modem"</div><div>+</div><div>+      looking at parent device '/devices/pci0000:00/0000:00:14.0/usb3':</div><div>+        KERNELS=="usb3"</div><div>+        SUBSYSTEMS=="usb"</div><div>+        DRIVERS=="usb"</div><div>+        ATTRS{bDeviceSubClass}=="00"</div><div>+        ATTRS{bDeviceProtocol}=="01"</div><div>+        ATTRS{devpath}=="0"</div><div>+        ATTRS{idVendor}=="1d6b"</div><div>+        ATTRS{speed}=="480"</div><div>+        ATTRS{bNumInterfaces}==" 1"</div><div>+        ATTRS{bConfigurationValue}=="1"</div><div>+        ATTRS{bMaxPacketSize0}=="64"</div><div>+        ATTRS{authorized_default}=="1"</div><div>+        ATTRS{busnum}=="3"</div><div>+        ATTRS{devnum}=="1"</div><div>+        ATTRS{configuration}==""</div><div>+        ATTRS{bMaxPower}=="0mA"</div><div>+        ATTRS{authorized}=="1"</div><div>+        ATTRS{bmAttributes}=="e0"</div><div>+        ATTRS{bNumConfigurations}=="1"</div><div>+        ATTRS{maxchild}=="4"</div><div>+        ATTRS{bcdDevice}=="0313"</div><div>+        ATTRS{avoid_reset_quirk}=="0"</div><div>+        ATTRS{quirks}=="0x0"</div><div>+        ATTRS{serial}=="0000:00:14.0"</div><div>+        ATTRS{version}==" 2.00"</div><div>+        ATTRS{urbnum}=="516"</div><div>+        ATTRS{ltm_capable}=="no"</div><div>+        ATTRS{manufacturer}=="Linux 3.13.0-48-generic xhci_hcd"</div><div>+        ATTRS{removable}=="unknown"</div><div>+        ATTRS{idProduct}=="0002"</div><div>+        ATTRS{bDeviceClass}=="09"</div><div>+        ATTRS{product}=="xHCI Host Controller"</div><div>+</div><div>+      looking at parent device '/devices/pci0000:00/0000:00:14.0':</div><div>+        KERNELS=="0000:00:14.0"</div><div>+        SUBSYSTEMS=="pci"</div><div>+        DRIVERS=="xhci_hcd"</div><div>+        ATTRS{irq}=="42"</div><div>+        ATTRS{subsystem_vendor}=="0x1028"</div><div>+        ATTRS{broken_parity_status}=="0"</div><div>+        ATTRS{class}=="0x0c0330"</div><div>+        ATTRS{consistent_dma_mask_bits}=="64"</div><div>+        ATTRS{dma_mask_bits}=="64"</div><div>+        ATTRS{local_cpus}=="00000000,00000000,00000000,00000000,00000000,00000000,00000000,000000ff"</div><div>+        ATTRS{device}=="0x1e31"</div><div>+        ATTRS{enable}=="1"</div><div>+        ATTRS{msi_bus}==""</div><div>+        ATTRS{local_cpulist}=="0-7"</div><div>+        ATTRS{vendor}=="0x8086"</div><div>+        ATTRS{subsystem_device}=="0x0577"</div><div>+        ATTRS{numa_node}=="-1"</div><div>+        ATTRS{d3cold_allowed}=="1"</div><div>+</div><div>+      looking at parent device '/devices/pci0000:00':</div><div>+        KERNELS=="pci0000:00"</div><div>+        SUBSYSTEMS==""</div><div>+        DRIVERS==""</div><div>+    */</div><div><br></div><div>     return mm_base_modem_grab_port (modem,</div><div>                                     mm_port_probe_get_port_subsys (probe),</div><div>diff --git a/src/mm-base-modem.c b/src/mm-base-modem.c</div><div>index 9cd7c5f..449dddc 100644</div><div>--- a/src/mm-base-modem.c</div><div>+++ b/src/mm-base-modem.c</div><div>@@ -674,6 +674,26 @@ mm_base_modem_peek_best_data_port (MMBaseModem *self,</div><div>     return NULL;</div><div> }</div><div><br></div><div>+MMPort *</div><div>+mm_base_modem_peek_current_data_port (MMBaseModem *self,</div><div>+                                   MMPortType type)</div><div>+{</div><div>+    GList *l;</div><div>+</div><div>+    g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL);</div><div>+</div><div>+    /* Return first connected data port */</div><div>+    for (l = self->priv->data; l; l = g_list_next (l)) {</div><div>+        if (mm_port_get_connected ((MMPort *)l->data) &&</div><div>+            (mm_port_get_port_type ((MMPort *)l->data) == type ||</div><div>+             type == MM_PORT_TYPE_UNKNOWN)) {</div><div>+            return (MMPort *)l->data;</div><div>+        }</div><div>+    }</div><div>+</div><div>+    return NULL;</div><div>+}</div><div>+</div><div> GList *</div><div> mm_base_modem_get_data_ports (MMBaseModem *self)</div><div> {</div><div>diff --git a/src/mm-base-modem.h b/src/mm-base-modem.h</div><div>index 3c0d16f..bbea52d 100644</div><div>--- a/src/mm-base-modem.h</div><div>+++ b/src/mm-base-modem.h</div><div>@@ -131,6 +131,7 @@ MMPortMbim       *mm_base_modem_peek_port_mbim_for_data (MMBaseModem *self, MMPo</div><div> #endif</div><div> MMPortSerialAt   *mm_base_modem_peek_best_at_port      (MMBaseModem *self, GError **error);</div><div> MMPort           *mm_base_modem_peek_best_data_port    (MMBaseModem *self, MMPortType type);</div><div>+MMPort           *mm_base_modem_peek_current_data_port (MMBaseModem *self, MMPortType type);</div><div> GList            *mm_base_modem_peek_data_ports        (MMBaseModem *self);</div><div><br></div><div> MMPortSerialAt   *mm_base_modem_get_port_primary      (MMBaseModem *self);</div><div>--</div><div>1.9.1</div></div><div><br></div><div><br></div><div><br></div></div></div>