Originally developed by: Ben Chan <benchan@chromium.org> This patch replaces mm_bearer_report_disconnection() with a more generic mm_bearer_report_connection_status(), which allows reporting any connection status of a bearer. This further allows getting rid of those custom report_connection_status functions in plugic specific bearer subclasses. Note that while plugin-specific implementations can receive multiple 'MMBearerConnectionStatus' values, the generic implementation is only allowed to receive DISCONNECTED. Plugins need to make sure that they process all the other status values, and only report DISCONNECTED to the parent when required. MBM: The MBM bearer implementation of report_connection_status() expects either CONNECTED or DISCONNECTED. If any of these is received and there is an ongoing connection attempt, the corresponding operation will be completed. If there is no connection attempt, we will just handle the DISCONNECTED state, calling the parent method to notify that the modem got network-disconnected. Icera: The Icera bearer implementation of report_connection_status() expects either CONNECTED, CONNECT FAILED or DISCONNECTED. If any of these is received and there is an ongoing connection or disconnection attempt, the corresponding operation will be completed. If there is no connection or disconnection attempt, we will just handle the CONNECT FAILED and DISCONNECTED states, calling the parent method (always with DISCONNECTED) to notify that the modem got network-disconnected. Option/HSO: The Option/HSO bearer implementation of report_connection_status() expects either CONNECTED, CONNECTION FAILED or DISCONNECTED. If any of these is received and there is an ongoing connection or disconnection attempt, the corresponding operation will be completed. If there is no connection or disconnection attempt, we will just handle the CONNECTION FAILED and DISCONNECTED states, calling the parent method (always with DISCONNECTED) to notify that the modem got network-disconnected. Huawei: The Huawei bearer implementation of report_connection_status() expects either CONNECTED or DISCONNECTED. These messages are not used to process pending connection or disconnection attempts; so if they are received while one of these is on-going, it will just be ignored. CONNECTED reports are also ignored, so we will just handle the DISCONNECTED state, calling the parent method to notify that the modem got network-disconnected. Altair-LTE: The Altair-LTE bearers will only report DISCONNECTED on network-disconnected cases. There is no custom report_connection_status(). Novatel-LTE: The Novatel-LTE bearers will only report DISCONNECTED on network-disconnected cases. There is no custom report_connection_status().
1371 lines
45 KiB
C
1371 lines
45 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) 2008 - 2010 Ericsson AB
|
|
* Copyright (C) 2009 - 2012 Red Hat, Inc.
|
|
* Copyright (C) 2012 Lanedo GmbH
|
|
*
|
|
* Author: Per Hallsmark <per.hallsmark@ericsson.com>
|
|
* Bjorn Runaker <bjorn.runaker@ericsson.com>
|
|
* Torgny Johansson <torgny.johansson@ericsson.com>
|
|
* Jonas Sjöquist <jonas.sjoquist@ericsson.com>
|
|
* Dan Williams <dcbw@redhat.com>
|
|
* Aleksander Morgado <aleksander@lanedo.com>
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
|
|
#include "ModemManager.h"
|
|
#include "mm-log.h"
|
|
#include "mm-bearer-list.h"
|
|
#include "mm-errors-types.h"
|
|
#include "mm-modem-helpers.h"
|
|
#include "mm-broadband-modem-mbm.h"
|
|
#include "mm-broadband-bearer-mbm.h"
|
|
#include "mm-sim-mbm.h"
|
|
#include "mm-base-modem-at.h"
|
|
#include "mm-iface-modem.h"
|
|
#include "mm-iface-modem-3gpp.h"
|
|
|
|
static void iface_modem_init (MMIfaceModem *iface);
|
|
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
|
|
|
|
static MMIfaceModem *iface_modem_parent;
|
|
static MMIfaceModem3gpp *iface_modem_3gpp_parent;
|
|
|
|
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbm, mm_broadband_modem_mbm, 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))
|
|
|
|
#define MBM_NETWORK_MODE_OFFLINE 0
|
|
#define MBM_NETWORK_MODE_ANY 1
|
|
#define MBM_NETWORK_MODE_LOW_POWER 4
|
|
#define MBM_NETWORK_MODE_2G 5
|
|
#define MBM_NETWORK_MODE_3G 6
|
|
|
|
#define MBM_E2NAP_DISCONNECTED 0
|
|
#define MBM_E2NAP_CONNECTED 1
|
|
#define MBM_E2NAP_CONNECTING 2
|
|
|
|
struct _MMBroadbandModemMbmPrivate {
|
|
gboolean have_emrdy;
|
|
|
|
GRegex *e2nap_regex;
|
|
GRegex *e2nap_ext_regex;
|
|
GRegex *emrdy_regex;
|
|
GRegex *pacsp_regex;
|
|
GRegex *estksmenu_regex;
|
|
GRegex *estksms_regex;
|
|
GRegex *emwi_regex;
|
|
GRegex *erinfo_regex;
|
|
|
|
guint mbm_mode;
|
|
};
|
|
|
|
/*****************************************************************************/
|
|
/* Create Bearer (Modem interface) */
|
|
|
|
static MMBearer *
|
|
modem_create_bearer_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
MMBearer *bearer;
|
|
|
|
bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
|
|
mm_dbg ("New MBM bearer created at DBus path '%s'", mm_bearer_get_path (bearer));
|
|
|
|
return g_object_ref (bearer);
|
|
}
|
|
|
|
static void
|
|
broadband_bearer_mbm_new_ready (GObject *source,
|
|
GAsyncResult *res,
|
|
GSimpleAsyncResult *simple)
|
|
{
|
|
MMBearer *bearer = NULL;
|
|
GError *error = NULL;
|
|
|
|
bearer = mm_broadband_bearer_mbm_new_finish (res, &error);
|
|
if (!bearer)
|
|
g_simple_async_result_take_error (simple, error);
|
|
else
|
|
g_simple_async_result_set_op_res_gpointer (simple,
|
|
bearer,
|
|
(GDestroyNotify)g_object_unref);
|
|
g_simple_async_result_complete (simple);
|
|
g_object_unref (simple);
|
|
}
|
|
|
|
static void
|
|
modem_create_bearer (MMIfaceModem *self,
|
|
MMBearerProperties *properties,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GSimpleAsyncResult *result;
|
|
|
|
result = g_simple_async_result_new (G_OBJECT (self),
|
|
callback,
|
|
user_data,
|
|
modem_create_bearer);
|
|
|
|
mm_dbg ("Creating MBM bearer...");
|
|
mm_broadband_bearer_mbm_new (MM_BROADBAND_MODEM_MBM (self),
|
|
properties,
|
|
NULL, /* cancellable */
|
|
(GAsyncReadyCallback)broadband_bearer_mbm_new_ready,
|
|
result);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Create SIM (Modem interface) */
|
|
|
|
static MMSim *
|
|
create_sim_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return mm_sim_mbm_new_finish (res, error);
|
|
}
|
|
|
|
static void
|
|
create_sim (MMIfaceModem *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
/* New MBM SIM */
|
|
mm_sim_mbm_new (MM_BASE_MODEM (self),
|
|
NULL, /* cancellable */
|
|
callback,
|
|
user_data);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* After SIM unlock (Modem interface) */
|
|
|
|
static gboolean
|
|
modem_after_sim_unlock_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
after_sim_unlock_wait_cb (GSimpleAsyncResult *result)
|
|
{
|
|
g_simple_async_result_complete (result);
|
|
g_object_unref (result);
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
modem_after_sim_unlock (MMIfaceModem *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GSimpleAsyncResult *result;
|
|
|
|
result = g_simple_async_result_new (G_OBJECT (self),
|
|
callback,
|
|
user_data,
|
|
modem_after_sim_unlock);
|
|
|
|
/* wait so sim pin is done */
|
|
g_timeout_add (500, (GSourceFunc)after_sim_unlock_wait_cb, result);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Load supported modes (Modem interface) */
|
|
|
|
static GArray *
|
|
load_supported_modes_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
|
|
return NULL;
|
|
|
|
return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
|
|
}
|
|
|
|
static void
|
|
parent_load_supported_modes_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GSimpleAsyncResult *simple)
|
|
{
|
|
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_simple_async_result_take_error (simple, error);
|
|
g_simple_async_result_complete (simple);
|
|
g_object_unref (simple);
|
|
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);
|
|
/* 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);
|
|
g_array_unref (all);
|
|
g_array_unref (combinations);
|
|
|
|
g_simple_async_result_set_op_res_gpointer (simple, filtered, (GDestroyNotify) g_array_unref);
|
|
g_simple_async_result_complete (simple);
|
|
g_object_unref (simple);
|
|
}
|
|
|
|
static void
|
|
load_supported_modes (MMIfaceModem *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
/* Run parent's loading */
|
|
iface_modem_parent->load_supported_modes (
|
|
MM_IFACE_MODEM (self),
|
|
(GAsyncReadyCallback)parent_load_supported_modes_ready,
|
|
g_simple_async_result_new (G_OBJECT (self),
|
|
callback,
|
|
user_data,
|
|
load_supported_modes));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Load initial allowed/preferred modes (Modem interface) */
|
|
|
|
static gboolean
|
|
load_current_modes_finish (MMIfaceModem *_self,
|
|
GAsyncResult *res,
|
|
MMModemMode *allowed,
|
|
MMModemMode *preferred,
|
|
GError **error)
|
|
{
|
|
MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self);
|
|
const gchar *response;
|
|
guint a;
|
|
|
|
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
|
|
if (!response)
|
|
return FALSE;
|
|
|
|
if (mm_get_uint_from_str (mm_strip_tag (response, "+CFUN:"), &a)) {
|
|
/* No settings to set preferred */
|
|
*preferred = MM_MODEM_MODE_NONE;
|
|
|
|
switch (a) {
|
|
case MBM_NETWORK_MODE_OFFLINE:
|
|
case MBM_NETWORK_MODE_LOW_POWER:
|
|
/* Do not update internal mbm_mode */
|
|
*allowed = MM_MODEM_MODE_NONE;
|
|
break;
|
|
case MBM_NETWORK_MODE_2G:
|
|
self->priv->mbm_mode = MBM_NETWORK_MODE_2G;
|
|
*allowed = MM_MODEM_MODE_2G;
|
|
break;
|
|
case MBM_NETWORK_MODE_3G:
|
|
self->priv->mbm_mode = MBM_NETWORK_MODE_3G;
|
|
*allowed = MM_MODEM_MODE_3G;
|
|
break;
|
|
default:
|
|
/* Do not update internal mbm_mode */
|
|
*allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
g_set_error (error,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"Couldn't parse +CFUN response: '%s'",
|
|
response);
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
load_current_modes (MMIfaceModem *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
mm_base_modem_at_command (MM_BASE_MODEM (self),
|
|
"+CFUN?",
|
|
3,
|
|
FALSE,
|
|
callback,
|
|
user_data);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Set allowed modes (Modem interface) */
|
|
|
|
typedef struct {
|
|
MMBroadbandModemMbm *self;
|
|
GSimpleAsyncResult *result;
|
|
gint mbm_mode;
|
|
} SetCurrentModesContext;
|
|
|
|
static void
|
|
set_current_modes_context_complete_and_free (SetCurrentModesContext *ctx)
|
|
{
|
|
g_simple_async_result_complete_in_idle (ctx->result);
|
|
g_object_unref (ctx->result);
|
|
g_object_unref (ctx->self);
|
|
g_slice_free (SetCurrentModesContext, ctx);
|
|
}
|
|
|
|
static gboolean
|
|
set_current_modes_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
|
|
}
|
|
|
|
static void
|
|
allowed_mode_update_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
SetCurrentModesContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
mm_base_modem_at_command_finish (self, res, &error);
|
|
if (error)
|
|
/* Let the error be critical. */
|
|
g_simple_async_result_take_error (ctx->result, error);
|
|
else {
|
|
/* Cache current allowed mode */
|
|
ctx->self->priv->mbm_mode = ctx->mbm_mode;
|
|
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
|
|
}
|
|
set_current_modes_context_complete_and_free (ctx);
|
|
}
|
|
|
|
static void
|
|
set_current_modes (MMIfaceModem *self,
|
|
MMModemMode allowed,
|
|
MMModemMode preferred,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
SetCurrentModesContext *ctx;
|
|
gchar *command;
|
|
|
|
ctx = g_slice_new (SetCurrentModesContext);
|
|
ctx->self = g_object_ref (self);
|
|
ctx->result = g_simple_async_result_new (G_OBJECT (self),
|
|
callback,
|
|
user_data,
|
|
set_current_modes);
|
|
ctx->mbm_mode = -1;
|
|
|
|
if (allowed == MM_MODEM_MODE_2G)
|
|
ctx->mbm_mode = MBM_NETWORK_MODE_2G;
|
|
else if (allowed == MM_MODEM_MODE_3G)
|
|
ctx->mbm_mode = MBM_NETWORK_MODE_3G;
|
|
else if ((allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G) ||
|
|
allowed == MM_MODEM_MODE_ANY) &&
|
|
preferred == MM_MODEM_MODE_NONE)
|
|
ctx->mbm_mode = MBM_NETWORK_MODE_ANY;
|
|
|
|
if (ctx->mbm_mode < 0) {
|
|
gchar *allowed_str;
|
|
gchar *preferred_str;
|
|
|
|
allowed_str = mm_modem_mode_build_string_from_mask (allowed);
|
|
preferred_str = mm_modem_mode_build_string_from_mask (preferred);
|
|
g_simple_async_result_set_error (ctx->result,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"Requested mode (allowed: '%s', preferred: '%s') not "
|
|
"supported by the modem.",
|
|
allowed_str,
|
|
preferred_str);
|
|
g_free (allowed_str);
|
|
g_free (preferred_str);
|
|
|
|
set_current_modes_context_complete_and_free (ctx);
|
|
return;
|
|
}
|
|
|
|
command = g_strdup_printf ("+CFUN=%d", ctx->mbm_mode);
|
|
mm_base_modem_at_command (
|
|
MM_BASE_MODEM (self),
|
|
command,
|
|
3,
|
|
FALSE,
|
|
(GAsyncReadyCallback)allowed_mode_update_ready,
|
|
ctx);
|
|
g_free (command);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Initializing the modem (during first enabling) */
|
|
|
|
typedef struct {
|
|
GSimpleAsyncResult *result;
|
|
MMBroadbandModemMbm *self;
|
|
} EnablingModemInitContext;
|
|
|
|
static void
|
|
enabling_modem_init_context_complete_and_free (EnablingModemInitContext *ctx)
|
|
{
|
|
g_simple_async_result_complete (ctx->result);
|
|
g_object_unref (ctx->result);
|
|
g_object_unref (ctx->self);
|
|
g_slice_free (EnablingModemInitContext, ctx);
|
|
}
|
|
|
|
static gboolean
|
|
enabling_modem_init_finish (MMBroadbandModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
|
|
}
|
|
|
|
static void
|
|
enabling_init_sequence_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
EnablingModemInitContext *ctx)
|
|
{
|
|
/* Ignore errors */
|
|
mm_base_modem_at_sequence_full_finish (self, res, NULL, NULL);
|
|
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
|
|
enabling_modem_init_context_complete_and_free (ctx);
|
|
}
|
|
|
|
static const MMBaseModemAtCommand enabling_modem_init_sequence[] = {
|
|
/* Init command */
|
|
{ "&F", 3, FALSE, NULL },
|
|
/* Ensure disconnected */
|
|
{ "*ENAP=0", 3, FALSE, NULL },
|
|
{ NULL }
|
|
};
|
|
|
|
static void
|
|
run_enabling_init_sequence (EnablingModemInitContext *ctx)
|
|
{
|
|
mm_base_modem_at_sequence_full (MM_BASE_MODEM (ctx->self),
|
|
mm_base_modem_peek_port_primary (MM_BASE_MODEM (ctx->self)),
|
|
enabling_modem_init_sequence,
|
|
NULL, /* response_processor_context */
|
|
NULL, /* response_processor_context_free */
|
|
NULL, /* cancellable */
|
|
(GAsyncReadyCallback)enabling_init_sequence_ready,
|
|
ctx);
|
|
}
|
|
|
|
static void
|
|
emrdy_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
EnablingModemInitContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
/* EMRDY unsolicited response might have happened between the command
|
|
* submission and the response. This was seen once:
|
|
*
|
|
* (ttyACM0): --> 'AT*EMRDY?<CR>'
|
|
* (ttyACM0): <-- 'T*EMRD<CR><LF>*EMRDY: 1<CR><LF>Y?'
|
|
*
|
|
* So suppress the warning if the unsolicited handler handled the response
|
|
* before we get here.
|
|
*/
|
|
if (!mm_base_modem_at_command_finish (self, res, &error)) {
|
|
if (g_error_matches (error,
|
|
MM_SERIAL_ERROR,
|
|
MM_SERIAL_ERROR_RESPONSE_TIMEOUT))
|
|
mm_warn ("timed out waiting for EMRDY response.");
|
|
else
|
|
ctx->self->priv->have_emrdy = TRUE;
|
|
g_error_free (error);
|
|
}
|
|
|
|
run_enabling_init_sequence (ctx);
|
|
}
|
|
|
|
static void
|
|
enabling_modem_init (MMBroadbandModem *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
EnablingModemInitContext *ctx;
|
|
|
|
ctx = g_slice_new0 (EnablingModemInitContext);
|
|
ctx->result = g_simple_async_result_new (G_OBJECT (self),
|
|
callback,
|
|
user_data,
|
|
enabling_modem_init);
|
|
ctx->self = g_object_ref (self);
|
|
|
|
/* Modem is ready?, no need to check EMRDY */
|
|
if (ctx->self->priv->have_emrdy) {
|
|
run_enabling_init_sequence (ctx);
|
|
return;
|
|
}
|
|
|
|
mm_base_modem_at_command (MM_BASE_MODEM (self),
|
|
"*EMRDY?",
|
|
3,
|
|
FALSE,
|
|
(GAsyncReadyCallback)emrdy_ready,
|
|
ctx);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Modem power down (Modem interface) */
|
|
|
|
static gboolean
|
|
modem_power_down_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
|
|
}
|
|
|
|
static void
|
|
modem_power_down (MMIfaceModem *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
/* Use AT+CFUN=4 for power down. It will stop the RF (IMSI detach), and
|
|
* keeps access to the SIM */
|
|
mm_base_modem_at_command (MM_BASE_MODEM (self),
|
|
"+CFUN=4",
|
|
3,
|
|
FALSE,
|
|
callback,
|
|
user_data);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Powering up the modem (Modem interface) */
|
|
|
|
static gboolean
|
|
modem_power_up_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
/* By default, errors in the power up command are ignored. */
|
|
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
modem_power_up (MMIfaceModem *_self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self);
|
|
gchar *command;
|
|
|
|
g_assert (self->priv->mbm_mode == MBM_NETWORK_MODE_ANY ||
|
|
self->priv->mbm_mode == MBM_NETWORK_MODE_2G ||
|
|
self->priv->mbm_mode == MBM_NETWORK_MODE_3G);
|
|
|
|
command = g_strdup_printf ("+CFUN=%u", self->priv->mbm_mode);
|
|
mm_base_modem_at_command (MM_BASE_MODEM (self),
|
|
command,
|
|
5,
|
|
FALSE,
|
|
callback,
|
|
user_data);
|
|
g_free (command);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Power state loading (Modem interface) */
|
|
|
|
static MMModemPowerState
|
|
load_power_state_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
const gchar *response;
|
|
guint a;
|
|
|
|
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
|
|
if (!response)
|
|
return FALSE;
|
|
|
|
if (mm_get_uint_from_str (mm_strip_tag (response, "+CFUN:"), &a)) {
|
|
switch (a) {
|
|
case MBM_NETWORK_MODE_OFFLINE:
|
|
return MM_MODEM_POWER_STATE_OFF;
|
|
|
|
case MBM_NETWORK_MODE_LOW_POWER:
|
|
return MM_MODEM_POWER_STATE_LOW;
|
|
|
|
case MBM_NETWORK_MODE_ANY:
|
|
case MBM_NETWORK_MODE_2G:
|
|
case MBM_NETWORK_MODE_3G:
|
|
return MM_MODEM_POWER_STATE_ON;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_set_error (error,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"Couldn't parse +CFUN response: '%s'",
|
|
response);
|
|
return MM_MODEM_POWER_STATE_UNKNOWN;
|
|
}
|
|
|
|
static void
|
|
load_power_state (MMIfaceModem *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
mm_base_modem_at_command (MM_BASE_MODEM (self),
|
|
"+CFUN?",
|
|
3,
|
|
FALSE,
|
|
callback,
|
|
user_data);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Reset (Modem interface) */
|
|
|
|
static gboolean
|
|
reset_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
/* Ignore errors */
|
|
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
reset (MMIfaceModem *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
mm_base_modem_at_command (MM_BASE_MODEM (self),
|
|
"*E2RESET",
|
|
3,
|
|
FALSE,
|
|
callback,
|
|
user_data);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Factory reset (Modem interface) */
|
|
|
|
static gboolean
|
|
factory_reset_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
/* Ignore errors */
|
|
mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
static const MMBaseModemAtCommand factory_reset_sequence[] = {
|
|
/* Init command */
|
|
{ "&F +CMEE=0", 3, FALSE, NULL },
|
|
{ "+COPS=0", 3, FALSE, NULL },
|
|
{ "+CR=0", 3, FALSE, NULL },
|
|
{ "+CRC=0", 3, FALSE, NULL },
|
|
{ "+CREG=0", 3, FALSE, NULL },
|
|
{ "+CMER=0", 3, FALSE, NULL },
|
|
{ "*EPEE=0", 3, FALSE, NULL },
|
|
{ "+CNMI=2, 0, 0, 0, 0", 3, FALSE, NULL },
|
|
{ "+CGREG=0", 3, FALSE, NULL },
|
|
{ "*EIAD=0", 3, FALSE, NULL },
|
|
{ "+CGSMS=3", 3, FALSE, NULL },
|
|
{ "+CSCA=\"\",129", 3, FALSE, NULL },
|
|
{ NULL }
|
|
};
|
|
|
|
static void
|
|
factory_reset (MMIfaceModem *self,
|
|
const gchar *code,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
mm_dbg ("Ignoring factory reset code: '%s'", code);
|
|
|
|
mm_base_modem_at_sequence (MM_BASE_MODEM (self),
|
|
factory_reset_sequence,
|
|
NULL, /* response_processor_context */
|
|
NULL, /* response_processor_context_free */
|
|
callback,
|
|
user_data);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Load unlock retries (Modem interface) */
|
|
|
|
static MMUnlockRetries *
|
|
load_unlock_retries_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
MMUnlockRetries *unlock_retries;
|
|
const gchar *response;
|
|
gint matched;
|
|
guint a, b, c ,d;
|
|
|
|
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
|
|
if (!response)
|
|
return NULL;
|
|
|
|
matched = sscanf (response, "*EPIN: %d, %d, %d, %d",
|
|
&a, &b, &c, &d);
|
|
if (matched != 4) {
|
|
g_set_error (error,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"Could not parse PIN retries results: '%s'",
|
|
response);
|
|
return NULL;
|
|
}
|
|
|
|
if (a > 998) {
|
|
g_set_error (error,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"Invalid PIN attempts left: '%u'",
|
|
a);
|
|
return NULL;
|
|
}
|
|
|
|
unlock_retries = mm_unlock_retries_new ();
|
|
mm_unlock_retries_set (unlock_retries, MM_MODEM_LOCK_SIM_PIN, a);
|
|
mm_unlock_retries_set (unlock_retries, MM_MODEM_LOCK_SIM_PUK, b);
|
|
mm_unlock_retries_set (unlock_retries, MM_MODEM_LOCK_SIM_PIN2, c);
|
|
mm_unlock_retries_set (unlock_retries, MM_MODEM_LOCK_SIM_PUK2, d);
|
|
return unlock_retries;
|
|
}
|
|
|
|
static void
|
|
load_unlock_retries (MMIfaceModem *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
mm_dbg ("loading unlock retries (mbm)...");
|
|
mm_base_modem_at_command (MM_BASE_MODEM (self),
|
|
"*EPIN?",
|
|
10,
|
|
FALSE,
|
|
callback,
|
|
user_data);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Setup/Cleanup unsolicited events (3GPP interface) */
|
|
|
|
typedef struct {
|
|
MMBearerConnectionStatus status;
|
|
} BearerListReportStatusForeachContext;
|
|
|
|
static void
|
|
bearer_list_report_status_foreach (MMBearer *bearer,
|
|
BearerListReportStatusForeachContext *ctx)
|
|
{
|
|
mm_bearer_report_connection_status (bearer, ctx->status);
|
|
}
|
|
|
|
static void
|
|
e2nap_received (MMAtSerialPort *port,
|
|
GMatchInfo *info,
|
|
MMBroadbandModemMbm *self)
|
|
{
|
|
MMBearerList *list = NULL;
|
|
guint state;
|
|
BearerListReportStatusForeachContext ctx;
|
|
|
|
if (!mm_get_uint_from_match_info (info, 1, &state))
|
|
return;
|
|
|
|
ctx.status = MM_BEARER_CONNECTION_STATUS_UNKNOWN;
|
|
|
|
switch (state) {
|
|
case MBM_E2NAP_DISCONNECTED:
|
|
mm_dbg ("disconnected");
|
|
ctx.status = MM_BEARER_CONNECTION_STATUS_DISCONNECTED;
|
|
break;
|
|
case MBM_E2NAP_CONNECTED:
|
|
mm_dbg ("connected");
|
|
ctx.status = MM_BEARER_CONNECTION_STATUS_CONNECTED;
|
|
break;
|
|
case MBM_E2NAP_CONNECTING:
|
|
mm_dbg ("connecting");
|
|
break;
|
|
default:
|
|
/* Should not happen */
|
|
mm_dbg ("unhandled E2NAP state %d", state);
|
|
}
|
|
|
|
/* If unknown status, don't try to report anything */
|
|
if (ctx.status == MM_BEARER_CONNECTION_STATUS_UNKNOWN)
|
|
return;
|
|
|
|
/* If empty bearer list, nothing else to do */
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_BEARER_LIST, &list,
|
|
NULL);
|
|
if (!list)
|
|
return;
|
|
|
|
mm_bearer_list_foreach (list,
|
|
(MMBearerListForeachFunc)bearer_list_report_status_foreach,
|
|
&ctx);
|
|
g_object_unref (list);
|
|
}
|
|
|
|
static void
|
|
erinfo_received (MMAtSerialPort *port,
|
|
GMatchInfo *info,
|
|
MMBroadbandModemMbm *self)
|
|
{
|
|
MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
|
|
guint mode;
|
|
|
|
if (mm_get_uint_from_match_info (info, 2, &mode)) {
|
|
switch (mode) {
|
|
case 1:
|
|
act = MM_MODEM_ACCESS_TECHNOLOGY_GPRS;
|
|
break;
|
|
case 2:
|
|
act = MM_MODEM_ACCESS_TECHNOLOGY_EDGE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* 3G modes take precedence */
|
|
if (mm_get_uint_from_match_info (info, 3, &mode)) {
|
|
switch (mode) {
|
|
case 1:
|
|
act = MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
|
|
break;
|
|
case 2:
|
|
act = MM_MODEM_ACCESS_TECHNOLOGY_HSDPA;
|
|
break;
|
|
case 3:
|
|
act = MM_MODEM_ACCESS_TECHNOLOGY_HSPA;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self),
|
|
act,
|
|
MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK);
|
|
}
|
|
|
|
static void
|
|
set_unsolicited_events_handlers (MMBroadbandModemMbm *self,
|
|
gboolean enable)
|
|
{
|
|
MMAtSerialPort *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 < 2; i++) {
|
|
if (!ports[i])
|
|
continue;
|
|
|
|
/* Access technology related */
|
|
mm_at_serial_port_add_unsolicited_msg_handler (
|
|
ports[i],
|
|
self->priv->erinfo_regex,
|
|
enable ? (MMAtSerialUnsolicitedMsgFn)erinfo_received : NULL,
|
|
enable ? self : NULL,
|
|
NULL);
|
|
|
|
/* Connection related */
|
|
mm_at_serial_port_add_unsolicited_msg_handler (
|
|
ports[i],
|
|
self->priv->e2nap_regex,
|
|
enable ? (MMAtSerialUnsolicitedMsgFn)e2nap_received : NULL,
|
|
enable ? self : NULL,
|
|
NULL);
|
|
mm_at_serial_port_add_unsolicited_msg_handler (
|
|
ports[i],
|
|
self->priv->e2nap_ext_regex,
|
|
enable ? (MMAtSerialUnsolicitedMsgFn)e2nap_received : NULL,
|
|
enable ? self : NULL,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
|
|
}
|
|
|
|
static void
|
|
parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
|
|
GAsyncResult *res,
|
|
GSimpleAsyncResult *simple)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error))
|
|
g_simple_async_result_take_error (simple, error);
|
|
else {
|
|
/* Our own setup now */
|
|
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_MBM (self), TRUE);
|
|
g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
|
|
}
|
|
|
|
g_simple_async_result_complete (simple);
|
|
g_object_unref (simple);
|
|
}
|
|
|
|
static void
|
|
modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GSimpleAsyncResult *result;
|
|
|
|
result = g_simple_async_result_new (G_OBJECT (self),
|
|
callback,
|
|
user_data,
|
|
modem_3gpp_setup_unsolicited_events);
|
|
|
|
/* Chain up parent's setup */
|
|
iface_modem_3gpp_parent->setup_unsolicited_events (
|
|
self,
|
|
(GAsyncReadyCallback)parent_setup_unsolicited_events_ready,
|
|
result);
|
|
}
|
|
|
|
static void
|
|
parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self,
|
|
GAsyncResult *res,
|
|
GSimpleAsyncResult *simple)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error))
|
|
g_simple_async_result_take_error (simple, error);
|
|
else
|
|
g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
|
|
g_simple_async_result_complete (simple);
|
|
g_object_unref (simple);
|
|
}
|
|
|
|
static void
|
|
modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GSimpleAsyncResult *result;
|
|
|
|
result = g_simple_async_result_new (G_OBJECT (self),
|
|
callback,
|
|
user_data,
|
|
modem_3gpp_cleanup_unsolicited_events);
|
|
|
|
/* Our own cleanup first */
|
|
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_MBM (self), FALSE);
|
|
|
|
/* And now chain up parent's cleanup */
|
|
iface_modem_3gpp_parent->cleanup_unsolicited_events (
|
|
self,
|
|
(GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready,
|
|
result);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Enabling unsolicited events (3GPP interface) */
|
|
|
|
static gboolean
|
|
modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
|
|
}
|
|
|
|
static void
|
|
own_enable_unsolicited_events_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
GSimpleAsyncResult *simple)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
mm_base_modem_at_sequence_full_finish (self, res, NULL, &error);
|
|
if (error)
|
|
g_simple_async_result_take_error (simple, error);
|
|
else
|
|
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
|
|
g_simple_async_result_complete (simple);
|
|
g_object_unref (simple);
|
|
}
|
|
|
|
static const MMBaseModemAtCommand unsolicited_enable_sequence[] = {
|
|
{ "*ERINFO=1", 5, FALSE, NULL },
|
|
{ "*E2NAP=1", 5, FALSE, NULL },
|
|
{ NULL }
|
|
};
|
|
|
|
static void
|
|
parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
|
|
GAsyncResult *res,
|
|
GSimpleAsyncResult *simple)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) {
|
|
g_simple_async_result_take_error (simple, error);
|
|
g_simple_async_result_complete (simple);
|
|
g_object_unref (simple);
|
|
}
|
|
|
|
/* Our own enable now */
|
|
mm_base_modem_at_sequence_full (
|
|
MM_BASE_MODEM (self),
|
|
mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
|
|
unsolicited_enable_sequence,
|
|
NULL, /* response_processor_context */
|
|
NULL, /* response_processor_context_free */
|
|
NULL, /* cancellable */
|
|
(GAsyncReadyCallback)own_enable_unsolicited_events_ready,
|
|
simple);
|
|
}
|
|
|
|
static void
|
|
modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GSimpleAsyncResult *result;
|
|
|
|
result = g_simple_async_result_new (G_OBJECT (self),
|
|
callback,
|
|
user_data,
|
|
modem_3gpp_enable_unsolicited_events);
|
|
|
|
/* Chain up parent's enable */
|
|
iface_modem_3gpp_parent->enable_unsolicited_events (
|
|
self,
|
|
(GAsyncReadyCallback)parent_enable_unsolicited_events_ready,
|
|
result);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Disabling unsolicited events (3GPP interface) */
|
|
|
|
static gboolean
|
|
modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
|
|
}
|
|
|
|
static void
|
|
parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self,
|
|
GAsyncResult *res,
|
|
GSimpleAsyncResult *simple)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error))
|
|
g_simple_async_result_take_error (simple, error);
|
|
else
|
|
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
|
|
g_simple_async_result_complete (simple);
|
|
g_object_unref (simple);
|
|
}
|
|
|
|
static void
|
|
own_disable_unsolicited_events_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
GSimpleAsyncResult *simple)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
mm_base_modem_at_sequence_full_finish (self, res, NULL, &error);
|
|
if (error) {
|
|
g_simple_async_result_take_error (simple, error);
|
|
g_simple_async_result_complete (simple);
|
|
g_object_unref (simple);
|
|
return;
|
|
}
|
|
|
|
/* Next, chain up parent's disable */
|
|
iface_modem_3gpp_parent->disable_unsolicited_events (
|
|
MM_IFACE_MODEM_3GPP (self),
|
|
(GAsyncReadyCallback)parent_disable_unsolicited_events_ready,
|
|
simple);
|
|
}
|
|
|
|
static const MMBaseModemAtCommand unsolicited_disable_sequence[] = {
|
|
{ "*ERINFO=0", 5, FALSE, NULL },
|
|
{ "*E2NAP=0", 5, FALSE, NULL },
|
|
{ NULL }
|
|
};
|
|
|
|
static void
|
|
modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GSimpleAsyncResult *result;
|
|
|
|
result = g_simple_async_result_new (G_OBJECT (self),
|
|
callback,
|
|
user_data,
|
|
modem_3gpp_disable_unsolicited_events);
|
|
|
|
/* Our own disable first */
|
|
mm_base_modem_at_sequence_full (
|
|
MM_BASE_MODEM (self),
|
|
mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
|
|
unsolicited_disable_sequence,
|
|
NULL, /* response_processor_context */
|
|
NULL, /* response_processor_context_free */
|
|
NULL, /* cancellable */
|
|
(GAsyncReadyCallback)own_disable_unsolicited_events_ready,
|
|
result);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Setup ports (Broadband modem class) */
|
|
|
|
static void
|
|
emrdy_received (MMAtSerialPort *port,
|
|
GMatchInfo *info,
|
|
MMBroadbandModemMbm *self)
|
|
{
|
|
self->priv->have_emrdy = TRUE;
|
|
}
|
|
|
|
static void
|
|
setup_ports (MMBroadbandModem *_self)
|
|
{
|
|
MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self);
|
|
MMAtSerialPort *ports[2];
|
|
guint i;
|
|
|
|
/* Call parent's setup ports first always */
|
|
MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_mbm_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));
|
|
|
|
/* Setup unsolicited handlers which should be always on */
|
|
for (i = 0; i < 2; i++) {
|
|
if (!ports[i])
|
|
continue;
|
|
|
|
/* The Ericsson modems always have a free AT command port, so we
|
|
* don't need to flash the ports when disconnecting to get back to
|
|
* command mode. F5521gw R2A07 resets port properties like echo when
|
|
* flashed, leading to confusion. bgo #650740
|
|
*/
|
|
g_object_set (G_OBJECT (ports[i]), MM_SERIAL_PORT_FLASH_OK, FALSE, NULL);
|
|
|
|
mm_at_serial_port_add_unsolicited_msg_handler (
|
|
ports[i],
|
|
self->priv->emrdy_regex,
|
|
(MMAtSerialUnsolicitedMsgFn)emrdy_received,
|
|
self,
|
|
NULL);
|
|
|
|
/* Several unsolicited messages to always ignore... */
|
|
mm_at_serial_port_add_unsolicited_msg_handler (
|
|
ports[i],
|
|
self->priv->pacsp_regex,
|
|
NULL, NULL, NULL);
|
|
|
|
mm_at_serial_port_add_unsolicited_msg_handler (
|
|
ports[i],
|
|
self->priv->estksmenu_regex,
|
|
NULL, NULL, NULL);
|
|
|
|
mm_at_serial_port_add_unsolicited_msg_handler (
|
|
ports[i],
|
|
self->priv->estksms_regex,
|
|
NULL, NULL, NULL);
|
|
|
|
mm_at_serial_port_add_unsolicited_msg_handler (
|
|
ports[i],
|
|
self->priv->emwi_regex,
|
|
NULL, NULL, NULL);
|
|
}
|
|
|
|
/* Now reset the unsolicited messages we'll handle when enabled */
|
|
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_MBM (self), FALSE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
MMBroadbandModemMbm *
|
|
mm_broadband_modem_mbm_new (const gchar *device,
|
|
const gchar **drivers,
|
|
const gchar *plugin,
|
|
guint16 vendor_id,
|
|
guint16 product_id)
|
|
{
|
|
return g_object_new (MM_TYPE_BROADBAND_MODEM_MBM,
|
|
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,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
mm_broadband_modem_mbm_init (MMBroadbandModemMbm *self)
|
|
{
|
|
/* Initialize private data */
|
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
|
|
MM_TYPE_BROADBAND_MODEM_MBM,
|
|
MMBroadbandModemMbmPrivate);
|
|
|
|
/* Prepare regular expressions to setup */
|
|
self->priv->e2nap_regex = g_regex_new ("\\r\\n\\*E2NAP: (\\d)\\r\\n",
|
|
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
|
|
self->priv->e2nap_ext_regex = g_regex_new ("\\r\\n\\*E2NAP: (\\d),.*\\r\\n",
|
|
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
|
|
self->priv->emrdy_regex = g_regex_new ("\\r\\n\\*EMRDY: \\d\\r\\n",
|
|
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
|
|
self->priv->pacsp_regex = g_regex_new ("\\r\\n\\+PACSP(\\d)\\r\\n",
|
|
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
|
|
self->priv->estksmenu_regex = g_regex_new ("\\R\\*ESTKSMENU:.*\\R",
|
|
G_REGEX_RAW | G_REGEX_OPTIMIZE | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF, G_REGEX_MATCH_NEWLINE_CRLF, NULL);
|
|
self->priv->estksms_regex = g_regex_new ("\\r\\n\\*ESTKSMS:.*\\r\\n",
|
|
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
|
|
self->priv->emwi_regex = g_regex_new ("\\r\\n\\*EMWI: (\\d),(\\d).*\\r\\n",
|
|
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
|
|
self->priv->erinfo_regex = g_regex_new ("\\r\\n\\*ERINFO:\\s*(\\d),(\\d),(\\d).*\\r\\n",
|
|
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
|
|
|
|
self->priv->mbm_mode = MBM_NETWORK_MODE_ANY;
|
|
}
|
|
|
|
static void
|
|
finalize (GObject *object)
|
|
{
|
|
MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (object);
|
|
|
|
g_regex_unref (self->priv->e2nap_regex);
|
|
g_regex_unref (self->priv->e2nap_ext_regex);
|
|
g_regex_unref (self->priv->emrdy_regex);
|
|
g_regex_unref (self->priv->pacsp_regex);
|
|
g_regex_unref (self->priv->estksmenu_regex);
|
|
g_regex_unref (self->priv->estksms_regex);
|
|
g_regex_unref (self->priv->emwi_regex);
|
|
g_regex_unref (self->priv->erinfo_regex);
|
|
|
|
G_OBJECT_CLASS (mm_broadband_modem_mbm_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
iface_modem_init (MMIfaceModem *iface)
|
|
{
|
|
iface_modem_parent = g_type_interface_peek_parent (iface);
|
|
|
|
iface->create_bearer = modem_create_bearer;
|
|
iface->create_bearer_finish = modem_create_bearer_finish;
|
|
iface->create_sim = create_sim;
|
|
iface->create_sim_finish = create_sim_finish;
|
|
iface->modem_after_sim_unlock = modem_after_sim_unlock;
|
|
iface->modem_after_sim_unlock_finish = modem_after_sim_unlock_finish;
|
|
iface->load_supported_modes = load_supported_modes;
|
|
iface->load_supported_modes_finish = load_supported_modes_finish;
|
|
iface->load_current_modes = load_current_modes;
|
|
iface->load_current_modes_finish = load_current_modes_finish;
|
|
iface->set_current_modes = set_current_modes;
|
|
iface->set_current_modes_finish = set_current_modes_finish;
|
|
iface->reset = reset;
|
|
iface->reset_finish = reset_finish;
|
|
iface->factory_reset = factory_reset;
|
|
iface->factory_reset_finish = factory_reset_finish;
|
|
iface->load_unlock_retries = load_unlock_retries;
|
|
iface->load_unlock_retries_finish = load_unlock_retries_finish;
|
|
iface->load_power_state = load_power_state;
|
|
iface->load_power_state_finish = load_power_state_finish;
|
|
iface->modem_power_up = modem_power_up;
|
|
iface->modem_power_up_finish = modem_power_up_finish;
|
|
iface->modem_power_down = modem_power_down;
|
|
iface->modem_power_down_finish = modem_power_down_finish;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static void
|
|
mm_broadband_modem_mbm_class_init (MMBroadbandModemMbmClass *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 (MMBroadbandModemMbmPrivate));
|
|
|
|
object_class->finalize = finalize;
|
|
broadband_modem_class->setup_ports = setup_ports;
|
|
broadband_modem_class->enabling_modem_init = enabling_modem_init;
|
|
broadband_modem_class->enabling_modem_init_finish = enabling_modem_init_finish;
|
|
}
|