Files
ModemManager/plugins/cinterion/mm-broadband-modem-cinterion.c
T
Aleksander Morgado b3f315ad2d core,log: new 'MSG' log level between 'INFO' and 'WARN'
We're bumping the current "INFO" level messages to the new "MSG"
level, also making the new level the default.

The old "INFO" level will be used to setup an intermediate level of
logging which is not as verbose as "DEBUG" but still provides some
capabilities to analyze the behavior of a modem.
2022-11-04 13:12:56 +00:00

3357 lines
118 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
* Copyright (C) 2011 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>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include "ModemManager.h"
#include "mm-modem-helpers.h"
#include "mm-serial-parsers.h"
#include "mm-log-object.h"
#include "mm-errors-types.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
#include "mm-iface-modem-messaging.h"
#include "mm-iface-modem-location.h"
#include "mm-iface-modem-voice.h"
#include "mm-base-modem-at.h"
#include "mm-broadband-modem-cinterion.h"
#include "mm-modem-helpers-cinterion.h"
#include "mm-shared-cinterion.h"
#include "mm-broadband-bearer-cinterion.h"
#include "mm-iface-modem-signal.h"
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
static void iface_modem_messaging_init (MMIfaceModemMessaging *iface);
static void iface_modem_location_init (MMIfaceModemLocation *iface);
static void iface_modem_voice_init (MMIfaceModemVoice *iface);
static void iface_modem_time_init (MMIfaceModemTime *iface);
static void iface_modem_signal_init (MMIfaceModemSignal *iface);
static void shared_cinterion_init (MMSharedCinterion *iface);
static MMIfaceModem *iface_modem_parent;
static MMIfaceModem3gpp *iface_modem_3gpp_parent;
static MMIfaceModemLocation *iface_modem_location_parent;
static MMIfaceModemVoice *iface_modem_voice_parent;
static MMIfaceModemTime *iface_modem_time_parent;
static MMIfaceModemSignal *iface_modem_signal_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemCinterion, mm_broadband_modem_cinterion, MM_TYPE_BROADBAND_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIGNAL, iface_modem_signal_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_CINTERION, shared_cinterion_init))
typedef enum {
FEATURE_SUPPORT_UNKNOWN,
FEATURE_NOT_SUPPORTED,
FEATURE_SUPPORTED,
} FeatureSupport;
struct _MMBroadbandModemCinterionPrivate {
/* Command to go into sleep mode */
gchar *sleep_mode_cmd;
/* Cached supported bands in Cinterion format */
guint supported_bands[MM_CINTERION_RB_BLOCK_N];
/* Cached supported modes for SMS setup */
GArray *cnmi_supported_mode;
GArray *cnmi_supported_mt;
GArray *cnmi_supported_bm;
GArray *cnmi_supported_ds;
GArray *cnmi_supported_bfr;
/* Cached supported rats for SXRAT */
GArray *sxrat_supported_rat;
GArray *sxrat_supported_pref1;
/* ignore regex */
GRegex *sysstart_regex;
/* +CIEV indications as configured via AT^SIND */
GRegex *ciev_regex;
/* Ignore SIM hotswap SCKS msg, until ready */
GRegex *scks_regex;
/* Flags for feature support checks */
FeatureSupport swwan_support;
FeatureSupport sind_psinfo_support;
FeatureSupport smoni_support;
FeatureSupport sind_simstatus_support;
FeatureSupport sxrat_support;
/* Mode combination to apply if "any" requested */
MMModemMode any_allowed;
/* Flags for model-based behaviors */
MMCinterionModemFamily modem_family;
MMCinterionRadioBandFormat rb_format;
/* Initial EPS bearer context number */
gint initial_eps_bearer_cid;
};
/*****************************************************************************/
MMCinterionModemFamily
mm_broadband_modem_cinterion_get_family (MMBroadbandModemCinterion *self)
{
return self->priv->modem_family;
}
/*****************************************************************************/
/* Check support (Signal interface) */
static gboolean
signal_check_support_finish (MMIfaceModemSignal *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_signal_check_support_ready (MMIfaceModemSignal *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!iface_modem_signal_parent->check_support_finish (self, res, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
check_smoni_support (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
/* Fetch the result to the SMONI test. If no response given (error triggered), assume unsupported */
if (mm_base_modem_at_command_finish (_self, res, NULL)) {
mm_obj_dbg (self, "SMONI supported");
self->priv->smoni_support = FEATURE_SUPPORTED;
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
mm_obj_dbg (self, "SMONI unsupported");
self->priv->smoni_support = FEATURE_NOT_SUPPORTED;
/* Otherwise, check if the parent CESQ-based implementation works */
g_assert (iface_modem_signal_parent->check_support && iface_modem_signal_parent->check_support_finish);
iface_modem_signal_parent->check_support (MM_IFACE_MODEM_SIGNAL (self),
(GAsyncReadyCallback) parent_signal_check_support_ready,
task);
}
static void
signal_check_support (MMIfaceModemSignal *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"^SMONI=?",
3,
TRUE,
(GAsyncReadyCallback) check_smoni_support,
task);
}
/*****************************************************************************/
/* Load extended signal information (Signal interface) */
static gboolean
signal_load_values_finish (MMIfaceModemSignal *_self,
GAsyncResult *res,
MMSignal **cdma,
MMSignal **evdo,
MMSignal **gsm,
MMSignal **umts,
MMSignal **lte,
MMSignal **nr5g,
GError **error)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
const gchar *response;
if (self->priv->smoni_support == FEATURE_NOT_SUPPORTED)
return iface_modem_signal_parent->load_values_finish (_self, res, cdma, evdo, gsm, umts, lte, nr5g, error);
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (_self), res, error);
if (!response || !mm_cinterion_smoni_response_to_signal_info (response, gsm, umts, lte, error))
return FALSE;
if (cdma)
*cdma = NULL;
if (evdo)
*evdo = NULL;
if (nr5g)
*nr5g = NULL;
return TRUE;
}
static void
signal_load_values (MMIfaceModemSignal *_self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
if (self->priv->smoni_support == FEATURE_SUPPORTED) {
mm_base_modem_at_command (MM_BASE_MODEM (self),
"^SMONI",
3,
FALSE,
callback,
user_data);
return;
}
/* ^SMONI not supported, fallback to the parent */
iface_modem_signal_parent->load_values (_self, cancellable, callback, user_data);
}
/*****************************************************************************/
/* Enable unsolicited events (SMS indications) (Messaging interface) */
static gboolean
messaging_enable_unsolicited_events_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
cnmi_test_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static gboolean
value_supported (const GArray *array,
const guint value)
{
guint i;
if (!array)
return FALSE;
for (i = 0; i < array->len; i++) {
if (g_array_index (array, guint, i) == value)
return TRUE;
}
return FALSE;
}
static void
messaging_enable_unsolicited_events (MMIfaceModemMessaging *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
GString *cmd;
GError *error = NULL;
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* AT+CNMI=<mode>,[<mt>[,<bm>[,<ds>[,<bfr>]]]] */
cmd = g_string_new ("+CNMI=");
/* Mode 2 or 1 */
if (value_supported (self->priv->cnmi_supported_mode, 2))
g_string_append_printf (cmd, "%u,", 2);
else if (value_supported (self->priv->cnmi_supported_mode, 1))
g_string_append_printf (cmd, "%u,", 1);
else {
error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"SMS settings don't accept [2,1] <mode>");
goto out;
}
/* mt 2 or 1 */
if (value_supported (self->priv->cnmi_supported_mt, 2))
g_string_append_printf (cmd, "%u,", 2);
else if (value_supported (self->priv->cnmi_supported_mt, 1))
g_string_append_printf (cmd, "%u,", 1);
else {
error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"SMS settings don't accept [2,1] <mt>");
goto out;
}
/* bm 2 or 0 */
if (value_supported (self->priv->cnmi_supported_bm, 2))
g_string_append_printf (cmd, "%u,", 2);
else if (value_supported (self->priv->cnmi_supported_bm, 0))
g_string_append_printf (cmd, "%u,", 0);
else {
error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"SMS settings don't accept [2,0] <bm>");
goto out;
}
/* ds 2, 1 or 0 */
if (value_supported (self->priv->cnmi_supported_ds, 2))
g_string_append_printf (cmd, "%u,", 2);
else if (value_supported (self->priv->cnmi_supported_ds, 1))
g_string_append_printf (cmd, "%u,", 1);
else if (value_supported (self->priv->cnmi_supported_ds, 0))
g_string_append_printf (cmd, "%u,", 0);
else {
error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"SMS settings don't accept [2,1,0] <ds>");
goto out;
}
/* bfr 1 */
if (value_supported (self->priv->cnmi_supported_bfr, 1))
g_string_append_printf (cmd, "%u", 1);
/* otherwise, skip setting it */
out:
/* Early error report */
if (error) {
g_task_return_error (task, error);
g_object_unref (task);
g_string_free (cmd, TRUE);
return;
}
mm_base_modem_at_command (MM_BASE_MODEM (self),
cmd->str,
3,
FALSE,
(GAsyncReadyCallback)cnmi_test_ready,
task);
g_string_free (cmd, TRUE);
}
/*****************************************************************************/
/* Check if Messaging supported (Messaging interface) */
static gboolean
messaging_check_support_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
cnmi_format_check_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
GError *error = NULL;
const gchar *response;
response = mm_base_modem_at_command_finish (_self, res, &error);
if (error) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Parse */
if (!mm_cinterion_parse_cnmi_test (response,
&self->priv->cnmi_supported_mode,
&self->priv->cnmi_supported_mt,
&self->priv->cnmi_supported_bm,
&self->priv->cnmi_supported_ds,
&self->priv->cnmi_supported_bfr,
&error)) {
mm_obj_warn (self, "error reading SMS setup: %s", error->message);
g_error_free (error);
}
/* CNMI command is supported; assume we have full messaging capabilities */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
messaging_check_support (MMIfaceModemMessaging *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* We assume that CDMA-only modems don't have messaging capabilities */
if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self))) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"CDMA-only modems don't have messaging capabilities");
g_object_unref (task);
return;
}
/* Check CNMI support */
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CNMI=?",
3,
TRUE,
(GAsyncReadyCallback)cnmi_format_check_ready,
task);
}
/*****************************************************************************/
/* Power down */
static gboolean
modem_power_down_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
sleep_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
g_autoptr(GError) error = NULL;
if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
mm_obj_dbg (self, "couldn't send power down command: %s", error->message);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
send_sleep_mode_command (GTask *task)
{
MMBroadbandModemCinterion *self;
self = g_task_get_source_object (task);
if (self->priv->sleep_mode_cmd && self->priv->sleep_mode_cmd[0]) {
mm_base_modem_at_command (MM_BASE_MODEM (self),
self->priv->sleep_mode_cmd,
5,
FALSE,
(GAsyncReadyCallback)sleep_ready,
task);
return;
}
/* No default command; just finish without sending anything */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
supported_functionality_status_query_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
const gchar *response;
g_autoptr(GError) error = NULL;
g_assert (self->priv->sleep_mode_cmd == NULL);
response = mm_base_modem_at_command_finish (_self, res, &error);
if (!response) {
mm_obj_warn (self, "couldn't query supported functionality status: %s", error->message);
self->priv->sleep_mode_cmd = g_strdup ("");
} else {
/* We need to get which power-off command to use to put the modem in low
* power mode (with serial port open for AT commands, but with RF switched
* off). According to the documentation of various Cinterion modems, some
* support AT+CFUN=4 (HC25) and those which don't support it can use
* AT+CFUN=7 (CYCLIC SLEEP mode with 2s timeout after last character
* received in the serial port).
*
* So, just look for '4' in the reply; if not found, look for '7', and if
* not found, report warning and don't use any.
*/
if (strstr (response, "4") != NULL) {
mm_obj_dbg (self, "device supports CFUN=4 sleep mode");
self->priv->sleep_mode_cmd = g_strdup ("+CFUN=4");
} else if (strstr (response, "7") != NULL) {
mm_obj_dbg (self, "device supports CFUN=7 sleep mode");
self->priv->sleep_mode_cmd = g_strdup ("+CFUN=7");
} else {
mm_obj_warn (self, "unknown functionality mode to go into sleep mode");
self->priv->sleep_mode_cmd = g_strdup ("");
}
}
send_sleep_mode_command (task);
}
static void
modem_power_down (MMIfaceModem *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* If sleep command already decided, use it. */
if (self->priv->sleep_mode_cmd)
send_sleep_mode_command (task);
else
mm_base_modem_at_command (
MM_BASE_MODEM (self),
"+CFUN=?",
3,
FALSE,
(GAsyncReadyCallback)supported_functionality_status_query_ready,
task);
}
/*****************************************************************************/
/* Modem Power Off */
#define MAX_POWER_OFF_WAIT_TIME_SECS 20
typedef struct {
MMPortSerialAt *port;
GRegex *shutdown_regex;
gboolean shutdown_received;
gboolean smso_replied;
gboolean serial_open;
guint timeout_id;
} PowerOffContext;
static void
power_off_context_free (PowerOffContext *ctx)
{
if (ctx->serial_open)
mm_port_serial_close (MM_PORT_SERIAL (ctx->port));
if (ctx->timeout_id)
g_source_remove (ctx->timeout_id);
mm_port_serial_at_add_unsolicited_msg_handler (ctx->port, ctx->shutdown_regex, NULL, NULL, NULL);
g_object_unref (ctx->port);
g_regex_unref (ctx->shutdown_regex);
g_slice_free (PowerOffContext, ctx);
}
static gboolean
modem_power_off_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
complete_power_off (GTask *task)
{
PowerOffContext *ctx;
ctx = g_task_get_task_data (task);
if (!ctx->shutdown_received || !ctx->smso_replied)
return;
/* remove timeout right away */
g_assert (ctx->timeout_id);
g_source_remove (ctx->timeout_id);
ctx->timeout_id = 0;
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
smso_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
PowerOffContext *ctx;
GError *error = NULL;
ctx = g_task_get_task_data (task);
if (!mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Set as replied and see if we can complete */
ctx->smso_replied = TRUE;
complete_power_off (task);
}
static void
shutdown_received (MMPortSerialAt *port,
GMatchInfo *match_info,
GTask *task)
{
PowerOffContext *ctx;
ctx = g_task_get_task_data (task);
/* Cleanup handler right away, we don't want it called any more */
mm_port_serial_at_add_unsolicited_msg_handler (port, ctx->shutdown_regex, NULL, NULL, NULL);
/* Set as received and see if we can complete */
ctx->shutdown_received = TRUE;
complete_power_off (task);
}
static gboolean
power_off_timeout_cb (GTask *task)
{
PowerOffContext *ctx;
ctx = g_task_get_task_data (task);
ctx->timeout_id = 0;
/* The SMSO reply should have come earlier */
g_warn_if_fail (ctx->smso_replied == TRUE);
/* Cleanup handler right away, we no longer want to receive it */
mm_port_serial_at_add_unsolicited_msg_handler (ctx->port, ctx->shutdown_regex, NULL, NULL, NULL);
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Power off operation timed out");
g_object_unref (task);
return G_SOURCE_REMOVE;
}
static void
modem_power_off (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
PowerOffContext *ctx;
GError *error = NULL;
task = g_task_new (self, NULL, callback, user_data);
ctx = g_slice_new0 (PowerOffContext);
ctx->port = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
ctx->shutdown_regex = g_regex_new ("\\r\\n\\^SHUTDOWN\\r\\n",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
ctx->timeout_id = g_timeout_add_seconds (MAX_POWER_OFF_WAIT_TIME_SECS,
(GSourceFunc)power_off_timeout_cb,
task);
g_task_set_task_data (task, ctx, (GDestroyNotify) power_off_context_free);
/* We'll need to wait for a ^SHUTDOWN before returning the action, which is
* when the modem tells us that it is ready to be shutdown */
mm_port_serial_at_add_unsolicited_msg_handler (
ctx->port,
ctx->shutdown_regex,
(MMPortSerialAtUnsolicitedMsgFn)shutdown_received,
task,
NULL);
/* In order to get the ^SHUTDOWN notification, we must keep the port open
* during the wait time */
ctx->serial_open = mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error);
if (G_UNLIKELY (error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Note: we'll use a timeout < MAX_POWER_OFF_WAIT_TIME_SECS for the AT command,
* so we're sure that the AT command reply will always come before the timeout
* fires */
g_assert (MAX_POWER_OFF_WAIT_TIME_SECS > 5);
mm_base_modem_at_command_full (MM_BASE_MODEM (self),
ctx->port,
"^SMSO",
5,
FALSE, /* allow_cached */
FALSE, /* is_raw */
NULL, /* cancellable */
(GAsyncReadyCallback)smso_ready,
task);
}
/*****************************************************************************/
/* Access technologies polling */
static gboolean
load_access_technologies_finish (MMIfaceModem *self,
GAsyncResult *res,
MMModemAccessTechnology *access_technologies,
guint *mask,
GError **error)
{
GError *inner_error = NULL;
gssize val;
val = g_task_propagate_int (G_TASK (res), &inner_error);
if (inner_error) {
g_propagate_error (error, inner_error);
return FALSE;
}
*access_technologies = (MMModemAccessTechnology) val;
*mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY;
return TRUE;
}
static void
smong_query_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
const gchar *response;
GError *error = NULL;
MMModemAccessTechnology access_tech;
response = mm_base_modem_at_command_finish (self, res, &error);
if (!response || !mm_cinterion_parse_smong_response (response, &access_tech, &error))
g_task_return_error (task, error);
else
g_task_return_int (task, (gssize) access_tech);
g_object_unref (task);
}
static void
load_access_technologies (MMIfaceModem *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* Abort access technology polling if ^SIND psinfo URCs are enabled */
if (self->priv->sind_psinfo_support == FEATURE_SUPPORTED) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "No need to poll access technologies");
g_object_unref (task);
return;
}
mm_base_modem_at_command (
MM_BASE_MODEM (self),
"^SMONG",
3,
FALSE,
(GAsyncReadyCallback)smong_query_ready,
task);
}
/*****************************************************************************/
/* Disable unsolicited events (3GPP interface) */
static gboolean
modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
g_autoptr(GError) error = NULL;
if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error))
mm_obj_warn (self, "couldn't disable parent 3GPP unsolicited events: %s", error->message);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
parent_disable_unsolicited_messages (GTask *task)
{
/* Chain up parent's disable */
iface_modem_3gpp_parent->disable_unsolicited_events (
MM_IFACE_MODEM_3GPP (g_task_get_source_object (task)),
(GAsyncReadyCallback)parent_disable_unsolicited_events_ready,
task);
}
static void
sind_psinfo_disable_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
g_autoptr(GError) error = NULL;
if (!mm_base_modem_at_command_finish (self, res, &error))
mm_obj_warn (self, "Couldn't disable ^SIND psinfo notifications: %s", error->message);
parent_disable_unsolicited_messages (task);
}
static void
modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemCinterion *self;
GTask *task;
self = MM_BROADBAND_MODEM_CINTERION (_self);
task = g_task_new (self, NULL, callback, user_data);
if (self->priv->sind_psinfo_support == FEATURE_SUPPORTED) {
/* Disable access technology update reporting */
mm_base_modem_at_command (MM_BASE_MODEM (self),
"AT^SIND=\"psinfo\",0",
3,
FALSE,
(GAsyncReadyCallback)sind_psinfo_disable_ready,
task);
return;
}
parent_disable_unsolicited_messages (task);
}
/*****************************************************************************/
/* Enable unsolicited events (3GPP interface) */
static gboolean
modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
sind_psinfo_enable_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self;
g_autoptr(GError) error = NULL;
const gchar *response;
guint mode;
guint val;
self = MM_BROADBAND_MODEM_CINTERION (_self);
if (!(response = mm_base_modem_at_command_finish (_self, res, &error))) {
/* something went wrong, disable indicator */
self->priv->sind_psinfo_support = FEATURE_NOT_SUPPORTED;
mm_obj_warn (self, "couldn't enable ^SIND psinfo notifications: %s", error->message);
} else if (!mm_cinterion_parse_sind_response (response, NULL, &mode, &val, &error)) {
/* problem with parsing, disable indicator */
self->priv->sind_psinfo_support = FEATURE_NOT_SUPPORTED;
mm_obj_warn (self, "couldn't parse ^SIND psinfo response: %s", error->message);
} else {
/* Report initial access technology gathered right away */
mm_obj_dbg (self, "reporting initial access technologies...");
mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self),
mm_cinterion_get_access_technology_from_sind_psinfo (val, self),
MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK);
}
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
set_urc_dest_port_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self;
g_autoptr(GError) error = NULL;
self = MM_BROADBAND_MODEM_CINTERION (_self);
if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (_self), res, &error))
mm_obj_dbg (self, "couldn't guarantee unsolicited events are sent to the correct port: %s", error->message);
if (self->priv->sind_psinfo_support == FEATURE_SUPPORTED) {
/* Enable access technology update reporting */
mm_base_modem_at_command (MM_BASE_MODEM (self),
"AT^SIND=\"psinfo\",1",
3,
FALSE,
(GAsyncReadyCallback)sind_psinfo_enable_ready,
task);
return;
}
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
g_autoptr(GError) error = NULL;
if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error))
mm_obj_warn (self, "couldn't enable parent 3GPP unsolicited events: %s", error->message);
/* Make sure unsolicited events are sent to an AT port (PLS9 can default to DATA port) */
mm_base_modem_at_command (MM_BASE_MODEM (self),
"^SCFG=\"URC/DstIfc\",\"app\"",
5,
FALSE,
(GAsyncReadyCallback)set_urc_dest_port_ready,
task);
}
static void
modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* Chain up parent's enable */
iface_modem_3gpp_parent->enable_unsolicited_events (
self,
(GAsyncReadyCallback)parent_enable_unsolicited_events_ready,
task);
}
/*****************************************************************************/
/* Setup/Cleanup unsolicited events (3GPP interface) */
static void
sind_ciev_received (MMPortSerialAt *port,
GMatchInfo *match_info,
MMBroadbandModemCinterion *self)
{
guint val = 0;
gchar *indicator;
indicator = mm_get_string_unquoted_from_match_info (match_info, 1);
if (!mm_get_uint_from_match_info (match_info, 2, &val))
mm_obj_dbg (self, "couldn't parse indicator '%s' value", indicator);
else {
mm_obj_dbg (self, "received indicator '%s' update: %u", indicator, val);
if (g_strcmp0 (indicator, "psinfo") == 0) {
mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self),
mm_cinterion_get_access_technology_from_sind_psinfo (val, self),
MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK);
}
}
g_free (indicator);
}
static void
set_unsolicited_events_handlers (MMBroadbandModemCinterion *self,
gboolean enable)
{
MMPortSerialAt *ports[2];
guint i;
ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
/* Enable unsolicited events in given port */
for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
mm_port_serial_at_add_unsolicited_msg_handler (
ports[i],
self->priv->ciev_regex,
enable ? (MMPortSerialAtUnsolicitedMsgFn)sind_ciev_received : NULL,
enable ? self : NULL,
NULL);
}
}
static gboolean
modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error))
g_task_return_error (task, error);
else {
/* Our own setup now */
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_CINTERION (self), TRUE);
g_task_return_boolean (task, TRUE);
}
g_object_unref (task);
}
static void
modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* Chain up parent's setup */
iface_modem_3gpp_parent->setup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_setup_unsolicited_events_ready,
task);
}
static void
parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* Our own cleanup first */
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_CINTERION (self), FALSE);
/* And now chain up parent's cleanup */
iface_modem_3gpp_parent->cleanup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready,
task);
}
/*****************************************************************************/
/* Common operation to load expected CID for the initial EPS bearer */
static gboolean
load_initial_eps_bearer_cid_finish (MMBroadbandModemCinterion *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
scfg_prov_cfg_query_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
g_autoptr(GError) error = NULL;
const gchar *response;
response = mm_base_modem_at_command_finish (_self, res, &error);
if (!response)
mm_obj_dbg (self, "couldn't query MNO profiles: %s", error->message);
else if (!mm_cinterion_provcfg_response_to_cid (response,
MM_BROADBAND_MODEM_CINTERION (self)->priv->modem_family,
mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
self,
&self->priv->initial_eps_bearer_cid,
&error))
mm_obj_dbg (self, "failed processing list of MNO profiles: %s", error->message);
if (self->priv->initial_eps_bearer_cid < 0) {
mm_obj_dbg (self, "using default EPS bearer context id: 1");
self->priv->initial_eps_bearer_cid = 1;
} else
mm_obj_dbg (self, "loaded EPS bearer context id from list of MNO profiles: %d", self->priv->initial_eps_bearer_cid);
/* This operation really never fails */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
load_initial_eps_bearer_cid (MMBroadbandModemCinterion *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
g_assert (self->priv->initial_eps_bearer_cid < 0);
task = g_task_new (self, NULL, callback, user_data);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"^SCFG=\"MEopMode/Prov/Cfg\"",
20,
FALSE,
(GAsyncReadyCallback)scfg_prov_cfg_query_ready,
task);
}
/*****************************************************************************/
/* Set initial EPS bearer settings */
typedef enum {
SET_INITIAL_EPS_STEP_FIRST = 0,
SET_INITIAL_EPS_STEP_CHECK_MODE,
SET_INITIAL_EPS_STEP_RF_OFF,
SET_INITIAL_EPS_STEP_APN,
SET_INITIAL_EPS_STEP_AUTH,
SET_INITIAL_EPS_STEP_RF_ON,
SET_INITIAL_EPS_STEP_LAST,
} SetInitialEpsStep;
typedef struct {
MMBearerProperties *properties;
SetInitialEpsStep step;
guint initial_cfun_mode;
GError *saved_error;
} SetInitialEpsContext;
static void
set_initial_eps_context_free (SetInitialEpsContext *ctx)
{
g_assert (!ctx->saved_error);
g_object_unref (ctx->properties);
g_slice_free (SetInitialEpsContext, ctx);
}
static gboolean
modem_3gpp_set_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void set_initial_eps_step (GTask *task);
static void
set_initial_eps_rf_on_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
g_autoptr(GError) error = NULL;
SetInitialEpsContext *ctx;
ctx = (SetInitialEpsContext *) g_task_get_task_data (task);
if (!mm_base_modem_at_command_finish (self, res, &error)) {
mm_obj_warn (self, "couldn't set RF back on: %s", error->message);
if (!ctx->saved_error)
ctx->saved_error = g_steal_pointer (&error);
}
/* Go to next step */
ctx->step++;
set_initial_eps_step (task);
}
static void
set_initial_eps_auth_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
SetInitialEpsContext *ctx;
ctx = (SetInitialEpsContext *) g_task_get_task_data (task);
if (!mm_base_modem_at_command_finish (_self, res, &ctx->saved_error)) {
mm_obj_warn (self, "couldn't configure context %d auth settings: %s",
self->priv->initial_eps_bearer_cid, ctx->saved_error->message);
/* Fallback to recover RF before returning the error */
ctx->step = SET_INITIAL_EPS_STEP_RF_ON;
} else {
/* Go to next step */
ctx->step++;
}
set_initial_eps_step (task);
}
static void
set_initial_eps_cgdcont_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
SetInitialEpsContext *ctx;
ctx = (SetInitialEpsContext *) g_task_get_task_data (task);
if (!mm_base_modem_at_command_finish (_self, res, &ctx->saved_error)) {
mm_obj_warn (self, "couldn't configure context %d settings: %s",
self->priv->initial_eps_bearer_cid, ctx->saved_error->message);
/* Fallback to recover RF before returning the error */
ctx->step = SET_INITIAL_EPS_STEP_RF_ON;
} else {
/* Go to next step */
ctx->step++;
}
set_initial_eps_step (task);
}
static void
set_initial_eps_rf_off_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
SetInitialEpsContext *ctx;
ctx = (SetInitialEpsContext *) g_task_get_task_data (task);
if (!mm_base_modem_at_command_finish (self, res, &error)) {
mm_obj_warn (self, "couldn't set RF off: %s", error->message);
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Go to next step */
ctx->step++;
set_initial_eps_step (task);
}
static void
set_initial_eps_cfun_mode_load_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
const gchar *response;
SetInitialEpsContext *ctx;
guint mode;
ctx = (SetInitialEpsContext *) g_task_get_task_data (task);
response = mm_base_modem_at_command_finish (self, res, &error);
if (!response || !mm_3gpp_parse_cfun_query_response (response, &mode, &error)) {
mm_obj_warn (self, "couldn't load initial functionality mode: %s", error->message);
g_task_return_error (task, error);
g_object_unref (task);
return;
}
mm_obj_dbg (self, "current functionality mode: %u", mode);
if (mode != 1 && mode != 4) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
"cannot setup the default LTE bearer settings: "
"the SIM must be powered");
g_task_return_error (task, error);
g_object_unref (task);
return;
}
ctx->initial_cfun_mode = mode;
ctx->step++;
set_initial_eps_step (task);
}
static void
set_initial_eps_step (GTask *task)
{
MMBroadbandModemCinterion *self;
SetInitialEpsContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
switch (ctx->step) {
case SET_INITIAL_EPS_STEP_FIRST:
ctx->step++;
/* fall through */
case SET_INITIAL_EPS_STEP_CHECK_MODE:
mm_base_modem_at_command (
MM_BASE_MODEM (self),
"+CFUN?",
5,
FALSE,
(GAsyncReadyCallback)set_initial_eps_cfun_mode_load_ready,
task);
return;
case SET_INITIAL_EPS_STEP_RF_OFF:
if (ctx->initial_cfun_mode != 4) {
mm_base_modem_at_command (
MM_BASE_MODEM (self),
"+CFUN=4",
5,
FALSE,
(GAsyncReadyCallback)set_initial_eps_rf_off_ready,
task);
return;
}
ctx->step++;
/* fall through */
case SET_INITIAL_EPS_STEP_APN: {
const gchar *apn;
g_autofree gchar *quoted_apn = NULL;
g_autofree gchar *apn_cmd = NULL;
const gchar *ip_family_str;
MMBearerIpFamily ip_family;
ip_family = mm_bearer_properties_get_ip_type (ctx->properties);
if (ip_family == MM_BEARER_IP_FAMILY_NONE || ip_family == MM_BEARER_IP_FAMILY_ANY)
ip_family = MM_BEARER_IP_FAMILY_IPV4;
ip_family_str = mm_3gpp_get_pdp_type_from_ip_family (ip_family);
apn = mm_bearer_properties_get_apn (ctx->properties);
mm_obj_dbg (self, "context %d with APN '%s' and PDP type '%s'",
self->priv->initial_eps_bearer_cid, apn, ip_family_str);
quoted_apn = mm_port_serial_at_quote_string (apn);
apn_cmd = g_strdup_printf ("+CGDCONT=%u,\"%s\",%s",
self->priv->initial_eps_bearer_cid, ip_family_str, quoted_apn);
mm_base_modem_at_command (
MM_BASE_MODEM (self),
apn_cmd,
20,
FALSE,
(GAsyncReadyCallback)set_initial_eps_cgdcont_ready,
task);
return;
}
case SET_INITIAL_EPS_STEP_AUTH: {
g_autofree gchar *auth_cmd = NULL;
auth_cmd = mm_cinterion_build_auth_string (self,
MM_BROADBAND_MODEM_CINTERION (self)->priv->modem_family,
ctx->properties,
self->priv->initial_eps_bearer_cid);
mm_base_modem_at_command (
MM_BASE_MODEM (self),
auth_cmd,
20,
FALSE,
(GAsyncReadyCallback)set_initial_eps_auth_ready,
task);
return;
}
case SET_INITIAL_EPS_STEP_RF_ON:
if (ctx->initial_cfun_mode == 1) {
mm_base_modem_at_command (
MM_BASE_MODEM (self),
"+CFUN=1",
5,
FALSE,
(GAsyncReadyCallback)set_initial_eps_rf_on_ready,
task);
return;
}
ctx->step++;
/* fall through */
case SET_INITIAL_EPS_STEP_LAST:
if (ctx->saved_error)
g_task_return_error (task, g_steal_pointer (&ctx->saved_error));
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
default:
g_assert_not_reached ();
}
}
static void
modem_3gpp_set_initial_eps_bearer_settings (MMIfaceModem3gpp *self,
MMBearerProperties *properties,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
SetInitialEpsContext *ctx;
task = g_task_new (self, NULL, callback, user_data);
/* The initial EPS bearer settings should have already been loaded */
g_assert (MM_BROADBAND_MODEM_CINTERION (self)->priv->initial_eps_bearer_cid >= 0);
/* Setup context */
ctx = g_slice_new0 (SetInitialEpsContext);
ctx->properties = g_object_ref (properties);
ctx->step = SET_INITIAL_EPS_STEP_FIRST;
g_task_set_task_data (task, ctx, (GDestroyNotify) set_initial_eps_context_free);
set_initial_eps_step (task);
}
/*****************************************************************************/
/* Common initial EPS bearer info loading for both:
* - runtime status
* - configuration settings
*/
typedef enum {
COMMON_LOAD_INITIAL_EPS_STEP_FIRST = 0,
COMMON_LOAD_INITIAL_EPS_STEP_PROFILE,
COMMON_LOAD_INITIAL_EPS_STEP_APN,
COMMON_LOAD_INITIAL_EPS_STEP_AUTH,
COMMON_LOAD_INITIAL_EPS_STEP_LAST,
} CommonLoadInitialEpsStep;
typedef struct {
MMBearerProperties *properties;
CommonLoadInitialEpsStep step;
gboolean runtime;
} CommonLoadInitialEpsContext;
static void
common_load_initial_eps_context_free (CommonLoadInitialEpsContext *ctx)
{
g_clear_object (&ctx->properties);
g_slice_free (CommonLoadInitialEpsContext, ctx);
}
static MMBearerProperties *
common_load_initial_eps_bearer_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error));
}
static void common_load_initial_eps_step (GTask *task);
static void
common_load_initial_eps_auth_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
const gchar *response;
CommonLoadInitialEpsContext *ctx;
g_autoptr(GError) error = NULL;
MMBearerAllowedAuth auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;
g_autofree gchar *username = NULL;
ctx = (CommonLoadInitialEpsContext *) g_task_get_task_data (task);
response = mm_base_modem_at_command_finish (_self, res, &error);
if (!response)
mm_obj_dbg (self, "couldn't load context %d auth settings: %s",
self->priv->initial_eps_bearer_cid, error->message);
else if (!mm_cinterion_parse_sgauth_response (response, self->priv->initial_eps_bearer_cid, &auth, &username, &error))
mm_obj_dbg (self, "couldn't parse context %d auth settings: %s", self->priv->initial_eps_bearer_cid, error->message);
else {
mm_bearer_properties_set_allowed_auth (ctx->properties, auth);
mm_bearer_properties_set_user (ctx->properties, username);
}
/* Go to next step */
ctx->step++;
common_load_initial_eps_step (task);
}
static void
common_load_initial_eps_load_cid_ready (MMBroadbandModemCinterion *self,
GAsyncResult *res,
GTask *task)
{
CommonLoadInitialEpsContext *ctx;
ctx = (CommonLoadInitialEpsContext *) g_task_get_task_data (task);
load_initial_eps_bearer_cid_finish (self, res, NULL);
g_assert (self->priv->initial_eps_bearer_cid >= 0);
/* Go to next step */
ctx->step++;
common_load_initial_eps_step (task);
}
static void
common_load_initial_eps_cgcontrdp_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
const gchar *response;
CommonLoadInitialEpsContext *ctx;
g_autofree gchar *apn = NULL;
g_autoptr(GError) error = NULL;
ctx = (CommonLoadInitialEpsContext *) g_task_get_task_data (task);
/* errors aren't fatal */
response = mm_base_modem_at_command_finish (_self, res, &error);
if (!response)
mm_obj_dbg (self, "couldn't load context %d settings: %s",
self->priv->initial_eps_bearer_cid, error->message);
else if (!mm_3gpp_parse_cgcontrdp_response (response, NULL, NULL, &apn, NULL, NULL, NULL, NULL, NULL, &error))
mm_obj_dbg (self, "couldn't parse CGDCONTRDP response: %s", error->message);
else
mm_bearer_properties_set_apn (ctx->properties, apn);
/* Go to next step */
ctx->step++;
common_load_initial_eps_step (task);
}
static void
common_load_initial_eps_cgdcont_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
const gchar *response;
CommonLoadInitialEpsContext *ctx;
g_autoptr(GError) error = NULL;
ctx = (CommonLoadInitialEpsContext *) g_task_get_task_data (task);
/* errors aren't fatal */
response = mm_base_modem_at_command_finish (_self, res, &error);
if (!response)
mm_obj_dbg (self, "couldn't load context %d status: %s",
self->priv->initial_eps_bearer_cid, error->message);
else {
GList *context_list;
context_list = mm_3gpp_parse_cgdcont_read_response (response, &error);
if (!context_list)
mm_obj_dbg (self, "couldn't parse CGDCONT response: %s", error->message);
else {
GList *l;
for (l = context_list; l; l = g_list_next (l)) {
MM3gppPdpContext *pdp = l->data;
if (pdp->cid == (guint) self->priv->initial_eps_bearer_cid) {
mm_bearer_properties_set_ip_type (ctx->properties, pdp->pdp_type);
mm_bearer_properties_set_apn (ctx->properties, pdp->apn ? pdp->apn : "");
break;
}
}
if (!l)
mm_obj_dbg (self, "no status reported for context %d", self->priv->initial_eps_bearer_cid);
mm_3gpp_pdp_context_list_free (context_list);
}
}
/* Go to next step */
ctx->step++;
common_load_initial_eps_step (task);
}
static void
common_load_initial_eps_step (GTask *task)
{
MMBroadbandModemCinterion *self;
CommonLoadInitialEpsContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
switch (ctx->step) {
case COMMON_LOAD_INITIAL_EPS_STEP_FIRST:
ctx->step++;
/* fall through */
case COMMON_LOAD_INITIAL_EPS_STEP_PROFILE:
/* Initial EPS bearer CID initialization run once only */
if (G_UNLIKELY (self->priv->initial_eps_bearer_cid < 0)) {
load_initial_eps_bearer_cid (
self,
(GAsyncReadyCallback)common_load_initial_eps_load_cid_ready,
task);
return;
}
ctx->step++;
/* fall through */
case COMMON_LOAD_INITIAL_EPS_STEP_APN:
if (ctx->runtime) {
mm_base_modem_at_command (
MM_BASE_MODEM (self),
"+CGDCONT?",
20,
FALSE,
(GAsyncReadyCallback)common_load_initial_eps_cgdcont_ready,
task);
} else {
g_autofree gchar *cmd = NULL;
cmd = g_strdup_printf ("+CGCONTRDP=%u", self->priv->initial_eps_bearer_cid);
mm_base_modem_at_command (
MM_BASE_MODEM (self),
"+CGCONTRDP",
20,
FALSE,
(GAsyncReadyCallback)common_load_initial_eps_cgcontrdp_ready,
task);
}
return;
case COMMON_LOAD_INITIAL_EPS_STEP_AUTH:
mm_base_modem_at_command (
MM_BASE_MODEM (self),
"^SGAUTH?",
20,
FALSE,
(GAsyncReadyCallback)common_load_initial_eps_auth_ready,
task);
return;
case COMMON_LOAD_INITIAL_EPS_STEP_LAST:
g_task_return_pointer (task, g_steal_pointer (&ctx->properties), g_object_unref);
g_object_unref (task);
return;
default:
g_assert_not_reached ();
}
}
static void
common_load_initial_eps_bearer (MMIfaceModem3gpp *self,
gboolean runtime,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
CommonLoadInitialEpsContext *ctx;
task = g_task_new (self, NULL, callback, user_data);
/* Setup context */
ctx = g_slice_new0 (CommonLoadInitialEpsContext);
ctx->runtime = runtime;
ctx->properties = mm_bearer_properties_new ();
ctx->step = COMMON_LOAD_INITIAL_EPS_STEP_FIRST;
g_task_set_task_data (task, ctx, (GDestroyNotify) common_load_initial_eps_context_free);
common_load_initial_eps_step (task);
}
/*****************************************************************************/
/* Initial EPS bearer runtime status loading */
static MMBearerProperties *
modem_3gpp_load_initial_eps_bearer_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return common_load_initial_eps_bearer_finish (self, res, error);
}
static void
modem_3gpp_load_initial_eps_bearer (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
common_load_initial_eps_bearer (self, TRUE, callback, user_data);
}
/*****************************************************************************/
/* Initial EPS bearer settings loading -> set configuration */
static MMBearerProperties *
modem_3gpp_load_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return common_load_initial_eps_bearer_finish (self, res, error);
}
static void
modem_3gpp_load_initial_eps_bearer_settings (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
common_load_initial_eps_bearer (self, FALSE, callback, user_data);
}
/*****************************************************************************/
/* Load supported modes (Modem interface) */
static GArray *
load_supported_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_load_supported_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
GArray *all;
GArray *combinations;
GArray *filtered;
MMModemModeCombination mode;
all = iface_modem_parent->load_supported_modes_finish (self, res, &error);
if (!all) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Build list of combinations */
combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 3);
/* 2G only */
mode.allowed = MM_MODEM_MODE_2G;
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
/* 3G only */
mode.allowed = MM_MODEM_MODE_3G;
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
if (mm_iface_modem_is_4g (self)) {
/* 4G only */
mode.allowed = MM_MODEM_MODE_4G;
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
/* 2G, 3G and 4G */
mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
} else {
/* 2G and 3G */
mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
}
/* Filter out those unsupported modes */
filtered = mm_filter_supported_modes (all, combinations, self);
g_array_unref (all);
g_array_unref (combinations);
g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
g_object_unref (task);
}
static void
sxrat_load_supported_modes_ready (MMBroadbandModemCinterion *self,
GTask *task)
{
GArray *combinations;
MMModemModeCombination mode;
g_assert (self->priv->sxrat_supported_rat);
g_assert (self->priv->sxrat_supported_pref1);
/* Build list of combinations */
combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 3);
if (value_supported (self->priv->sxrat_supported_rat, 0)) {
/* 2G only */
mode.allowed = MM_MODEM_MODE_2G;
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
}
if (value_supported (self->priv->sxrat_supported_rat, 1)) {
/* 2G+3G with none preferred */
mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
self->priv->any_allowed = mode.allowed;
if (value_supported (self->priv->sxrat_supported_pref1, 0)) {
/* 2G preferred */
mode.preferred = MM_MODEM_MODE_2G;
g_array_append_val (combinations, mode);
}
if (value_supported (self->priv->sxrat_supported_pref1, 2)) {
/* 3G preferred */
mode.preferred = MM_MODEM_MODE_3G;
g_array_append_val (combinations, mode);
}
}
if (value_supported (self->priv->sxrat_supported_rat, 2)) {
/* 3G only */
mode.allowed = MM_MODEM_MODE_3G;
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
}
if (value_supported (self->priv->sxrat_supported_rat, 3)) {
/* 4G only */
mode.allowed = MM_MODEM_MODE_4G;
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
}
if (value_supported (self->priv->sxrat_supported_rat, 4)) {
/* 3G+4G with none preferred */
mode.allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
self->priv->any_allowed = mode.allowed;
if (value_supported (self->priv->sxrat_supported_pref1, 2)) {
/* 3G preferred */
mode.preferred = MM_MODEM_MODE_3G;
g_array_append_val (combinations, mode);
}
if (value_supported (self->priv->sxrat_supported_pref1, 3)) {
/* 4G preferred */
mode.preferred = MM_MODEM_MODE_4G;
g_array_append_val (combinations, mode);
}
}
if (value_supported (self->priv->sxrat_supported_rat, 5)) {
/* 2G+4G with none preferred */
mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G);
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
self->priv->any_allowed = mode.allowed;
if (value_supported (self->priv->sxrat_supported_pref1, 0)) {
/* 2G preferred */
mode.preferred = MM_MODEM_MODE_2G;
g_array_append_val (combinations, mode);
}
if (value_supported (self->priv->sxrat_supported_pref1, 3)) {
/* 4G preferred */
mode.preferred = MM_MODEM_MODE_4G;
g_array_append_val (combinations, mode);
}
}
if (value_supported (self->priv->sxrat_supported_rat, 6)) {
/* 2G+3G+4G with none preferred */
mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
self->priv->any_allowed = mode.allowed;
if (value_supported (self->priv->sxrat_supported_pref1, 0)) {
/* 2G preferred */
mode.preferred = MM_MODEM_MODE_2G;
g_array_append_val (combinations, mode);
}
if (value_supported (self->priv->sxrat_supported_pref1, 2)) {
/* 3G preferred */
mode.preferred = MM_MODEM_MODE_3G;
g_array_append_val (combinations, mode);
}
if (value_supported (self->priv->sxrat_supported_pref1, 3)) {
/* 4G preferred */
mode.preferred = MM_MODEM_MODE_4G;
g_array_append_val (combinations, mode);
}
}
g_task_return_pointer (task, combinations, (GDestroyNotify) g_array_unref);
g_object_unref (task);
}
static void
sxrat_test_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
g_autoptr(GError) error = NULL;
const gchar *response;
response = mm_base_modem_at_command_finish (_self, res, &error);
if (!error) {
mm_cinterion_parse_sxrat_test (response,
&self->priv->sxrat_supported_rat,
&self->priv->sxrat_supported_pref1,
NULL,
&error);
if (!error) {
self->priv->sxrat_support = FEATURE_SUPPORTED;
sxrat_load_supported_modes_ready (self, task);
return;
}
mm_obj_warn (self, "error reading SXRAT response: %s", error->message);
}
self->priv->sxrat_support = FEATURE_NOT_SUPPORTED;
/* Run parent's loading in case SXRAT is not supported */
iface_modem_parent->load_supported_modes (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback)parent_load_supported_modes_ready,
task);
}
static void
load_supported_modes (MMIfaceModem *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* First check SXRAT support, if not already done */
if (self->priv->sxrat_support == FEATURE_SUPPORT_UNKNOWN) {
mm_base_modem_at_command (MM_BASE_MODEM (self),
"^SXRAT=?",
3,
TRUE,
(GAsyncReadyCallback)sxrat_test_ready,
task);
return;
}
if (self->priv->sxrat_support == FEATURE_SUPPORTED) {
sxrat_load_supported_modes_ready (self, task);
return;
}
/* Run parent's loading */
iface_modem_parent->load_supported_modes (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback)parent_load_supported_modes_ready,
task);
}
/*****************************************************************************/
/* Set current modes (Modem interface) */
static gboolean
set_current_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
set_current_modes_reregister_in_network_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!mm_iface_modem_3gpp_reregister_in_network_finish (self, res, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
allowed_access_technology_update_ready (MMBroadbandModemCinterion *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error)
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
cops_set_current_modes (MMBroadbandModemCinterion *self,
MMModemMode allowed,
MMModemMode preferred,
GTask *task)
{
gchar *command;
g_assert (preferred == MM_MODEM_MODE_NONE);
/* We will try to simulate the possible allowed modes here. The
* Cinterion devices do not seem to allow setting preferred access
* technology in devices, but they allow restricting to a given
* one:
* - 2G-only is forced by forcing GERAN RAT (AcT=0)
* - 3G-only is forced by forcing UTRAN RAT (AcT=2)
* - 4G-only is forced by forcing E-UTRAN RAT (AcT=7)
* - for the remaining ones, we default to automatic selection of RAT,
* which is based on the quality of the connection.
*/
if (mm_iface_modem_is_4g (MM_IFACE_MODEM (self)) && allowed == MM_MODEM_MODE_4G)
command = g_strdup ("+COPS=,,,7");
else if (mm_iface_modem_is_3g (MM_IFACE_MODEM (self)) && allowed == MM_MODEM_MODE_3G)
command = g_strdup ("+COPS=,,,2");
else if (mm_iface_modem_is_2g (MM_IFACE_MODEM (self)) && allowed == MM_MODEM_MODE_2G)
command = g_strdup ("+COPS=,,,0");
else {
/* For any other combination (e.g. ANY or no AcT given, defaults to Auto. For this case, we cannot provide
* AT+COPS=,,, (i.e. just without a last value). Instead, we need to
* re-run the last manual/automatic selection command which succeeded,
* (or auto by default if none was launched) */
mm_iface_modem_3gpp_reregister_in_network (MM_IFACE_MODEM_3GPP (self),
(GAsyncReadyCallback) set_current_modes_reregister_in_network_ready,
task);
return;
}
mm_base_modem_at_command (
MM_BASE_MODEM (self),
command,
20,
FALSE,
(GAsyncReadyCallback)allowed_access_technology_update_ready,
task);
g_free (command);
}
static void
sxrat_set_current_modes (MMBroadbandModemCinterion *self,
MMModemMode allowed,
MMModemMode preferred,
GTask *task)
{
gchar *command;
GError *error = NULL;
g_assert (self->priv->any_allowed != MM_MODEM_MODE_NONE);
/* Handle ANY */
if (allowed == MM_MODEM_MODE_ANY)
allowed = self->priv->any_allowed;
command = mm_cinterion_build_sxrat_set_command (allowed, preferred, &error);
if (!command) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
mm_base_modem_at_command (
MM_BASE_MODEM (self),
command,
30,
FALSE,
(GAsyncReadyCallback)allowed_access_technology_update_ready,
task);
g_free (command);
}
static void
set_current_modes (MMIfaceModem *_self,
MMModemMode allowed,
MMModemMode preferred,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
if (self->priv->sxrat_support == FEATURE_SUPPORTED)
sxrat_set_current_modes (self, allowed, preferred, task);
else if (self->priv->sxrat_support == FEATURE_NOT_SUPPORTED)
cops_set_current_modes (self, allowed, preferred, task);
else
g_assert_not_reached ();
}
/*****************************************************************************/
/* Supported bands (Modem interface) */
static GArray *
load_supported_bands_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
scfg_test_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
const gchar *response;
GError *error = NULL;
GArray *bands;
response = mm_base_modem_at_command_finish (_self, res, &error);
if (!response ||
!mm_cinterion_parse_scfg_test (response,
self->priv->modem_family,
mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
&bands,
&self->priv->rb_format,
&error))
g_task_return_error (task, error);
else {
if (!mm_cinterion_build_band (bands,
NULL,
FALSE,
self->priv->rb_format,
self->priv->modem_family,
self->priv->supported_bands,
&error))
g_task_return_error (task, error);
else
g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
}
g_object_unref (task);
}
static void
load_supported_bands (MMIfaceModem *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
GTask *task;
MMPort *primary;
MMKernelDevice *port;
const gchar *family = NULL;
/* Lookup for the tag specifying which modem family the current device belongs */
primary = MM_PORT (mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)));
port = mm_port_peek_kernel_device (primary);
family = mm_kernel_device_get_global_property (port, "ID_MM_CINTERION_MODEM_FAMILY");
/* if the property is not set, default family */
self->priv->modem_family = MM_CINTERION_MODEM_FAMILY_DEFAULT;
/* set used family also in the string for mm_obj_dbg */
if (!family)
family = "default";
if (g_ascii_strcasecmp (family, "imt") == 0)
self->priv->modem_family = MM_CINTERION_MODEM_FAMILY_IMT;
else if (g_ascii_strcasecmp (family, "default") != 0) {
mm_obj_dbg (self, "cinterion modem family '%s' unknown", family);
family = "default";
}
mm_obj_dbg (self, "Using cinterion %s modem family", family);
task = g_task_new (_self, NULL, callback, user_data);
mm_base_modem_at_command (MM_BASE_MODEM (_self),
"AT^SCFG=?",
3,
FALSE,
(GAsyncReadyCallback)scfg_test_ready,
task);
}
/*****************************************************************************/
/* Load current bands (Modem interface) */
static GArray *
load_current_bands_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
get_band_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
const gchar *response;
GError *error = NULL;
GArray *bands = NULL;
response = mm_base_modem_at_command_finish (_self, res, &error);
if (!response ||
!mm_cinterion_parse_scfg_response (response,
self->priv->modem_family,
mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
&bands,
self->priv->rb_format,
&error))
g_task_return_error (task, error);
else
g_task_return_pointer (task, bands, (GDestroyNotify) g_array_unref);
g_object_unref (task);
}
static void
load_current_bands (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* The timeout in this command is extremely large, because there are some
* modules like the EGS5 that build the response based on the current network
* registration, and that implies the module needs to be registered. If for
* any reason there is no serving network where to register, the response
* comes after a very long time, up to 100s. */
mm_base_modem_at_command (MM_BASE_MODEM (self),
"AT^SCFG?",
120,
FALSE,
(GAsyncReadyCallback)get_band_ready,
task);
}
/*****************************************************************************/
/* Set current bands (Modem interface) */
typedef struct {
MMBaseModemAtCommandAlloc *cmds;
} SetCurrentBandsContext;
static void
set_current_bands_context_free (SetCurrentBandsContext *ctx)
{
if (ctx->cmds) {
guint i;
for (i = 0; ctx->cmds[i].command; i++)
mm_base_modem_at_command_alloc_clear (&ctx->cmds[i]);
g_free (ctx->cmds);
}
g_slice_free (SetCurrentBandsContext, ctx);
}
static gboolean
set_current_bands_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
scfg_set_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
scfg_set_ready_sequence (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
mm_base_modem_at_sequence_finish (self, res, NULL, &error);
if (error)
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
set_bands_3g (GTask *task,
GArray *bands_array)
{
MMBroadbandModemCinterion *self;
GError *error = NULL;
guint band[MM_CINTERION_RB_BLOCK_N] = { 0 };
self = g_task_get_source_object (task);
if (!mm_cinterion_build_band (bands_array,
self->priv->supported_bands,
FALSE, /* 2G and 3G */
self->priv->rb_format,
self->priv->modem_family,
band,
&error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
if (self->priv->rb_format == MM_CINTERION_RADIO_BAND_FORMAT_SINGLE) {
g_autofree gchar *cmd = NULL;
/* Following the setup:
* AT^SCFG="Radion/Band",<rba>
* We will set the preferred band equal to the allowed band, so that we force
* the modem to connect at that specific frequency only. Note that we will be
* passing a number here!
*
* The optional <rbe> field is set to 1, so that changes take effect
* immediately.
*/
cmd = g_strdup_printf ("^SCFG=\"Radio/Band\",%u,1", band[MM_CINTERION_RB_BLOCK_LEGACY]);
mm_base_modem_at_command (MM_BASE_MODEM (self),
cmd,
15,
FALSE,
(GAsyncReadyCallback)scfg_set_ready,
task);
return;
}
if (self->priv->rb_format == MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE) {
SetCurrentBandsContext *ctx;
ctx = g_slice_new0 (SetCurrentBandsContext);
g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_bands_context_free);
if (self->priv->modem_family == MM_CINTERION_MODEM_FAMILY_IMT) {
g_autofree gchar *bandstr2G = NULL;
g_autofree gchar *bandstr3G = NULL;
g_autofree gchar *bandstr4G = NULL;
g_autofree gchar *bandstr2G_enc = NULL;
g_autofree gchar *bandstr3G_enc = NULL;
g_autofree gchar *bandstr4G_enc = NULL;
bandstr2G = g_strdup_printf ("0x%08X", band[MM_CINTERION_RB_BLOCK_GSM]);
bandstr3G = g_strdup_printf ("0x%08X", band[MM_CINTERION_RB_BLOCK_UMTS]);
bandstr4G = g_strdup_printf ("0x%08X", band[MM_CINTERION_RB_BLOCK_LTE_LOW]);
bandstr2G_enc = mm_modem_charset_str_from_utf8 (bandstr2G,
mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
FALSE,
&error);
if (!bandstr2G_enc) {
g_prefix_error (&error, "Couldn't convert 2G band string to current charset: ");
g_task_return_error (task, error);
g_object_unref (task);
return;
}
bandstr3G_enc = mm_modem_charset_str_from_utf8 (bandstr3G,
mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
FALSE,
&error);
if (!bandstr3G_enc) {
g_prefix_error (&error, "Couldn't convert 3G band string to current charset: ");
g_task_return_error (task, error);
g_object_unref (task);
return;
}
bandstr4G_enc = mm_modem_charset_str_from_utf8 (bandstr4G,
mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
FALSE,
&error);
if (!bandstr4G_enc) {
g_prefix_error (&error, "Couldn't convert 4G band string to current charset: ");
g_task_return_error (task, error);
g_object_unref (task);
return;
}
ctx->cmds = g_new0 (MMBaseModemAtCommandAlloc, 3 + 1);
ctx->cmds[0].command = g_strdup_printf ("^SCFG=\"Radio/Band/2G\",\"%s\"", bandstr2G_enc);
ctx->cmds[1].command = g_strdup_printf ("^SCFG=\"Radio/Band/3G\",\"%s\"", bandstr3G_enc);
ctx->cmds[2].command = g_strdup_printf ("^SCFG=\"Radio/Band/4G\",\"%s\"", bandstr4G_enc);
ctx->cmds[0].timeout = ctx->cmds[1].timeout = ctx->cmds[2].timeout = 60;
} else {
ctx->cmds = g_new0 (MMBaseModemAtCommandAlloc, 3 + 1);
ctx->cmds[0].command = g_strdup_printf ("^SCFG=\"Radio/Band/2G\",\"%08x\",,1", band[MM_CINTERION_RB_BLOCK_GSM]);
ctx->cmds[1].command = g_strdup_printf ("^SCFG=\"Radio/Band/3G\",\"%08x\",,1", band[MM_CINTERION_RB_BLOCK_UMTS]);
ctx->cmds[2].command = g_strdup_printf ("^SCFG=\"Radio/Band/4G\",\"%08x\",\"%08x\",1", band[MM_CINTERION_RB_BLOCK_LTE_LOW], band[MM_CINTERION_RB_BLOCK_LTE_HIGH]);
ctx->cmds[0].timeout = ctx->cmds[1].timeout = ctx->cmds[2].timeout = 15;
}
mm_base_modem_at_sequence (MM_BASE_MODEM (self),
(const MMBaseModemAtCommand *)ctx->cmds,
NULL,
NULL,
(GAsyncReadyCallback)scfg_set_ready_sequence,
task);
return;
}
g_assert_not_reached ();
}
static void
set_bands_2g (GTask *task,
GArray *bands_array)
{
MMBroadbandModemCinterion *self;
GError *error = NULL;
guint band[MM_CINTERION_RB_BLOCK_N] = { 0 };
g_autofree gchar *cmd = NULL;
g_autofree gchar *bandstr = NULL;
g_autofree gchar *bandstr_enc = NULL;
self = g_task_get_source_object (task);
if (!mm_cinterion_build_band (bands_array,
self->priv->supported_bands,
TRUE, /* 2G only */
MM_CINTERION_RADIO_BAND_FORMAT_SINGLE,
0,
band,
&error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Build string with the value, in the proper charset */
bandstr = g_strdup_printf ("%u", band[MM_CINTERION_RB_BLOCK_LEGACY]);
bandstr_enc = mm_modem_charset_str_from_utf8 (bandstr,
mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
FALSE,
&error);
if (!bandstr_enc) {
g_prefix_error (&error, "Couldn't convert band string to current charset: ");
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Following the setup:
* AT^SCFG="Radion/Band",<rbp>,<rba>
* We will set the preferred band equal to the allowed band, so that we force
* the modem to connect at that specific frequency only. Note that we will be
* passing double-quote enclosed strings here!
*/
cmd = g_strdup_printf ("^SCFG=\"Radio/Band\",\"%s\",\"%s\"", bandstr_enc, bandstr_enc);
mm_base_modem_at_command (MM_BASE_MODEM (self),
cmd,
15,
FALSE,
(GAsyncReadyCallback)scfg_set_ready,
task);
}
static void
set_current_bands (MMIfaceModem *self,
GArray *bands_array,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
/* The bands that we get here are previously validated by the interface, and
* that means that ALL the bands given here were also given in the list of
* supported bands. BUT BUT, that doesn't mean that the exact list of bands
* will end up being valid, as not all combinations are possible. E.g,
* Cinterion modems supporting only 2G have specific combinations allowed.
*/
task = g_task_new (self, NULL, callback, user_data);
if (mm_iface_modem_is_3g (self))
set_bands_3g (task, bands_array);
else
set_bands_2g (task, bands_array);
}
/*****************************************************************************/
/* Flow control */
static gboolean
setup_flow_control_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
setup_flow_control_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_finish (self, res, &error))
/* Let the error be critical. We DO need RTS/CTS in order to have
* proper modem disabling. */
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
setup_flow_control (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* We need to enable RTS/CTS so that CYCLIC SLEEP mode works */
g_object_set (self, MM_BROADBAND_MODEM_FLOW_CONTROL, MM_FLOW_CONTROL_RTS_CTS, NULL);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"\\Q3",
3,
FALSE,
(GAsyncReadyCallback)setup_flow_control_ready,
task);
}
/*****************************************************************************/
/* Load unlock retries (Modem interface) */
typedef struct {
MMUnlockRetries *retries;
guint i;
} LoadUnlockRetriesContext;
typedef struct {
MMModemLock lock;
const gchar *command;
} UnlockRetriesMap;
static const UnlockRetriesMap unlock_retries_map [] = {
{ MM_MODEM_LOCK_SIM_PIN, "^SPIC=\"SC\"" },
{ MM_MODEM_LOCK_SIM_PUK, "^SPIC=\"SC\",1" },
{ MM_MODEM_LOCK_SIM_PIN2, "^SPIC=\"P2\"" },
{ MM_MODEM_LOCK_SIM_PUK2, "^SPIC=\"P2\",1" },
{ MM_MODEM_LOCK_PH_FSIM_PIN, "^SPIC=\"PS\"" },
{ MM_MODEM_LOCK_PH_FSIM_PUK, "^SPIC=\"PS\",1" },
{ MM_MODEM_LOCK_PH_NET_PIN, "^SPIC=\"PN\"" },
{ MM_MODEM_LOCK_PH_NET_PUK, "^SPIC=\"PN\",1" },
};
static void
load_unlock_retries_context_free (LoadUnlockRetriesContext *ctx)
{
g_object_unref (ctx->retries);
g_slice_free (LoadUnlockRetriesContext, ctx);
}
static MMUnlockRetries *
load_unlock_retries_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void load_unlock_retries_context_step (GTask *task);
static void
spic_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
LoadUnlockRetriesContext *ctx;
const gchar *response;
g_autoptr(GError) error = NULL;
ctx = g_task_get_task_data (task);
response = mm_base_modem_at_command_finish (self, res, &error);
if (!response) {
mm_obj_dbg (self, "Couldn't load retry count for lock '%s': %s",
mm_modem_lock_get_string (unlock_retries_map[ctx->i].lock),
error->message);
} else {
guint val;
response = mm_strip_tag (response, "^SPIC:");
if (!mm_get_uint_from_str (response, &val))
mm_obj_dbg (self, "couldn't parse retry count value for lock '%s'",
mm_modem_lock_get_string (unlock_retries_map[ctx->i].lock));
else
mm_unlock_retries_set (ctx->retries, unlock_retries_map[ctx->i].lock, val);
}
/* Go to next lock value */
ctx->i++;
load_unlock_retries_context_step (task);
}
static void
load_unlock_retries_context_step (GTask *task)
{
MMBroadbandModemCinterion *self;
LoadUnlockRetriesContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
if (ctx->i == G_N_ELEMENTS (unlock_retries_map)) {
g_task_return_pointer (task, g_object_ref (ctx->retries), g_object_unref);
g_object_unref (task);
return;
}
mm_base_modem_at_command (
MM_BASE_MODEM (self),
unlock_retries_map[ctx->i].command,
3,
FALSE,
(GAsyncReadyCallback)spic_ready,
task);
}
static void
load_unlock_retries (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
LoadUnlockRetriesContext *ctx;
task = g_task_new (self, NULL, callback, user_data);
ctx = g_slice_new0 (LoadUnlockRetriesContext);
ctx->retries = mm_unlock_retries_new ();
ctx->i = 0;
g_task_set_task_data (task, ctx, (GDestroyNotify)load_unlock_retries_context_free);
load_unlock_retries_context_step (task);
}
/*****************************************************************************/
/* After SIM unlock (Modem interface) */
#define MAX_AFTER_SIM_UNLOCK_RETRIES 15
typedef enum {
CINTERION_SIM_STATUS_REMOVED = 0,
CINTERION_SIM_STATUS_INSERTED = 1,
CINTERION_SIM_STATUS_INIT_COMPLETED = 5,
} CinterionSimStatus;
typedef struct {
guint retries;
guint timeout_id;
} AfterSimUnlockContext;
static gboolean
after_sim_unlock_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void after_sim_unlock_context_step (GTask *task);
static gboolean
simstatus_timeout_cb (GTask *task)
{
AfterSimUnlockContext *ctx;
ctx = g_task_get_task_data (task);
ctx->timeout_id = 0;
after_sim_unlock_context_step (task);
return G_SOURCE_REMOVE;
}
static void
simstatus_check_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
AfterSimUnlockContext *ctx;
const gchar *response;
response = mm_base_modem_at_command_finish (self, res, NULL);
if (response) {
gchar *descr = NULL;
guint val = 0;
if (mm_cinterion_parse_sind_response (response, &descr, NULL, &val, NULL) &&
g_str_equal (descr, "simstatus") &&
val == CINTERION_SIM_STATUS_INIT_COMPLETED) {
/* SIM ready! */
g_free (descr);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
g_free (descr);
}
/* Need to retry after 1 sec */
ctx = g_task_get_task_data (task);
g_assert (ctx->timeout_id == 0);
ctx->timeout_id = g_timeout_add_seconds (1, (GSourceFunc)simstatus_timeout_cb, task);
}
static void
after_sim_unlock_context_step (GTask *task)
{
MMBroadbandModemCinterion *self;
AfterSimUnlockContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
/* if not supported or too much wait, skip */
if (self->priv->sind_simstatus_support != FEATURE_SUPPORTED || ctx->retries == 0) {
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
/* Recheck */
ctx->retries--;
mm_base_modem_at_command (MM_BASE_MODEM (self),
"^SIND=\"simstatus\",2",
3,
FALSE,
(GAsyncReadyCallback)simstatus_check_ready,
task);
}
static void
sind_indicators_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self;
g_autoptr(GError) error = NULL;
const gchar *response;
self = MM_BROADBAND_MODEM_CINTERION (_self);
if (!(response = mm_base_modem_at_command_finish (_self, res, &error))) {
self->priv->sind_psinfo_support = FEATURE_NOT_SUPPORTED;
mm_obj_dbg (self, "psinfo support? no");
self->priv->sind_simstatus_support = FEATURE_NOT_SUPPORTED;
mm_obj_dbg (self, "simstatus support? no");
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
if (g_regex_match_simple ("\\(\\s*psinfo\\s*,", response, 0, 0))
self->priv->sind_psinfo_support = FEATURE_SUPPORTED;
mm_obj_dbg (self, "psinfo support? %s", self->priv->sind_psinfo_support == FEATURE_SUPPORTED ? "yes":"no");
if (g_regex_match_simple ("\\(\\s*simstatus\\s*,", response, 0, 0))
self->priv->sind_simstatus_support = FEATURE_SUPPORTED;
mm_obj_dbg (self, "simstatus support? %s", self->priv->sind_simstatus_support == FEATURE_SUPPORTED ? "yes":"no");
after_sim_unlock_context_step (task);
}
static void
after_sim_unlock (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
AfterSimUnlockContext *ctx;
task = g_task_new (self, NULL, callback, user_data);
ctx = g_new0 (AfterSimUnlockContext, 1);
ctx->retries = MAX_AFTER_SIM_UNLOCK_RETRIES;
g_task_set_task_data (task, ctx, g_free);
/* check which indicators are available */
mm_base_modem_at_command (MM_BASE_MODEM (self),
"AT^SIND=?",
3,
FALSE,
(GAsyncReadyCallback)sind_indicators_ready,
task);
}
/*****************************************************************************/
/* Setup SIM hot swap (Modem interface) */
static void
cinterion_scks_unsolicited_handler (MMPortSerialAt *port,
GMatchInfo *match_info,
MMBroadbandModemCinterion *self)
{
guint scks;
if (!mm_get_uint_from_match_info (match_info, 1, &scks))
return;
switch (scks) {
case 0:
mm_obj_msg (self, "SIM removal detected");
break;
case 1:
mm_obj_msg (self, "SIM insertion detected");
break;
case 2:
mm_obj_msg (self, "SIM interface hardware deactivated (potentially non-electrically compatible SIM inserted)");
break;
case 3:
mm_obj_msg (self, "SIM interface hardware deactivated (technical problem, no precise diagnosis)");
break;
default:
g_assert_not_reached ();
break;
}
mm_iface_modem_process_sim_event (MM_IFACE_MODEM (self));
}
static gboolean
modem_setup_sim_hot_swap_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
cinterion_hot_swap_init_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
g_autoptr(GError) error = NULL;
MMPortSerialAt *primary;
MMPortSerialAt *secondary;
if (!mm_base_modem_at_command_finish (_self, res, &error)) {
g_prefix_error (&error, "Could not enable SCKS: ");
g_task_return_error (task, g_steal_pointer (&error));
g_object_unref (task);
return;
}
mm_obj_dbg (self, "SIM hot swap detect successfully enabled");
primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
mm_port_serial_at_add_unsolicited_msg_handler (
primary,
self->priv->scks_regex,
(MMPortSerialAtUnsolicitedMsgFn) cinterion_scks_unsolicited_handler,
self,
NULL);
secondary = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
if (secondary)
mm_port_serial_at_add_unsolicited_msg_handler (
secondary,
self->priv->scks_regex,
(MMPortSerialAtUnsolicitedMsgFn) cinterion_scks_unsolicited_handler,
self,
NULL);
if (!mm_broadband_modem_sim_hot_swap_ports_context_init (MM_BROADBAND_MODEM (self), &error))
mm_obj_warn (self, "failed to initialize SIM hot swap ports context: %s", error->message);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
modem_setup_sim_hot_swap (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
mm_obj_dbg (self, "Enabling SCKS URCs for SIM hot swap detection");
task = g_task_new (self, NULL, callback, user_data);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"^SCKS=1",
3,
FALSE,
(GAsyncReadyCallback) cinterion_hot_swap_init_ready,
task);
}
/*****************************************************************************/
/* SIM hot swap cleanup (Modem interface) */
static void
modem_cleanup_sim_hot_swap (MMIfaceModem *self)
{
mm_broadband_modem_sim_hot_swap_ports_context_reset (MM_BROADBAND_MODEM (self));
}
/*****************************************************************************/
/* Create Bearer (Modem interface) */
static MMBaseBearer *
cinterion_modem_create_bearer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
broadband_bearer_cinterion_new_ready (GObject *unused,
GAsyncResult *res,
GTask *task)
{
MMBaseBearer *bearer;
GError *error = NULL;
bearer = mm_broadband_bearer_cinterion_new_finish (res, &error);
if (!bearer)
g_task_return_error (task, error);
else
g_task_return_pointer (task, bearer, g_object_unref);
g_object_unref (task);
}
static void
broadband_bearer_new_ready (GObject *unused,
GAsyncResult *res,
GTask *task)
{
MMBaseBearer *bearer;
GError *error = NULL;
bearer = mm_broadband_bearer_new_finish (res, &error);
if (!bearer)
g_task_return_error (task, error);
else
g_task_return_pointer (task, bearer, g_object_unref);
g_object_unref (task);
}
static void
common_create_bearer (GTask *task)
{
MMBroadbandModemCinterion *self;
self = g_task_get_source_object (task);
switch (self->priv->swwan_support) {
case FEATURE_NOT_SUPPORTED:
mm_obj_dbg (self, "^SWWAN not supported, creating default bearer...");
mm_broadband_bearer_new (MM_BROADBAND_MODEM (self),
g_task_get_task_data (task),
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_new_ready,
task);
return;
case FEATURE_SUPPORTED:
mm_obj_dbg (self, "^SWWAN supported, creating cinterion bearer...");
mm_broadband_bearer_cinterion_new (MM_BROADBAND_MODEM_CINTERION (self),
g_task_get_task_data (task),
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_cinterion_new_ready,
task);
return;
case FEATURE_SUPPORT_UNKNOWN:
default:
g_assert_not_reached ();
}
}
static void
swwan_test_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
/* Fetch the result to the SWWAN test. If no response given (error triggered),
* assume unsupported */
if (!mm_base_modem_at_command_finish (_self, res, NULL)) {
mm_obj_dbg (self, "SWWAN unsupported");
self->priv->swwan_support = FEATURE_NOT_SUPPORTED;
} else {
mm_obj_dbg (self, "SWWAN supported");
self->priv->swwan_support = FEATURE_SUPPORTED;
}
/* Go on and create the bearer */
common_create_bearer (task);
}
static void
cinterion_modem_create_bearer (MMIfaceModem *_self,
MMBearerProperties *properties,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, g_object_ref (properties), g_object_unref);
/* Newer Cinterion modems may support SWWAN, which is the same as WWAN.
* Check to see if current modem supports it.*/
if (self->priv->swwan_support != FEATURE_SUPPORT_UNKNOWN) {
common_create_bearer (task);
return;
}
/* If we don't have a data port, don't even bother checking for ^SWWAN
* support. */
if (!mm_base_modem_peek_best_data_port (MM_BASE_MODEM (self), MM_PORT_TYPE_NET)) {
mm_obj_dbg (self, "skipping ^SWWAN check as no data port is available");
self->priv->swwan_support = FEATURE_NOT_SUPPORTED;
common_create_bearer (task);
return;
}
mm_obj_dbg (self, "checking ^SWWAN support...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"^SWWAN=?",
6,
TRUE, /* may be cached */
(GAsyncReadyCallback) swwan_test_ready,
task);
}
/*****************************************************************************/
static void
setup_ports (MMBroadbandModem *_self)
{
MMBroadbandModemCinterion *self = (MM_BROADBAND_MODEM_CINTERION (_self));
MMPortSerialAt *ports[2];
guint i;
/* Call parent's setup ports first always */
MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_cinterion_parent_class)->setup_ports (_self);
ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
mm_port_serial_at_add_unsolicited_msg_handler (
ports[i],
self->priv->sysstart_regex,
NULL, NULL, NULL);
mm_port_serial_at_add_unsolicited_msg_handler (
ports[i],
self->priv->scks_regex,
NULL, NULL, NULL);
}
}
/*****************************************************************************/
MMBroadbandModemCinterion *
mm_broadband_modem_cinterion_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id)
{
return g_object_new (MM_TYPE_BROADBAND_MODEM_CINTERION,
MM_BASE_MODEM_DEVICE, device,
MM_BASE_MODEM_DRIVERS, drivers,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
/* Generic bearer (TTY) or Cinterion bearer (NET) supported */
MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
NULL);
}
static void
mm_broadband_modem_cinterion_init (MMBroadbandModemCinterion *self)
{
/* Initialize private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
MM_TYPE_BROADBAND_MODEM_CINTERION,
MMBroadbandModemCinterionPrivate);
/* Initialize private variables */
self->priv->initial_eps_bearer_cid = -1;
self->priv->sind_psinfo_support = FEATURE_SUPPORT_UNKNOWN;
self->priv->swwan_support = FEATURE_SUPPORT_UNKNOWN;
self->priv->smoni_support = FEATURE_SUPPORT_UNKNOWN;
self->priv->sind_simstatus_support = FEATURE_SUPPORT_UNKNOWN;
self->priv->sxrat_support = FEATURE_SUPPORT_UNKNOWN;
self->priv->ciev_regex = g_regex_new ("\\r\\n\\+CIEV:\\s*([a-z]+),(\\d+)\\r\\n",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
self->priv->sysstart_regex = g_regex_new ("\\r\\n\\^SYSSTART.*\\r\\n",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
self->priv->scks_regex = g_regex_new ("\\^SCKS:\\s*([0-3])\\r\\n",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
self->priv->any_allowed = MM_MODEM_MODE_NONE;
}
static void
finalize (GObject *object)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (object);
g_free (self->priv->sleep_mode_cmd);
if (self->priv->cnmi_supported_mode)
g_array_unref (self->priv->cnmi_supported_mode);
if (self->priv->cnmi_supported_mt)
g_array_unref (self->priv->cnmi_supported_mt);
if (self->priv->cnmi_supported_bm)
g_array_unref (self->priv->cnmi_supported_bm);
if (self->priv->cnmi_supported_ds)
g_array_unref (self->priv->cnmi_supported_ds);
if (self->priv->cnmi_supported_bfr)
g_array_unref (self->priv->cnmi_supported_bfr);
if (self->priv->sxrat_supported_rat)
g_array_unref (self->priv->sxrat_supported_rat);
if (self->priv->sxrat_supported_pref1)
g_array_unref (self->priv->sxrat_supported_pref1);
g_regex_unref (self->priv->ciev_regex);
g_regex_unref (self->priv->sysstart_regex);
g_regex_unref (self->priv->scks_regex);
G_OBJECT_CLASS (mm_broadband_modem_cinterion_parent_class)->finalize (object);
}
static void
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;
iface->set_current_modes_finish = set_current_modes_finish;
iface->load_supported_bands = load_supported_bands;
iface->load_supported_bands_finish = load_supported_bands_finish;
iface->load_current_bands = load_current_bands;
iface->load_current_bands_finish = load_current_bands_finish;
iface->set_current_bands = set_current_bands;
iface->set_current_bands_finish = set_current_bands_finish;
iface->load_access_technologies = load_access_technologies;
iface->load_access_technologies_finish = load_access_technologies_finish;
iface->setup_flow_control = setup_flow_control;
iface->setup_flow_control_finish = setup_flow_control_finish;
iface->modem_after_sim_unlock = after_sim_unlock;
iface->modem_after_sim_unlock_finish = after_sim_unlock_finish;
iface->load_unlock_retries = load_unlock_retries;
iface->load_unlock_retries_finish = load_unlock_retries_finish;
iface->reset = mm_shared_cinterion_modem_reset;
iface->reset_finish = mm_shared_cinterion_modem_reset_finish;
iface->modem_power_down = modem_power_down;
iface->modem_power_down_finish = modem_power_down_finish;
iface->modem_power_off = modem_power_off;
iface->modem_power_off_finish = modem_power_off_finish;
iface->setup_sim_hot_swap = modem_setup_sim_hot_swap;
iface->setup_sim_hot_swap_finish = modem_setup_sim_hot_swap_finish;
iface->cleanup_sim_hot_swap = modem_cleanup_sim_hot_swap;
}
static MMIfaceModem *
peek_parent_interface (MMSharedCinterion *self)
{
return iface_modem_parent;
}
static void
iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
{
iface_modem_3gpp_parent = g_type_interface_peek_parent (iface);
iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events;
iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish;
iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events;
iface->disable_unsolicited_events_finish = modem_3gpp_disable_unsolicited_events_finish;
iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events;
iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish;
iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events;
iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish;
iface->load_initial_eps_bearer = modem_3gpp_load_initial_eps_bearer;
iface->load_initial_eps_bearer_finish = modem_3gpp_load_initial_eps_bearer_finish;
iface->load_initial_eps_bearer_settings = modem_3gpp_load_initial_eps_bearer_settings;
iface->load_initial_eps_bearer_settings_finish = modem_3gpp_load_initial_eps_bearer_settings_finish;
iface->set_initial_eps_bearer_settings = modem_3gpp_set_initial_eps_bearer_settings;
iface->set_initial_eps_bearer_settings_finish = modem_3gpp_set_initial_eps_bearer_settings_finish;
}
static void
iface_modem_messaging_init (MMIfaceModemMessaging *iface)
{
iface->check_support = messaging_check_support;
iface->check_support_finish = messaging_check_support_finish;
iface->enable_unsolicited_events = messaging_enable_unsolicited_events;
iface->enable_unsolicited_events_finish = messaging_enable_unsolicited_events_finish;
}
static void
iface_modem_location_init (MMIfaceModemLocation *iface)
{
iface_modem_location_parent = g_type_interface_peek_parent (iface);
iface->load_capabilities = mm_shared_cinterion_location_load_capabilities;
iface->load_capabilities_finish = mm_shared_cinterion_location_load_capabilities_finish;
iface->enable_location_gathering = mm_shared_cinterion_enable_location_gathering;
iface->enable_location_gathering_finish = mm_shared_cinterion_enable_location_gathering_finish;
iface->disable_location_gathering = mm_shared_cinterion_disable_location_gathering;
iface->disable_location_gathering_finish = mm_shared_cinterion_disable_location_gathering_finish;
}
static MMIfaceModemLocation *
peek_parent_location_interface (MMSharedCinterion *self)
{
return iface_modem_location_parent;
}
static void
iface_modem_voice_init (MMIfaceModemVoice *iface)
{
iface_modem_voice_parent = g_type_interface_peek_parent (iface);
iface->create_call = mm_shared_cinterion_create_call;
iface->check_support = mm_shared_cinterion_voice_check_support;
iface->check_support_finish = mm_shared_cinterion_voice_check_support_finish;
iface->enable_unsolicited_events = mm_shared_cinterion_voice_enable_unsolicited_events;
iface->enable_unsolicited_events_finish = mm_shared_cinterion_voice_enable_unsolicited_events_finish;
iface->disable_unsolicited_events = mm_shared_cinterion_voice_disable_unsolicited_events;
iface->disable_unsolicited_events_finish = mm_shared_cinterion_voice_disable_unsolicited_events_finish;
iface->setup_unsolicited_events = mm_shared_cinterion_voice_setup_unsolicited_events;
iface->setup_unsolicited_events_finish = mm_shared_cinterion_voice_setup_unsolicited_events_finish;
iface->cleanup_unsolicited_events = mm_shared_cinterion_voice_cleanup_unsolicited_events;
iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_voice_cleanup_unsolicited_events_finish;
}
static MMIfaceModemVoice *
peek_parent_voice_interface (MMSharedCinterion *self)
{
return iface_modem_voice_parent;
}
static void
iface_modem_time_init (MMIfaceModemTime *iface)
{
iface_modem_time_parent = g_type_interface_peek_parent (iface);
iface->setup_unsolicited_events = mm_shared_cinterion_time_setup_unsolicited_events;
iface->setup_unsolicited_events_finish = mm_shared_cinterion_time_setup_unsolicited_events_finish;
iface->cleanup_unsolicited_events = mm_shared_cinterion_time_cleanup_unsolicited_events;
iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_time_cleanup_unsolicited_events_finish;
}
static MMIfaceModemTime *
peek_parent_time_interface (MMSharedCinterion *self)
{
return iface_modem_time_parent;
}
static void
shared_cinterion_init (MMSharedCinterion *iface)
{
iface->peek_parent_interface = peek_parent_interface;
iface->peek_parent_location_interface = peek_parent_location_interface;
iface->peek_parent_voice_interface = peek_parent_voice_interface;
iface->peek_parent_time_interface = peek_parent_time_interface;
}
static void
iface_modem_signal_init (MMIfaceModemSignal *iface)
{
iface_modem_signal_parent = g_type_interface_peek_parent (iface);
iface->check_support = signal_check_support;
iface->check_support_finish = signal_check_support_finish;
iface->load_values = signal_load_values;
iface->load_values_finish = signal_load_values_finish;
}
static void
mm_broadband_modem_cinterion_class_init (MMBroadbandModemCinterionClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
g_type_class_add_private (object_class, sizeof (MMBroadbandModemCinterionPrivate));
/* Virtual methods */
object_class->finalize = finalize;
broadband_modem_class->setup_ports = setup_ports;
}