cinterion: implement support for the new SWWAN interface
This commit is contained in:

committed by
Aleksander Morgado

parent
2f12d96fe4
commit
1ffcb16349
1
AUTHORS
1
AUTHORS
@@ -69,3 +69,4 @@ Other contributors:
|
|||||||
Vitaly Gimly
|
Vitaly Gimly
|
||||||
Yuri Chornoivan
|
Yuri Chornoivan
|
||||||
kuonirat
|
kuonirat
|
||||||
|
Matthew Stanger
|
||||||
|
@@ -646,6 +646,8 @@ libmm_plugin_cinterion_la_SOURCES = \
|
|||||||
cinterion/mm-common-cinterion.h \
|
cinterion/mm-common-cinterion.h \
|
||||||
cinterion/mm-broadband-modem-cinterion.c \
|
cinterion/mm-broadband-modem-cinterion.c \
|
||||||
cinterion/mm-broadband-modem-cinterion.h \
|
cinterion/mm-broadband-modem-cinterion.h \
|
||||||
|
cinterion/mm-broadband-bearer-cinterion.c \
|
||||||
|
cinterion/mm-broadband-bearer-cinterion.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
if WITH_QMI
|
if WITH_QMI
|
||||||
libmm_plugin_cinterion_la_SOURCES += \
|
libmm_plugin_cinterion_la_SOURCES += \
|
||||||
|
902
plugins/cinterion/mm-broadband-bearer-cinterion.c
Normal file
902
plugins/cinterion/mm-broadband-bearer-cinterion.c
Normal file
@@ -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;
|
||||||
|
}
|
57
plugins/cinterion/mm-broadband-bearer-cinterion.h
Normal file
57
plugins/cinterion/mm-broadband-bearer-cinterion.h
Normal file
@@ -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 */
|
@@ -12,7 +12,9 @@
|
|||||||
*
|
*
|
||||||
* Copyright (C) 2011 Ammonit Measurement GmbH
|
* Copyright (C) 2011 Ammonit Measurement GmbH
|
||||||
* Copyright (C) 2011 Google Inc.
|
* Copyright (C) 2011 Google Inc.
|
||||||
|
* Copyright (C) 2016 Trimble Navigation Limited
|
||||||
* Author: Aleksander Morgado <aleksander@lanedo.com>
|
* Author: Aleksander Morgado <aleksander@lanedo.com>
|
||||||
|
* Contributor: Matthew Stanger <matthew_stanger@trimble.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
@@ -36,6 +38,7 @@
|
|||||||
#include "mm-broadband-modem-cinterion.h"
|
#include "mm-broadband-modem-cinterion.h"
|
||||||
#include "mm-modem-helpers-cinterion.h"
|
#include "mm-modem-helpers-cinterion.h"
|
||||||
#include "mm-common-cinterion.h"
|
#include "mm-common-cinterion.h"
|
||||||
|
#include "mm-broadband-bearer-cinterion.h"
|
||||||
|
|
||||||
static void iface_modem_init (MMIfaceModem *iface);
|
static void iface_modem_init (MMIfaceModem *iface);
|
||||||
static void iface_modem_3gpp_init (MMIfaceModem3gpp *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_MESSAGING, iface_modem_messaging_init)
|
||||||
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init))
|
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init))
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FeatureSupportUnknown,
|
||||||
|
FeatureNotSupported,
|
||||||
|
FeatureSupported
|
||||||
|
} FeatureSupport;
|
||||||
|
|
||||||
struct _MMBroadbandModemCinterionPrivate {
|
struct _MMBroadbandModemCinterionPrivate {
|
||||||
/* Flag to know if we should try AT^SIND or not to get psinfo */
|
/* Flag to know if we should try AT^SIND or not to get psinfo */
|
||||||
gboolean sind_psinfo;
|
gboolean sind_psinfo;
|
||||||
@@ -69,6 +78,9 @@ struct _MMBroadbandModemCinterionPrivate {
|
|||||||
GArray *cnmi_supported_bm;
|
GArray *cnmi_supported_bm;
|
||||||
GArray *cnmi_supported_ds;
|
GArray *cnmi_supported_ds;
|
||||||
GArray *cnmi_supported_bfr;
|
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 9:
|
||||||
case 10:
|
case 10:
|
||||||
return (MM_MODEM_ACCESS_TECHNOLOGY_HSDPA | MM_MODEM_ACCESS_TECHNOLOGY_HSUPA);
|
return (MM_MODEM_ACCESS_TECHNOLOGY_HSDPA | MM_MODEM_ACCESS_TECHNOLOGY_HSUPA);
|
||||||
|
case 16:
|
||||||
|
case 17:
|
||||||
|
return MM_MODEM_ACCESS_TECHNOLOGY_LTE;
|
||||||
default:
|
default:
|
||||||
|
mm_dbg ("Unable to identify access technology in case:%i", psinfoval);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
mm_err ("FAILED get_access_technology_from_psinfo-int");
|
||||||
|
|
||||||
g_set_error (error,
|
g_set_error (error,
|
||||||
MM_CORE_ERROR,
|
MM_CORE_ERROR,
|
||||||
@@ -811,6 +829,7 @@ load_access_technologies (MMIfaceModem *self,
|
|||||||
load_access_technologies);
|
load_access_technologies);
|
||||||
|
|
||||||
if (broadband->priv->sind_psinfo) {
|
if (broadband->priv->sind_psinfo) {
|
||||||
|
/* TODO: Trigger off psinfo URC instead of this polling. */
|
||||||
mm_base_modem_at_command (
|
mm_base_modem_at_command (
|
||||||
MM_BASE_MODEM (self),
|
MM_BASE_MODEM (self),
|
||||||
"^SIND?",
|
"^SIND?",
|
||||||
@@ -1635,6 +1654,99 @@ after_sim_unlock (MMIfaceModem *self,
|
|||||||
after_sim_unlock_context_step (ctx);
|
after_sim_unlock_context_step (ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* 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) */
|
/* Setup ports (Broadband modem class) */
|
||||||
|
|
||||||
@@ -1647,6 +1759,116 @@ setup_ports (MMBroadbandModem *self)
|
|||||||
mm_common_cinterion_setup_gps_port (self);
|
mm_common_cinterion_setup_gps_port (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 *
|
MMBroadbandModemCinterion *
|
||||||
@@ -1673,8 +1895,9 @@ mm_broadband_modem_cinterion_init (MMBroadbandModemCinterion *self)
|
|||||||
MM_TYPE_BROADBAND_MODEM_CINTERION,
|
MM_TYPE_BROADBAND_MODEM_CINTERION,
|
||||||
MMBroadbandModemCinterionPrivate);
|
MMBroadbandModemCinterionPrivate);
|
||||||
|
|
||||||
/* Set defaults */
|
/* Initialize private variables */
|
||||||
self->priv->sind_psinfo = TRUE; /* Initially, always try to get psinfo */
|
self->priv->sind_psinfo = TRUE; /* Initially, always try to get psinfo */
|
||||||
|
self->priv->swwan_support = FeatureSupportUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -1704,6 +1927,8 @@ iface_modem_init (MMIfaceModem *iface)
|
|||||||
{
|
{
|
||||||
iface_modem_parent = g_type_interface_peek_parent (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 = load_supported_modes;
|
||||||
iface->load_supported_modes_finish = load_supported_modes_finish;
|
iface->load_supported_modes_finish = load_supported_modes_finish;
|
||||||
iface->set_current_modes = set_current_modes;
|
iface->set_current_modes = set_current_modes;
|
||||||
@@ -1770,4 +1995,6 @@ mm_broadband_modem_cinterion_class_init (MMBroadbandModemCinterionClass *klass)
|
|||||||
/* Virtual methods */
|
/* Virtual methods */
|
||||||
object_class->finalize = finalize;
|
object_class->finalize = finalize;
|
||||||
broadband_modem_class->setup_ports = setup_ports;
|
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;
|
||||||
}
|
}
|
||||||
|
@@ -10,12 +10,15 @@
|
|||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details:
|
* GNU General Public License for more details:
|
||||||
*
|
*
|
||||||
|
* Copyright (C) 2016 Trimble Navigation Limited
|
||||||
* Copyright (C) 2014 Aleksander Morgado <aleksander@aleksander.es>
|
* Copyright (C) 2014 Aleksander Morgado <aleksander@aleksander.es>
|
||||||
|
* Contributor: Matthew Stanger <matthew_stanger@trimble.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "ModemManager.h"
|
#include "ModemManager.h"
|
||||||
#define _LIBMM_INSIDE_MM
|
#define _LIBMM_INSIDE_MM
|
||||||
@@ -24,6 +27,7 @@
|
|||||||
#include "mm-charsets.h"
|
#include "mm-charsets.h"
|
||||||
#include "mm-errors-types.h"
|
#include "mm-errors-types.h"
|
||||||
#include "mm-modem-helpers-cinterion.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
|
/* Setup relationship between the 3G band bitmask in the modem and the bitmask
|
||||||
* in ModemManager. */
|
* in ModemManager. */
|
||||||
@@ -480,5 +484,82 @@ mm_cinterion_parse_sind_response (const gchar *response,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,9 @@
|
|||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details:
|
* GNU General Public License for more details:
|
||||||
*
|
*
|
||||||
|
* Copyright (C) 2016 Trimble Navigation Limited
|
||||||
* Copyright (C) 2014 Aleksander Morgado <aleksander@aleksander.es>
|
* Copyright (C) 2014 Aleksander Morgado <aleksander@aleksander.es>
|
||||||
|
* Contributor: Matthew Stanger <matthew_stanger@trimble.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MM_MODEM_HELPERS_CINTERION_H
|
#ifndef MM_MODEM_HELPERS_CINTERION_H
|
||||||
@@ -63,4 +65,9 @@ gboolean mm_cinterion_parse_sind_response (const gchar *response,
|
|||||||
guint *value,
|
guint *value,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* ^SWWAN response parser */
|
||||||
|
gboolean mm_cinterion_parse_swwan_response (const gchar *response,
|
||||||
|
GList **result,
|
||||||
|
GError **error);
|
||||||
#endif /* MM_MODEM_HELPERS_CINTERION_H */
|
#endif /* MM_MODEM_HELPERS_CINTERION_H */
|
||||||
|
@@ -329,6 +329,140 @@ test_cnmi_phs8 (void)
|
|||||||
g_array_unref (expected_ds);
|
g_array_unref (expected_ds);
|
||||||
g_array_unref (expected_bfr);}
|
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 */
|
/* 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", 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/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/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);
|
g_test_add_func ("/MM/cinterion/sind/response/simstatus", test_sind_response_simstatus);
|
||||||
|
|
||||||
return g_test_run ();
|
return g_test_run ();
|
||||||
|
@@ -697,6 +697,26 @@ mm_base_modem_peek_best_data_port (MMBaseModem *self,
|
|||||||
return NULL;
|
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 *
|
GList *
|
||||||
mm_base_modem_get_data_ports (MMBaseModem *self)
|
mm_base_modem_get_data_ports (MMBaseModem *self)
|
||||||
{
|
{
|
||||||
|
@@ -131,6 +131,7 @@ MMPortMbim *mm_base_modem_peek_port_mbim_for_data (MMBaseModem *self, MMPo
|
|||||||
#endif
|
#endif
|
||||||
MMPortSerialAt *mm_base_modem_peek_best_at_port (MMBaseModem *self, GError **error);
|
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_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);
|
GList *mm_base_modem_peek_data_ports (MMBaseModem *self);
|
||||||
|
|
||||||
MMPortSerialAt *mm_base_modem_get_port_primary (MMBaseModem *self);
|
MMPortSerialAt *mm_base_modem_get_port_primary (MMBaseModem *self);
|
||||||
|
Reference in New Issue
Block a user