summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Stanger <matthew_stanger@trimble.com>2016-10-23 19:28:19 -0600
committerAleksander Morgado <aleksander@aleksander.es>2016-11-23 22:48:29 +0100
commitffe980b164437489e095976a348522c6c904e43d (patch)
treee5a16eada0796c340807da80f8bafe8b90b8d26a
parent905c8a48e96350af002536b627b46fbf517f727c (diff)
downloadModemManager-ffe980b164437489e095976a348522c6c904e43d.tar.gz
cinterion: implement support for the new SWWAN interface
-rw-r--r--AUTHORS1
-rw-r--r--plugins/Makefile.am2
-rw-r--r--plugins/cinterion/mm-broadband-bearer-cinterion.c902
-rw-r--r--plugins/cinterion/mm-broadband-bearer-cinterion.h57
-rw-r--r--plugins/cinterion/mm-broadband-modem-cinterion.c229
-rw-r--r--plugins/cinterion/mm-modem-helpers-cinterion.c81
-rw-r--r--plugins/cinterion/mm-modem-helpers-cinterion.h7
-rw-r--r--plugins/cinterion/tests/test-modem-helpers-cinterion.c135
-rw-r--r--src/mm-base-modem.c20
-rw-r--r--src/mm-base-modem.h1
10 files changed, 1434 insertions, 1 deletions
diff --git a/AUTHORS b/AUTHORS
index d6c2b31c5..ad1981885 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -69,3 +69,4 @@ Other contributors:
Vitaly Gimly
Yuri Chornoivan
kuonirat
+ Matthew Stanger
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 070e379e3..86026fd57 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -646,6 +646,8 @@ libmm_plugin_cinterion_la_SOURCES = \
cinterion/mm-common-cinterion.h \
cinterion/mm-broadband-modem-cinterion.c \
cinterion/mm-broadband-modem-cinterion.h \
+ cinterion/mm-broadband-bearer-cinterion.c \
+ cinterion/mm-broadband-bearer-cinterion.h \
$(NULL)
if WITH_QMI
libmm_plugin_cinterion_la_SOURCES += \
diff --git a/plugins/cinterion/mm-broadband-bearer-cinterion.c b/plugins/cinterion/mm-broadband-bearer-cinterion.c
new file mode 100644
index 000000000..df8831972
--- /dev/null
+++ b/plugins/cinterion/mm-broadband-bearer-cinterion.c
@@ -0,0 +1,902 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2016 Trimble Navigation Limited
+ * Author: Matthew Stanger <matthew_stanger@trimble.com>
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <arpa/inet.h>
+#include <ModemManager.h>
+#include "mm-base-modem-at.h"
+#include "mm-broadband-bearer-cinterion.h"
+#include "mm-log.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-cinterion.h"
+#include "mm-daemon-enums-types.h"
+
+G_DEFINE_TYPE (MMBroadbandBearerCinterion, mm_broadband_bearer_cinterion, MM_TYPE_BROADBAND_BEARER)
+
+/*****************************************************************************/
+/* Common enums and structs */
+
+#define FIRST_USB_INTERFACE 1
+#define SECOND_USB_INTERFACE 2
+
+typedef enum {
+ BearerCinterionAuthUnknown = -1,
+ BearerCinterionAuthNone = 0,
+ BearerCinterionAuthPap = 1,
+ BearerCinterionAuthChap = 2,
+ BearerCinterionAuthMsChapV2 = 3,
+} BearerCinterionAuthType;
+
+typedef enum {
+ Connect3gppContextStepInit = 0,
+ Connect3gppContextStepAuth,
+ Connect3gppContextStepPdpCtx,
+ Connect3gppContextStepStartSwwan,
+ Connect3gppContextStepValidateConnection,
+ Connect3gppContextStepIpConfig,
+ Connect3gppContextStepFinalizeBearer,
+} Connect3gppContextStep;
+
+typedef enum {
+ Disconnect3gppContextStepStopSwwan = 0,
+ Disconnect3gppContextStepConnectionStatus,
+ Disconnect3gppContextStepFinish
+} Disconnect3gppContextStep;
+
+typedef struct {
+ MMBroadbandBearerCinterion *self;
+ MMBaseModem *modem;
+ MMPortSerialAt *primary;
+ MMPort *data;
+ Connect3gppContextStep connect;
+ MMBearerIpConfig *ipv4_config;
+ GCancellable *cancellable;
+ GSimpleAsyncResult *result;
+} Connect3gppContext;
+
+typedef struct {
+ MMBroadbandBearerCinterion *self;
+ MMBaseModem *modem;
+ MMPortSerialAt *primary;
+ MMPort *data;
+ Disconnect3gppContextStep disconnect;
+ GSimpleAsyncResult *result;
+} Disconnect3gppContext;
+
+struct _MMBroadbandBearerCinterionPrivate {
+ guint network_disconnect_pending_id;/* Flag for network-initiated disconnect */
+ guint pdp_cid;/* Mapping for PDP Context to network (SWWAN) interface */
+};
+
+/*****************************************************************************/
+/* Common 3GPP Function Declarations */
+static void connect_3gpp_context_step (Connect3gppContext *ctx);
+static void disconnect_3gpp_context_step (Disconnect3gppContext *ctx);
+static void connect_3gpp_context_complete_and_free (Connect3gppContext *ctx);
+static void disconnect_3gpp_context_complete_and_free (Disconnect3gppContext *ctx);
+
+/*****************************************************************************/
+/* Common - Helper Functions*/
+
+static void
+pdp_cid_map (MMBroadbandBearerCinterion *self, const gchar *bearer_interface)
+{
+ /* Map PDP context from the current Bearer. USB0 -> 3rd context, USB1 -> 1st context.
+ * Note - Cinterion told me specifically to map the contexts to the interfaces this way, though
+ * I've seen that SWWAN appear's to work fine with any PDP to any interface */
+
+ if (g_strcmp0 (bearer_interface, "0a") == 0)
+ self->priv->pdp_cid = 3;
+ else if (g_strcmp0 (bearer_interface, "0c") == 0)
+ self->priv->pdp_cid = 1;
+ else
+ /* Shouldn't be able to create a bearer for SWWAN and not be able to
+ * find the net interface. Otherwise connects/disconnects will get wrecked */
+ g_assert_not_reached ();
+}
+
+static gint
+verify_connection_state_from_swwan_response (GList *result, GError **error)
+{
+ /* Returns 0 if SWWAN is connected, 1 if not connected, -1 on error
+ * for the bearer's target interface */
+
+ if (g_list_length(result) != 0) {
+ int first_result = GPOINTER_TO_INT(result->data);
+
+ /* Received an 'OK'(0) response */
+ if (first_result == 0)
+ return 1;
+ /* 1 || 3 result is the CID, given when that context is activated.
+ * TODO: Rework for dual sim connections. */
+ else if (first_result == 1 || first_result ==3)
+ return 0;
+ else {
+ for (; result; result = g_list_next(result))
+ mm_err ("Unknown SWWAN response data:%i", GPOINTER_TO_INT(result->data));
+
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Unparsable SWWAN response format.");
+
+ return -1;
+ }
+ }
+ else {
+ mm_err ("Unable to parse zero length SWWAN response.");
+
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Unparsable SWWAN response format.");
+
+ return -1;
+ }
+}
+
+
+/*****************************************************************************/
+/* Connect - Helper Functions*/
+
+static gint
+cinterion_parse_auth_type (MMBearerAllowedAuth mm_auth)
+{
+ switch (mm_auth) {
+ case MM_BEARER_ALLOWED_AUTH_NONE:
+ return BearerCinterionAuthNone;
+ case MM_BEARER_ALLOWED_AUTH_PAP:
+ return BearerCinterionAuthPap;
+ case MM_BEARER_ALLOWED_AUTH_CHAP:
+ return BearerCinterionAuthChap;
+ case MM_BEARER_ALLOWED_AUTH_MSCHAPV2:
+ return BearerCinterionAuthMsChapV2;
+ default:
+ return BearerCinterionAuthUnknown;
+ }
+}
+
+static void
+connect_3gpp_context_complete_and_free (Connect3gppContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+ g_object_unref (ctx->cancellable);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->modem);
+ g_object_unref (ctx->self);
+ g_clear_object (&ctx->ipv4_config);
+ g_clear_object (&ctx->data);
+ g_clear_object (&ctx->primary);
+ g_slice_free (Connect3gppContext, ctx);
+}
+
+static MMBearerConnectResult *
+connect_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return NULL;
+
+ return mm_bearer_connect_result_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+
+
+static void
+pdp_cid_connect (Connect3gppContext *ctx)
+{
+ const gchar *bearer_interface;
+
+ /* For E: INTERFACE=usb0 -> E: ID_USB_INTERFACE_NUM=0a
+ * For E: INTERFACE=usb1 -> E: ID_USB_INTERFACE_NUM=0c */
+
+ /* Look up the net port to associate with this bearer */
+ bearer_interface = mm_kernel_device_get_property (mm_port_peek_kernel_device (ctx->data),
+ "ID_USB_INTERFACE_NUM");
+
+ pdp_cid_map (ctx->self, bearer_interface);
+
+ return;
+}
+
+static void
+get_cmd_write_response_ctx_connect (MMBaseModem *modem,
+ GAsyncResult *res,
+ Connect3gppContext *ctx)
+{
+ /* Only use this to parse responses that respond 'Ok' or 'ERROR' */
+ GError *error = NULL;
+
+ /* Check to see if the command had an error */
+ mm_base_modem_at_command_finish (modem, res, &error);
+
+ /* We expect either OK or an error */
+ if (error != NULL) {
+ g_simple_async_result_take_error (ctx->result, error);
+ connect_3gpp_context_complete_and_free (ctx);
+ return;
+ }
+
+ g_clear_error (&error);
+
+ /*GOTO next step */
+ ctx->connect++;
+ connect_3gpp_context_step (ctx);
+}
+
+static void
+get_swwan_read_response_ctx_connect (MMBaseModem *modem,
+ GAsyncResult *res,
+ Connect3gppContext *ctx)
+{
+ const gchar *response;
+ GError *error = NULL;
+ GList *response_parsed = NULL;
+
+ /* Get the SWWAN read response */
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+
+ /* Error if parsing SWWAN read response fails */
+ if (!mm_cinterion_parse_swwan_response (response, &response_parsed, &error)) {
+ g_simple_async_result_take_error (ctx->result, error);
+ connect_3gpp_context_complete_and_free (ctx);
+ return;
+ }
+
+ /*Check parsed SWWAN reponse to see if we are now connected */
+ if (verify_connection_state_from_swwan_response(response_parsed, &error)) {
+ g_simple_async_result_take_error (ctx->result, error);
+ connect_3gpp_context_complete_and_free (ctx);
+ return;
+ }
+
+ g_list_free(response_parsed);
+ g_clear_error (&error);
+
+ /* GOTO next step */
+ ctx->connect++;
+ connect_3gpp_context_step (ctx);
+}
+
+static void
+setup_ip_settings (Connect3gppContext *ctx)
+{
+ MMBearerIpFamily ip_family;
+
+ ip_family = mm_bearer_properties_get_ip_type (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
+
+ if (ip_family == MM_BEARER_IP_FAMILY_NONE ||
+ ip_family == MM_BEARER_IP_FAMILY_ANY) {
+ gchar *ip_family_str;
+
+ ip_family = mm_base_bearer_get_default_ip_family (MM_BASE_BEARER (ctx->self));
+ ip_family_str = mm_bearer_ip_family_build_string_from_mask (ip_family);
+ mm_dbg ("No specific IP family requested, defaulting to %s",
+ ip_family_str);
+ g_free (ip_family_str);
+ }
+
+ /* Default to automatic/DHCP addressing */
+ ctx->ipv4_config = mm_bearer_ip_config_new ();
+ mm_bearer_ip_config_set_method (ctx->ipv4_config, MM_BEARER_IP_METHOD_DHCP);
+}
+
+static gchar *
+build_cinterion_auth_string (Connect3gppContext *ctx)
+{
+ const gchar *user = NULL;
+ const gchar *passwd = NULL;
+ MMBearerAllowedAuth auth;
+ gint encoded_auth = BearerCinterionAuthUnknown;
+ gchar *command = NULL;
+
+ user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
+ passwd = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
+
+ /* Normal use case is no user & pass so return as quick as possible */
+ if (!user && !passwd)
+ return NULL;
+
+ auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
+ encoded_auth = cinterion_parse_auth_type (auth);
+
+ /* Default to no authentication if not specified */
+ if (encoded_auth == BearerCinterionAuthUnknown) {
+ encoded_auth = BearerCinterionAuthNone;
+ mm_dbg ("Unable to detect authentication type. Defaulting to:%i", encoded_auth);
+ }
+
+ /* TODO: Haven't tested this as I can't get a hold of a SIM w/ this feature atm.
+ * Write Command
+ * AT^SGAUTH=<cid>[, <auth_type>[, <passwd>, <user>]]
+ * Response(s)
+ * OK
+ * ERROR
+ * +CME ERROR: <err>
+ */
+
+ command = g_strdup_printf ("^SGAUTH=%i,%i,%s,%s",
+ ctx->self->priv->pdp_cid,
+ encoded_auth,
+ passwd,
+ user);
+
+ return command;
+}
+
+static gchar *
+build_cinterion_pdp_context_string (Connect3gppContext *ctx)
+{
+ const gchar *apn = NULL;
+ gchar *command;
+
+ apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
+
+ /* TODO: Get IP type if protocol was specified. Hardcoded to IPV4 for now */
+
+ command = g_strdup_printf ("+CGDCONT=%i,\"IP\",\"%s\"",
+ ctx->self->priv->pdp_cid,
+ apn == NULL ? "" : apn);
+
+ return command;
+}
+
+static void
+handle_cancel_connect (Connect3gppContext *ctx)
+{
+ gchar *command;
+
+ /* 3rd context -> 1st wwan adapt / 1st context -> 2nd wwan adapt */
+ command = g_strdup_printf ("^SWWAN=%s,%i,%i",
+ "0",
+ ctx->self->priv->pdp_cid,
+ ctx->self->priv->pdp_cid == 3 ? FIRST_USB_INTERFACE : SECOND_USB_INTERFACE);
+
+ /* Disconnect, may not succeed. Will not check response on cancel */
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ command,
+ 3,
+ FALSE,
+ FALSE,
+ NULL,
+ NULL,
+ NULL);
+
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_CANCELLED,
+ "Cinterion connection operation has been cancelled");
+ connect_3gpp_context_complete_and_free (ctx);
+
+}
+
+static void
+create_cinterion_bearer (Connect3gppContext *ctx)
+{
+ if (ctx->ipv4_config) {
+ g_simple_async_result_set_op_res_gpointer (
+ ctx->result,
+ mm_bearer_connect_result_new (ctx->data, ctx->ipv4_config, NULL),
+ (GDestroyNotify)mm_bearer_connect_result_unref);
+ }
+ else {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cinterion connection failed to set IP protocol");
+ }
+}
+
+/*****************************************************************************/
+/* Connect - AT Command Wrappers*/
+
+static void
+send_swwan_read_command_ctx_connect (Connect3gppContext *ctx)
+{
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ "^SWWAN?",
+ 5,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback)get_swwan_read_response_ctx_connect,
+ ctx);
+}
+
+
+static void
+send_write_command_ctx_connect (Connect3gppContext *ctx,
+ gchar **command)
+{
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ *command,
+ 10,/*Seen it take 5 seconds :0 */
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback)get_cmd_write_response_ctx_connect,
+ ctx);
+}
+
+static void
+send_swwan_connect_command_ctx_connect (Connect3gppContext *ctx)
+{
+ /* 3rd context -> 1st wwan adapt / 1st context -> 2nd wwan adapt */
+ gchar *command;
+ command = g_strdup_printf ("^SWWAN=%s,%i,%i",
+ "1",
+ ctx->self->priv->pdp_cid,
+ ctx->self->priv->pdp_cid == 3 ? FIRST_USB_INTERFACE : SECOND_USB_INTERFACE);
+
+ send_write_command_ctx_connect(ctx, &command);
+
+ g_free (command);
+}
+
+static void
+send_swwan_disconnect_command_ctx_connect (Connect3gppContext *ctx)
+{
+ /* 3rd context -> 1st wwan adapt / 1st context -> 2nd wwan adapt */
+ gchar *command;
+ command = g_strdup_printf ("^SWWAN=%s,%i,%i",
+ "0",
+ ctx->self->priv->pdp_cid,
+ ctx->self->priv->pdp_cid == 3 ? FIRST_USB_INTERFACE : SECOND_USB_INTERFACE);
+
+ send_write_command_ctx_connect(ctx, &command);
+
+ g_free (command);
+}
+
+/*****************************************************************************/
+/* Connect - Bearer */
+
+static void
+connect_3gpp_context_step (Connect3gppContext *ctx)
+{
+ /* Check for cancellation */
+ if (g_cancellable_is_cancelled (ctx->cancellable)) {
+ handle_cancel_connect(ctx);
+ return;
+ }
+
+ mm_dbg ("Connect Step:%i", ctx->connect);
+
+ /* Network-initiated disconnect should not be outstanding at this point,
+ because it interferes with the connect attempt.*/
+ g_assert (ctx->self->priv->network_disconnect_pending_id == 0);
+
+ switch (ctx->connect) {
+ case Connect3gppContextStepInit:
+
+ /* Insure no connection is currently
+ * active with the bearer we're creating.*/
+ send_swwan_disconnect_command_ctx_connect (ctx);
+
+ return;
+ case Connect3gppContextStepAuth: {
+
+ gchar *command = NULL;
+
+ command = build_cinterion_auth_string (ctx);
+
+ mm_dbg ("Auth String:%s", command);
+
+ /* Send SGAUTH write, if User & Pass are provided.
+ * advance to next state by callback */
+ if (command != NULL) {
+ mm_dbg ("Sending auth");
+
+ send_write_command_ctx_connect (ctx, &command);
+ g_free (command);
+ return;
+ }
+
+ /* GOTO next step - Fall down below */
+ ctx->connect++;
+ }
+ case Connect3gppContextStepPdpCtx: {
+ gchar *command = NULL;
+
+ command = build_cinterion_pdp_context_string (ctx);
+
+ /*Set the PDP context with cgdcont.*/
+ send_write_command_ctx_connect (ctx, &command);
+
+ g_free (command);
+ return;
+ }
+ case Connect3gppContextStepStartSwwan:
+
+ send_swwan_connect_command_ctx_connect (ctx);
+
+ return;
+ case Connect3gppContextStepValidateConnection:
+
+ send_swwan_read_command_ctx_connect (ctx);
+
+ return;
+ case Connect3gppContextStepIpConfig:
+
+ setup_ip_settings (ctx);
+
+ /* GOTO next step - Fall down below */
+ ctx->connect++;
+ case Connect3gppContextStepFinalizeBearer:
+ /* Setup bearer */
+ create_cinterion_bearer (ctx);
+
+ /* Clear context */
+ ctx->self->priv->pdp_cid = 0;
+
+ connect_3gpp_context_complete_and_free (ctx);
+ return;
+ }
+}
+
+static void
+connect_3gpp (MMBroadbandBearer *self,
+ MMBroadbandModem *modem,
+ MMPortSerialAt *primary,
+ MMPortSerialAt *secondary,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Connect3gppContext *ctx;
+ MMPort *port;
+
+ g_assert (primary != NULL);
+
+ /* Get a Net port to setup the connection on */
+ port = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET);
+ if (!port) {
+ g_simple_async_report_error_in_idle (G_OBJECT (self),
+ callback,
+ user_data,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No valid data port found to launch connection");
+ return;
+ }
+
+ /* Setup connection context */
+ ctx = g_slice_new0 (Connect3gppContext);
+ ctx->self = g_object_ref (self);
+ ctx->modem = g_object_ref (modem);
+ ctx->data = g_object_ref (port);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ connect_3gpp);
+ ctx->cancellable = g_object_ref (cancellable);
+ ctx->primary = g_object_ref (primary);
+
+ /* Maps Bearer-> Net Interface-> PDP Context */
+ pdp_cid_connect (ctx);
+
+ /* Initialize */
+ ctx->connect = Connect3gppContextStepInit;
+
+ /* Run! */
+ connect_3gpp_context_step (ctx);
+}
+
+
+/*****************************************************************************/
+/* Disconnect - Helper Functions*/
+
+static void
+pdp_cid_disconnect(Disconnect3gppContext *ctx)
+{
+ const gchar *bearer_interface;
+
+ /* For E: INTERFACE=usb0 -> E: ID_USB_INTERFACE_NUM=0a
+ * For E: INTERFACE=usb1 -> E: ID_USB_INTERFACE_NUM=0c */
+
+ /* Look up the net port to associate with this bearer */
+ bearer_interface = mm_kernel_device_get_property (mm_port_peek_kernel_device (ctx->data),
+ "ID_USB_INTERFACE_NUM");
+
+ pdp_cid_map (ctx->self, bearer_interface);
+
+ return;
+}
+
+static void
+get_cmd_write_response_ctx_disconnect (MMBaseModem *modem,
+ GAsyncResult *res,
+ Disconnect3gppContext *ctx)
+{
+ /* We don't bother to check error or response here since, ctx flow's
+ * next step checks it */
+
+ /* GOTO next step */
+ ctx->disconnect++;
+ disconnect_3gpp_context_step (ctx);
+}
+
+static void
+get_swwan_read_response_ctx_disconnect (MMBaseModem *modem,
+ GAsyncResult *res,
+ Disconnect3gppContext *ctx)
+{
+ const gchar *response;
+ GError *error = NULL;
+ GList *response_parsed = NULL;
+
+ /* Get the SWWAN response */
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+
+ /* Return if the SWWAN read threw an error or parsing it fails */
+ if (!mm_cinterion_parse_swwan_response (response, &response_parsed, &error)) {
+ g_simple_async_result_take_error (ctx->result, error);
+ disconnect_3gpp_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Check parsed SWWAN reponse to see if we are now disconnected */
+ if (verify_connection_state_from_swwan_response (response_parsed, &error) != 1) {
+
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Disconnection attempt failed");
+
+ disconnect_3gpp_context_complete_and_free (ctx);
+ return;
+ }
+
+ g_list_free (response_parsed);
+ g_clear_error (&error);
+
+ /* GOTO next step */
+ ctx->disconnect++;
+ disconnect_3gpp_context_step (ctx);
+}
+
+/*****************************************************************************/
+/* Disconnect - AT Command Wrappers*/
+
+static void
+send_swwan_disconnect_command_ctx_disconnect (Disconnect3gppContext *ctx)
+{
+ /* 3rd context -> 1st wwan adapt / 1st context -> 2nd wwan adapt */
+ gchar *command;
+ command = g_strdup_printf ("^SWWAN=%s,%i,%i",
+ "0",
+ ctx->self->priv->pdp_cid,
+ ctx->self->priv->pdp_cid == 3 ? FIRST_USB_INTERFACE : SECOND_USB_INTERFACE);
+
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ command,
+ 10,/*Seen it take 5 seconds :0 */
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback)get_cmd_write_response_ctx_disconnect,
+ ctx);
+
+ g_free (command);
+}
+
+static void
+send_swwan_read_command_ctx_disconnect (Disconnect3gppContext *ctx)
+{
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ "^SWWAN?",
+ 5,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback)get_swwan_read_response_ctx_disconnect,
+ ctx);
+}
+
+/*****************************************************************************/
+/* Disconnect - Bearer */
+
+static void
+disconnect_3gpp_context_complete_and_free (Disconnect3gppContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+ g_clear_object (&ctx->data);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->primary);
+ g_object_unref (ctx->self);
+ g_object_unref (ctx->modem);
+ g_slice_free (Disconnect3gppContext, ctx);
+}
+
+static gboolean
+disconnect_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+disconnect_3gpp_context_step (Disconnect3gppContext *ctx)
+{
+ mm_dbg ("Disconnect Step:%i", ctx->disconnect);
+
+ switch (ctx->disconnect) {
+ case Disconnect3gppContextStepStopSwwan:
+
+ /* Has call back to next state */
+ send_swwan_disconnect_command_ctx_disconnect (ctx);
+
+ return;
+ case Disconnect3gppContextStepConnectionStatus:
+
+ /* Has call back to next state */
+ send_swwan_read_command_ctx_disconnect (ctx);
+
+ return;
+
+ case Disconnect3gppContextStepFinish:
+
+ ctx->self->priv->pdp_cid = 0;
+
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ disconnect_3gpp_context_complete_and_free (ctx);
+
+ return;
+ }
+}
+
+static void
+disconnect_3gpp (MMBroadbandBearer *self,
+ MMBroadbandModem *modem,
+ MMPortSerialAt *primary,
+ MMPortSerialAt *secondary,
+ MMPort *data,
+ guint cid,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Disconnect3gppContext *ctx;
+ MMPort *port;
+
+ g_assert (primary != NULL);
+
+ /* Note: Not sure how else to get active data port? Can this be done without adding this
+ * function to mm-base-modem.c?
+ * TODO: Dual SIM how do we know which interface to grab/disconnect if two are active? */
+ /* Get the Net port to be torn down */
+ port = mm_base_modem_peek_current_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET);
+ if (!port) {
+ g_simple_async_report_error_in_idle (G_OBJECT (self),
+ callback,
+ user_data,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No valid data port found to tear down.");
+ return;
+ }
+
+ /* Setup connection context */
+ ctx = g_slice_new0 (Disconnect3gppContext);
+ ctx->self = g_object_ref (self);
+ ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ disconnect_3gpp);
+ ctx->data = g_object_ref (port);
+ ctx->primary = g_object_ref (primary);
+
+ /* Maps Bearer->Net Interface-> PDP Context
+ * We can't disconnect if we don't know which context to disconnect from. */
+ pdp_cid_disconnect (ctx);
+
+ /* Initialize */
+ ctx->disconnect = Disconnect3gppContextStepStopSwwan;
+
+ /* Start */
+ disconnect_3gpp_context_step (ctx);
+}
+
+/*****************************************************************************/
+/* Setup and Init Bearers */
+
+MMBaseBearer *
+mm_broadband_bearer_cinterion_new_finish (GAsyncResult *res,
+ GError **error)
+{
+ GObject *bearer;
+ GObject *source;
+
+ source = g_async_result_get_source_object (res);
+ bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error);
+ g_object_unref (source);
+
+ if (!bearer)
+ return NULL;
+
+ /* Only export valid bearers */
+ mm_base_bearer_export (MM_BASE_BEARER (bearer));
+
+ return MM_BASE_BEARER (bearer);
+}
+
+static void
+dispose (GObject *object)
+{
+ MMBroadbandBearerCinterion *self = MM_BROADBAND_BEARER_CINTERION (object);
+
+ if (self->priv->network_disconnect_pending_id != 0) {
+ g_source_remove (self->priv->network_disconnect_pending_id);
+ self->priv->network_disconnect_pending_id = 0;
+ }
+
+ G_OBJECT_CLASS (mm_broadband_bearer_cinterion_parent_class)->dispose (object);
+}
+
+void
+mm_broadband_bearer_cinterion_new (MMBroadbandModemCinterion *modem,
+ MMBearerProperties *config,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_async_initable_new_async (
+ MM_TYPE_BROADBAND_BEARER_CINTERION,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ callback,
+ user_data,
+ MM_BASE_BEARER_MODEM, modem,
+ MM_BASE_BEARER_CONFIG, config,
+ NULL);
+}
+
+static void
+mm_broadband_bearer_cinterion_init (MMBroadbandBearerCinterion *self)
+{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_BROADBAND_BEARER_CINTERION,
+ MMBroadbandBearerCinterionPrivate);
+}
+
+static void
+mm_broadband_bearer_cinterion_class_init (MMBroadbandBearerCinterionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMBroadbandBearerCinterionPrivate));
+
+ object_class->dispose = dispose;
+
+ broadband_bearer_class->connect_3gpp = connect_3gpp;
+ broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish;
+ broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;
+ broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish;
+}
diff --git a/plugins/cinterion/mm-broadband-bearer-cinterion.h b/plugins/cinterion/mm-broadband-bearer-cinterion.h
new file mode 100644
index 000000000..01d360599
--- /dev/null
+++ b/plugins/cinterion/mm-broadband-bearer-cinterion.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2016 Trimble Navigation Limited
+ * Author: Matthew Stanger <Matthew_Stanger@trimble.com>
+ */
+
+#ifndef MM_BROADBAND_BEARER_CINTERION_H
+#define MM_BROADBAND_BEARER_CINTERION_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "mm-broadband-bearer.h"
+#include "mm-broadband-modem-cinterion.h"
+
+#define MM_TYPE_BROADBAND_BEARER_CINTERION (mm_broadband_bearer_cinterion_get_type ())
+#define MM_BROADBAND_BEARER_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_CINTERION, MMBroadbandBearerCinterion))
+#define MM_BROADBAND_BEARER_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_CINTERION, MMBroadbandBearerCinterionClass))
+#define MM_IS_BROADBAND_BEARER_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_CINTERION))
+#define MM_IS_BROADBAND_BEARER_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_CINTERION))
+#define MM_BROADBAND_BEARER_CINTERION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_CINTERION, MMBroadbandBearerCinterionClass))
+
+typedef struct _MMBroadbandBearerCinterion MMBroadbandBearerCinterion;
+typedef struct _MMBroadbandBearerCinterionClass MMBroadbandBearerCinterionClass;
+typedef struct _MMBroadbandBearerCinterionPrivate MMBroadbandBearerCinterionPrivate;
+
+struct _MMBroadbandBearerCinterion {
+ MMBroadbandBearer parent;
+ MMBroadbandBearerCinterionPrivate *priv;
+};
+
+struct _MMBroadbandBearerCinterionClass {
+ MMBroadbandBearerClass parent;
+};
+
+GType mm_broadband_bearer_cinterion_get_type (void);
+
+void mm_broadband_bearer_cinterion_new (MMBroadbandModemCinterion *modem,
+ MMBearerProperties *config,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+MMBaseBearer *mm_broadband_bearer_cinterion_new_finish (GAsyncResult *res,
+ GError **error);
+
+#endif /* MM_BROADBAND_BEARER_CINTERION_H */
diff --git a/plugins/cinterion/mm-broadband-modem-cinterion.c b/plugins/cinterion/mm-broadband-modem-cinterion.c
index 4882a4188..28bdc404f 100644
--- a/plugins/cinterion/mm-broadband-modem-cinterion.c
+++ b/plugins/cinterion/mm-broadband-modem-cinterion.c
@@ -12,7 +12,9 @@
*
* Copyright (C) 2011 Ammonit Measurement GmbH
* Copyright (C) 2011 Google Inc.
+ * Copyright (C) 2016 Trimble Navigation Limited
* Author: Aleksander Morgado <aleksander@lanedo.com>
+ * Contributor: Matthew Stanger <matthew_stanger@trimble.com>
*/
#include <config.h>
@@ -36,6 +38,7 @@
#include "mm-broadband-modem-cinterion.h"
#include "mm-modem-helpers-cinterion.h"
#include "mm-common-cinterion.h"
+#include "mm-broadband-bearer-cinterion.h"
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
@@ -50,6 +53,12 @@ G_DEFINE_TYPE_EXTENDED (MMBroadbandModemCinterion, mm_broadband_modem_cinterion,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init))
+typedef enum {
+ FeatureSupportUnknown,
+ FeatureNotSupported,
+ FeatureSupported
+} FeatureSupport;
+
struct _MMBroadbandModemCinterionPrivate {
/* Flag to know if we should try AT^SIND or not to get psinfo */
gboolean sind_psinfo;
@@ -69,6 +78,9 @@ struct _MMBroadbandModemCinterionPrivate {
GArray *cnmi_supported_bm;
GArray *cnmi_supported_ds;
GArray *cnmi_supported_bfr;
+
+ /*Flags for SWWAN support*/
+ FeatureSupport swwan_support;
};
/*****************************************************************************/
@@ -727,10 +739,16 @@ get_access_technology_from_psinfo (const gchar *psinfo,
case 9:
case 10:
return (MM_MODEM_ACCESS_TECHNOLOGY_HSDPA | MM_MODEM_ACCESS_TECHNOLOGY_HSUPA);
+ case 16:
+ case 17:
+ return MM_MODEM_ACCESS_TECHNOLOGY_LTE;
default:
+ mm_dbg ("Unable to identify access technology in case:%i", psinfoval);
break;
}
}
+ else
+ mm_err ("FAILED get_access_technology_from_psinfo-int");
g_set_error (error,
MM_CORE_ERROR,
@@ -811,6 +829,7 @@ load_access_technologies (MMIfaceModem *self,
load_access_technologies);
if (broadband->priv->sind_psinfo) {
+ /* TODO: Trigger off psinfo URC instead of this polling. */
mm_base_modem_at_command (
MM_BASE_MODEM (self),
"^SIND?",
@@ -1636,6 +1655,99 @@ after_sim_unlock (MMIfaceModem *self,
}
/*****************************************************************************/
+/* Initializing the modem (during first enabling) */
+
+typedef struct {
+ GSimpleAsyncResult *result;
+ MMBroadbandModemCinterion *self;
+} EnablingModemInitContext;
+
+static void
+enabling_modem_init_context_complete_and_free (EnablingModemInitContext *ctx)
+{
+ g_simple_async_result_complete (ctx->result);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->self);
+ g_slice_free (EnablingModemInitContext, ctx);
+}
+
+static gboolean
+enabling_modem_init_finish (MMBroadbandModem *self,
+ GAsyncResult *res,
+ GError **error)
+
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+swwan_test_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ EnablingModemInitContext *ctx)
+{
+ MMPort *port = NULL;
+ GError *error = NULL;
+
+ /* Fetch the result to the SWWAN test */
+ mm_base_modem_at_command_full_finish (self, res, &error);
+
+ port = mm_base_modem_peek_best_data_port (self, MM_PORT_TYPE_NET);
+
+ /* SWWAN requires a net port & valid test response*/
+ if (mm_port_get_subsys (port) == MM_PORT_SUBSYS_NET &&
+ !error) {
+ mm_dbg ("SWWAN supported");
+ ctx->self->priv->swwan_support = FeatureSupported;
+ }
+ else {
+ mm_dbg ("SWWAN unsupported");
+ ctx->self->priv->swwan_support = FeatureNotSupported;
+ }
+
+ if (error) /* Error is not valid */
+ g_clear_error (&error);
+
+ enabling_modem_init_context_complete_and_free (ctx);
+}
+
+static void
+check_for_swwan_support (EnablingModemInitContext *ctx)
+{
+ mm_base_modem_at_command_full (MM_BASE_MODEM (ctx->self),
+ mm_base_modem_peek_port_primary (MM_BASE_MODEM (ctx->self)),
+ "^SWWAN=?",
+ 6,
+ FALSE,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)swwan_test_ready,
+ ctx);
+}
+
+static void
+enabling_modem_init (MMBroadbandModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ EnablingModemInitContext *ctx;
+
+ ctx = g_slice_new0 (EnablingModemInitContext);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ enabling_modem_init);
+ ctx->self = g_object_ref (self);
+
+ /* Newer Cinterion modems may support SWWAN, which is the same as WWAN.
+ * Check to see if current modem supports it.*/
+ check_for_swwan_support (ctx);
+
+ /* TODO: Before upstream merge. This needs check_for_swwan_support to block
+ * until it's finish. Otherwise there is race conidion with swwan_support assert?
+ * I'm really not sure how else to do this other than move it or use sleep :? */
+}
+
+/*****************************************************************************/
/* Setup ports (Broadband modem class) */
static void
@@ -1648,6 +1760,116 @@ setup_ports (MMBroadbandModem *self)
}
/*****************************************************************************/
+/* Create Bearer (Modem interface) */
+
+typedef struct {
+ MMBroadbandModemCinterion *self;
+ GSimpleAsyncResult *result;
+ MMBearerProperties *properties;
+} CreateBearerContext;
+
+static void
+create_bearer_context_complete_and_free (CreateBearerContext *ctx)
+{
+ g_simple_async_result_complete (ctx->result);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->self);
+ g_object_unref (ctx->properties);
+ g_slice_free (CreateBearerContext, ctx);
+}
+
+static MMBaseBearer *
+cinterion_modem_create_bearer_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ MMBaseBearer *bearer;
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return NULL;
+
+ bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ mm_dbg ("New cinterion bearer created at DBus path '%s'", mm_base_bearer_get_path (bearer));
+ return g_object_ref (bearer);
+}
+
+static void
+broadband_bearer_cinterion_new_ready (GObject *source,
+ GAsyncResult *res,
+ CreateBearerContext *ctx)
+{
+ MMBaseBearer *bearer;
+ GError *error = NULL;
+
+ bearer = mm_broadband_bearer_cinterion_new_finish (res, &error);
+ if (!bearer)
+ g_simple_async_result_take_error (ctx->result, error);
+ else
+ g_simple_async_result_set_op_res_gpointer (ctx->result, bearer, (GDestroyNotify)g_object_unref);
+ create_bearer_context_complete_and_free (ctx);
+}
+
+static void
+broadband_bearer_new_ready (GObject *source,
+ GAsyncResult *res,
+ CreateBearerContext *ctx)
+{
+ MMBaseBearer *bearer;
+ GError *error = NULL;
+
+ bearer = mm_broadband_bearer_new_finish (res, &error);
+ if (!bearer)
+ g_simple_async_result_take_error (ctx->result, error);
+ else
+ g_simple_async_result_set_op_res_gpointer (ctx->result, bearer, (GDestroyNotify)g_object_unref);
+ create_bearer_context_complete_and_free (ctx);
+}
+
+static void
+create_bearer_for_net_port (CreateBearerContext *ctx)
+{
+ switch (ctx->self->priv->swwan_support) {
+ case FeatureNotSupported:
+ mm_dbg ("^SWWAN not supported, creating default bearer...");
+ mm_broadband_bearer_new (MM_BROADBAND_MODEM (ctx->self),
+ ctx->properties,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)broadband_bearer_new_ready,
+ ctx);
+ return;
+ case FeatureSupported:
+ mm_dbg ("^SWWAN supported, creating cinterion bearer...");
+ mm_broadband_bearer_cinterion_new (MM_BROADBAND_MODEM_CINTERION (ctx->self),
+ ctx->properties,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)broadband_bearer_cinterion_new_ready,
+ ctx);
+ return;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+cinterion_modem_create_bearer (MMIfaceModem *self,
+ MMBearerProperties *properties,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ CreateBearerContext *ctx = NULL;
+
+ ctx = g_slice_new0 (CreateBearerContext);
+ ctx->self = g_object_ref (self);
+ ctx->properties = g_object_ref (properties);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ cinterion_modem_create_bearer);
+
+ create_bearer_for_net_port (ctx);
+}
+
+/*****************************************************************************/
MMBroadbandModemCinterion *
mm_broadband_modem_cinterion_new (const gchar *device,
@@ -1673,8 +1895,9 @@ mm_broadband_modem_cinterion_init (MMBroadbandModemCinterion *self)
MM_TYPE_BROADBAND_MODEM_CINTERION,
MMBroadbandModemCinterionPrivate);
- /* Set defaults */
+ /* Initialize private variables */
self->priv->sind_psinfo = TRUE; /* Initially, always try to get psinfo */
+ self->priv->swwan_support = FeatureSupportUnknown;
}
static void
@@ -1704,6 +1927,8 @@ iface_modem_init (MMIfaceModem *iface)
{
iface_modem_parent = g_type_interface_peek_parent (iface);
+ iface->create_bearer = cinterion_modem_create_bearer;
+ iface->create_bearer_finish = cinterion_modem_create_bearer_finish;
iface->load_supported_modes = load_supported_modes;
iface->load_supported_modes_finish = load_supported_modes_finish;
iface->set_current_modes = set_current_modes;
@@ -1770,4 +1995,6 @@ mm_broadband_modem_cinterion_class_init (MMBroadbandModemCinterionClass *klass)
/* Virtual methods */
object_class->finalize = finalize;
broadband_modem_class->setup_ports = setup_ports;
+ broadband_modem_class->enabling_modem_init = enabling_modem_init;
+ broadband_modem_class->enabling_modem_init_finish = enabling_modem_init_finish;
}
diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.c b/plugins/cinterion/mm-modem-helpers-cinterion.c
index bffe00a9e..6312e7821 100644
--- a/plugins/cinterion/mm-modem-helpers-cinterion.c
+++ b/plugins/cinterion/mm-modem-helpers-cinterion.c
@@ -10,12 +10,15 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
+ * Copyright (C) 2016 Trimble Navigation Limited
* Copyright (C) 2014 Aleksander Morgado <aleksander@aleksander.es>
+ * Contributor: Matthew Stanger <matthew_stanger@trimble.com>
*/
#include <config.h>
#include <string.h>
#include <stdlib.h>
+#include <stdio.h>
#include "ModemManager.h"
#define _LIBMM_INSIDE_MM
@@ -24,6 +27,7 @@
#include "mm-charsets.h"
#include "mm-errors-types.h"
#include "mm-modem-helpers-cinterion.h"
+#include "mm-modem-helpers.h"
/* Setup relationship between the 3G band bitmask in the modem and the bitmask
* in ModemManager. */
@@ -482,3 +486,80 @@ mm_cinterion_parse_sind_response (const gchar *response,
return TRUE;
}
+
+/*****************************************************************************/
+/* ^SWWAN read parser
+ * Description: Parses <cid>, <state>[, <WWAN adapter>] or CME ERROR from SWWAN.
+ * Return: False if error occured while trying to parse SWWAN.
+ * Read Command
+ * AT^SWWAN?
+ * Response(s)
+ * [^SWWAN: <cid>, <state>[, <WWAN adapter>]]
+ * [^SWWAN: ...]
+ * OK
+ * ERROR
+ * +CME ERROR: <err>
+ *
+ * Examples:
+ * OK - If no WWAN connection is active, then read command just returns OK
+ * ^SWWAN: 3,1,1 - 3rd PDP Context, Activated, First WWAN Adaptor
+ * +CME ERROR: ? -
+*/
+
+gboolean
+mm_cinterion_parse_swwan_response (const gchar *response,
+ GList **result,
+ GError **error)
+{
+ if (*error)
+ return FALSE;
+
+ if (!response) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Recieved NULL from SWWAN response.");
+ return FALSE;
+ }
+
+ /* Parse for [^SWWAN: <cid>, <state>[, <WWAN adapter>]] */
+ if (strcasestr (response, "SWWAN")) {
+ gint matched;
+ guint cid, state, wwan_adapter;
+ matched = sscanf (response, "^SWWAN: %d,%d,%d",
+ &cid,
+ &state,
+ &wwan_adapter);
+
+ if (matched != 3 ||
+ cid < 1 || cid > 16 ||
+ state < 0 || state > 1 ||
+ wwan_adapter < 1 || wwan_adapter > 2) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Invalid format for SWWAN response: '%s'",
+ response);
+ return FALSE;
+ }
+
+ *result = g_list_append (*result, GINT_TO_POINTER(cid));
+ *result = g_list_append (*result, GINT_TO_POINTER(state));
+ *result = g_list_append (*result, GINT_TO_POINTER(wwan_adapter));
+ }
+ /* TODO: It'd be nice to get 'OK' from response so we don't have to assume that
+ * zero length response means 'OK' or am I doing it wrong?... */
+ else if (!g_utf8_strlen (response, 100))
+ *result = g_list_append (*result, GINT_TO_POINTER(0));
+ else {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Could not parse SWWAN response: '%s'",
+ response);
+ return FALSE;
+ }
+
+
+ return TRUE;
+}
diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.h b/plugins/cinterion/mm-modem-helpers-cinterion.h
index d37bcb070..c94341ec4 100644
--- a/plugins/cinterion/mm-modem-helpers-cinterion.h
+++ b/plugins/cinterion/mm-modem-helpers-cinterion.h
@@ -10,7 +10,9 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
+ * Copyright (C) 2016 Trimble Navigation Limited
* Copyright (C) 2014 Aleksander Morgado <aleksander@aleksander.es>
+ * Contributor: Matthew Stanger <matthew_stanger@trimble.com>
*/
#ifndef MM_MODEM_HELPERS_CINTERION_H
@@ -63,4 +65,9 @@ gboolean mm_cinterion_parse_sind_response (const gchar *response,
guint *value,
GError **error);
+/*****************************************************************************/
+/* ^SWWAN response parser */
+gboolean mm_cinterion_parse_swwan_response (const gchar *response,
+ GList **result,
+ GError **error);
#endif /* MM_MODEM_HELPERS_CINTERION_H */
diff --git a/plugins/cinterion/tests/test-modem-helpers-cinterion.c b/plugins/cinterion/tests/test-modem-helpers-cinterion.c
index 26b07ab28..a59071e28 100644
--- a/plugins/cinterion/tests/test-modem-helpers-cinterion.c
+++ b/plugins/cinterion/tests/test-modem-helpers-cinterion.c
@@ -329,6 +329,140 @@ test_cnmi_phs8 (void)
g_array_unref (expected_ds);
g_array_unref (expected_bfr);}
+static void
+test_swwan_parser (const GArray *expected_cid,
+ const GArray *expected_state,
+ const GArray *expected_adapter,
+ const gchar *at_cmd,
+ const gboolean test_for_errors)
+{
+ GError *error = NULL;
+ gboolean res = TRUE;
+ gchar *response;
+ guint i, j, k;
+
+ g_assert (expected_cid != NULL);
+ g_assert (expected_state != NULL);
+ g_assert (expected_adapter != NULL);
+
+ /* For each expected_cid */
+ for (i = 0; i < expected_cid->len; i++) {
+ /* For each expected_state */
+ for (j = 0; j < expected_state->len; j++) {
+ /* For each expected_adapter */
+ for (k = 0; k < expected_adapter->len; k++) {
+ GList *response_parsed = NULL;
+
+ /* Build a unique at_cmd string */
+ response = g_strdup_printf ("%s: %i,%i,%i\r\n\r\n",
+ at_cmd,
+ g_array_index (expected_cid, guint, i),
+ g_array_index (expected_state, guint, j),
+ g_array_index (expected_adapter, guint, k));
+
+ /* and send it to the parser */
+ res = mm_cinterion_parse_swwan_response (response,
+ &response_parsed,
+ &error);
+
+ if (test_for_errors) {
+ /* There should be errors raised */
+ g_assert (res == FALSE);
+ g_assert (error != NULL);
+
+ /* reset the error's everytime */
+ res = TRUE;
+ g_clear_error (&error);
+ }
+ else {
+ /* The parsed values we get back should match the AT string we sent */
+ g_assert (g_array_index (expected_cid, guint, i) ==
+ GPOINTER_TO_INT(g_list_nth_data (response_parsed, 0)));
+ g_assert (g_array_index (expected_state, guint, j) ==
+ GPOINTER_TO_INT(g_list_nth_data (response_parsed, 1)));
+ g_assert (g_array_index (expected_adapter, guint, k) ==
+ GPOINTER_TO_INT(g_list_nth_data (response_parsed, 2)));
+
+ /* and there should be no errors raised */
+ g_assert (res == TRUE);
+ g_assert_no_error (error);
+ }
+
+ g_list_free(response_parsed);
+ }
+ }
+ }
+}
+
+static void
+test_swwan_pls8 (void)
+{
+ GArray *good_cid;
+ GArray *good_state;
+ GArray *good_adapter;
+ GArray *bad_cid;
+ GArray *bad_state;
+ GArray *bad_adapter;
+ guint i;
+ guint val;
+
+ /* AT^SWWAN=? -> '^SWWAN: (0,1),(1-16),(1,2)' */
+ /* Setup array with good SWWAN values */
+ good_cid = g_array_sized_new (FALSE, FALSE, sizeof (guint), 16);
+ for (i = 1; i < 17; i++)
+ val = i, g_array_append_val (good_cid, val);
+
+ good_state = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2);
+ val = 0, g_array_append_val (good_state, val);
+ val = 1, g_array_append_val (good_state, val);
+
+ good_adapter = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2);
+ val = 1, g_array_append_val (good_adapter, val);
+ val = 2, g_array_append_val (good_adapter, val);
+
+ /* and test */
+ test_swwan_parser (good_cid,
+ good_state,
+ good_adapter,
+ "^SWWAN",
+ FALSE);
+
+ /* Setup array with bad SWWAN values */
+ bad_cid = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2);
+ val = -1, g_array_append_val (bad_cid, val);
+ val = 17, g_array_append_val (bad_cid, val);
+
+ bad_state = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2);
+ val = -1, g_array_append_val (bad_state, val);
+ val = 2, g_array_append_val (bad_state, val);
+
+ bad_adapter = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2);
+ val = -1, g_array_append_val (bad_adapter, val);
+ val = 0, g_array_append_val (bad_adapter, val);
+ val = 3, g_array_append_val (bad_adapter, val);
+
+ /* and test */
+ test_swwan_parser (bad_cid,
+ bad_state,
+ bad_adapter,
+ "^SWWAN",
+ TRUE);
+
+ /* and again with a bad cmd */
+ test_swwan_parser (bad_cid,
+ bad_state,
+ bad_adapter,
+ "^GARBAGE",
+ TRUE);
+
+
+ g_array_unref (good_cid);
+ g_array_unref (good_state);
+ g_array_unref (good_adapter);
+ g_array_unref (bad_cid);
+ g_array_unref (bad_state);
+ g_array_unref (bad_adapter);
+}
/*****************************************************************************/
/* Test ^SIND responses */
@@ -399,6 +533,7 @@ int main (int argc, char **argv)
g_test_add_func ("/MM/cinterion/scfg/response/2g", test_scfg_response_2g);
g_test_add_func ("/MM/cinterion/scfg/response/2g/ucs2", test_scfg_response_2g_ucs2);
g_test_add_func ("/MM/cinterion/cnmi/phs8", test_cnmi_phs8);
+ g_test_add_func ("/MM/cinterion/swwan/pls8", test_swwan_pls8);
g_test_add_func ("/MM/cinterion/sind/response/simstatus", test_sind_response_simstatus);
return g_test_run ();
diff --git a/src/mm-base-modem.c b/src/mm-base-modem.c
index 50717471b..465ceb594 100644
--- a/src/mm-base-modem.c
+++ b/src/mm-base-modem.c
@@ -697,6 +697,26 @@ mm_base_modem_peek_best_data_port (MMBaseModem *self,
return NULL;
}
+MMPort *
+mm_base_modem_peek_current_data_port (MMBaseModem *self,
+ MMPortType type)
+{
+ GList *l;
+
+ g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL);
+
+ /* Return first connected data port */
+ for (l = self->priv->data; l; l = g_list_next (l)) {
+ if (mm_port_get_connected ((MMPort *)l->data) &&
+ (mm_port_get_port_type ((MMPort *)l->data) == type ||
+ type == MM_PORT_TYPE_UNKNOWN)) {
+ return (MMPort *)l->data;
+ }
+ }
+
+ return NULL;
+}
+
GList *
mm_base_modem_get_data_ports (MMBaseModem *self)
{
diff --git a/src/mm-base-modem.h b/src/mm-base-modem.h
index db3d54d3f..6a5b88c54 100644
--- a/src/mm-base-modem.h
+++ b/src/mm-base-modem.h
@@ -131,6 +131,7 @@ MMPortMbim *mm_base_modem_peek_port_mbim_for_data (MMBaseModem *self, MMPo
#endif
MMPortSerialAt *mm_base_modem_peek_best_at_port (MMBaseModem *self, GError **error);
MMPort *mm_base_modem_peek_best_data_port (MMBaseModem *self, MMPortType type);
+MMPort *mm_base_modem_peek_current_data_port (MMBaseModem *self, MMPortType type);
GList *mm_base_modem_peek_data_ports (MMBaseModem *self);
MMPortSerialAt *mm_base_modem_get_port_primary (MMBaseModem *self);