blob: 816196dc04073f9a81ed93352d2e8ddd4ce590b0 [file] [log] [blame]
/* -*- 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 - 2012 Google, Inc.
*/
#include <stdlib.h>
#include <string.h>
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-iface-modem.h"
#include "mm-iface-modem-location.h"
#include "mm-iface-modem-3gpp.h"
#include "mm-base-modem.h"
#include "mm-modem-helpers.h"
#include "mm-error-helpers.h"
#include "mm-log.h"
#define SUBSYSTEM_3GPP "3gpp"
/* When comparing EPS bearer settings take into account that:
* - 'password' may not always be readable.
* - 'apn-type' may not always be supported.
* - 'profile-id' will not be known in the requested settings
* - we ignore settings not applicable to profiles, like 'allow-roaming' or
* 'rm-protocol'.
* - we apply very loose matching for all fields.
*/
#define MM_BEARER_PROPERTIES_CMP_FLAGS_EPS \
(MM_BEARER_PROPERTIES_CMP_FLAGS_LOOSE | \
MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PROFILE_ID | \
MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PASSWORD | \
MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ALLOW_ROAMING | \
MM_BEARER_PROPERTIES_CMP_FLAGS_NO_APN_TYPE | \
MM_BEARER_PROPERTIES_CMP_FLAGS_NO_RM_PROTOCOL)
/*****************************************************************************/
/* Private data context */
#define PRIVATE_TAG "iface-modem-3gpp-private-tag"
static GQuark private_quark;
typedef struct {
/* Registration state */
MMModem3gppRegistrationState state_cs;
MMModem3gppRegistrationState state_ps;
MMModem3gppRegistrationState state_eps;
MMModem3gppRegistrationState state_5gs;
gboolean manual_registration;
gchar *manual_registration_operator_id;
GCancellable *pending_registration_cancellable;
gboolean reloading_registration_info;
/* Registration checks */
guint check_timeout_source;
gboolean check_running;
} Private;
static void
private_free (Private *priv)
{
g_free (priv->manual_registration_operator_id);
if (priv->pending_registration_cancellable) {
g_cancellable_cancel (priv->pending_registration_cancellable);
g_object_unref (priv->pending_registration_cancellable);
}
if (priv->check_timeout_source)
g_source_remove (priv->check_timeout_source);
g_slice_free (Private, priv);
}
static Private *
get_private (MMIfaceModem3gpp *self)
{
Private *priv;
if (G_UNLIKELY (!private_quark))
private_quark = g_quark_from_static_string (PRIVATE_TAG);
priv = g_object_get_qdata (G_OBJECT (self), private_quark);
if (!priv) {
priv = g_slice_new0 (Private);
priv->state_cs = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
priv->state_ps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
priv->state_eps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
priv->state_5gs = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
}
return priv;
}
/*****************************************************************************/
void
mm_iface_modem_3gpp_bind_simple_status (MMIfaceModem3gpp *self,
MMSimpleStatus *status)
{
MmGdbusModem3gpp *skeleton;
g_object_get (self,
MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton,
NULL);
if (!skeleton)
return;
g_object_bind_property (skeleton, "registration-state",
status, MM_SIMPLE_PROPERTY_3GPP_REGISTRATION_STATE,
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
g_object_bind_property (skeleton, "operator-code",
status, MM_SIMPLE_PROPERTY_3GPP_OPERATOR_CODE,
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
g_object_bind_property (skeleton, "operator-name",
status, MM_SIMPLE_PROPERTY_3GPP_OPERATOR_NAME,
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
g_object_unref (skeleton);
}
/*****************************************************************************/
#define REG_STATE_IS_REGISTERED(state) \
(state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || \
state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING || \
state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY || \
state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_SMS_ONLY || \
state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME_CSFB_NOT_PREFERRED || \
state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_CSFB_NOT_PREFERRED)
#define REG_STATE_IS_UNKNOWN_IDLE_DENIED(state) \
(state == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN || \
state == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE || \
state == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED)
static MMModem3gppRegistrationState
get_consolidated_reg_state (MMIfaceModem3gpp *self)
{
Private *priv;
priv = get_private (self);
/* Some devices (Blackberries for example) will respond to +CGREG, but
* return ERROR for +CREG, probably because their firmware is just stupid.
* So here we prefer the +CREG response, but if we never got a successful
* +CREG response, we'll take +CGREG instead.
*/
if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
return priv->state_cs;
if (priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
return priv->state_ps;
if (priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
return priv->state_eps;
if (priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
return priv->state_5gs;
/* Searching? */
if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING ||
priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING ||
priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING ||
priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING)
return MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING;
/* If at least one state is DENIED and the others are UNKNOWN or IDLE, use DENIED */
if ((priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED ||
priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED ||
priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED ||
priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED) &&
REG_STATE_IS_UNKNOWN_IDLE_DENIED (priv->state_cs) &&
REG_STATE_IS_UNKNOWN_IDLE_DENIED (priv->state_ps) &&
REG_STATE_IS_UNKNOWN_IDLE_DENIED (priv->state_eps) &&
REG_STATE_IS_UNKNOWN_IDLE_DENIED (priv->state_5gs))
return MM_MODEM_3GPP_REGISTRATION_STATE_DENIED;
/* Emergency services? */
if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY ||
priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY ||
priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY ||
priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY)
return MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY;
/* Support for additional registration states reported when on LTE/5GNR.
*
* For example, we may see the modem registered in LTE (EPS==HOME), and we
* may get "SMS only" reported for CS.
*
* We give these states a very low priority w.r.t. the other ones as they
* are really likely never used (i.e. we would get as consolidated the LTE
* registration state, not the CS fall back state).
*
* We also warn in that case, because ideally we should always report the
* LTE registration state first, not this one.
*/
if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY ||
priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_SMS_ONLY ||
priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME_CSFB_NOT_PREFERRED ||
priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_CSFB_NOT_PREFERRED) {
mm_obj_warn (self, "3GPP CSFB registration state is consolidated: %s",
mm_modem_3gpp_registration_state_get_string (priv->state_cs));
return priv->state_cs;
}
/* Idle? */
if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE ||
priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE ||
priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE ||
priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE)
return MM_MODEM_3GPP_REGISTRATION_STATE_IDLE;
return MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
}
/*****************************************************************************/
typedef struct {
MMIfaceModem3gpp *self;
MmGdbusModem3gpp *skeleton;
GCancellable *cancellable;
gchar *operator_id;
GTimer *timer;
guint max_registration_time;
} RegisterInNetworkContext;
static void
register_in_network_context_free (RegisterInNetworkContext *ctx)
{
if (ctx->timer)
g_timer_destroy (ctx->timer);
if (ctx->cancellable) {
Private *priv;
/* Clear our cancellable if still around */
priv = get_private (ctx->self);
if (priv->pending_registration_cancellable == ctx->cancellable)
g_clear_object (&priv->pending_registration_cancellable);
g_object_unref (ctx->cancellable);
}
g_free (ctx->operator_id);
if (ctx->skeleton)
g_object_unref (ctx->skeleton);
g_object_unref (ctx->self);
g_slice_free (RegisterInNetworkContext, ctx);
}
static void
register_in_network_context_complete_failed (GTask *task,
GError *error)
{
RegisterInNetworkContext *ctx;
ctx = g_task_get_task_data (task);
mm_iface_modem_3gpp_update_cs_registration_state (ctx->self, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE);
mm_iface_modem_3gpp_update_ps_registration_state (ctx->self, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE);
mm_iface_modem_3gpp_update_eps_registration_state (ctx->self, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE);
mm_iface_modem_3gpp_update_access_technologies (ctx->self, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
mm_iface_modem_3gpp_update_location (ctx->self, 0, 0, 0);
g_task_return_error (task, error);
g_object_unref (task);
}
gboolean
mm_iface_modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static gboolean run_registration_checks (GTask *task);
static void
run_registration_checks_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
RegisterInNetworkContext *ctx;
GError *error = NULL;
MMModem3gppRegistrationState current_registration_state;
ctx = g_task_get_task_data (task);
mm_iface_modem_3gpp_run_registration_checks_finish (MM_IFACE_MODEM_3GPP (self), res, &error);
if (error) {
mm_obj_dbg (self, "3GPP registration check failed: %s", error->message);
register_in_network_context_complete_failed (task, error);
return;
}
current_registration_state = get_consolidated_reg_state (ctx->self);
/* If we got a final state and it's denied, we can assume the registration is
* finished */
if (current_registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED) {
mm_obj_dbg (self, "registration denied");
register_in_network_context_complete_failed (
task,
mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_NOT_ALLOWED, self));
return;
}
/* If we got registered, end registration checks */
if (REG_STATE_IS_REGISTERED (current_registration_state)) {
/* Request immediate access tech and signal update: we may have changed
* from home to roaming or viceversa, both registered states, so there
* wouldn't be an explicit refresh triggered from the modem interface as
* the modem never got un-registered during the sequence. */
mm_iface_modem_refresh_signal (MM_IFACE_MODEM (ctx->self));
mm_obj_dbg (self, "currently registered in a 3GPP network");
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
/* Don't spend too much time waiting to get registered */
if (g_timer_elapsed (ctx->timer, NULL) > ctx->max_registration_time) {
mm_obj_dbg (self, "3GPP registration check timed out");
register_in_network_context_complete_failed (
task,
mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, self));
return;
}
/* If we're still waiting for automatic registration to complete or
* fail, check again in a few seconds.
*
* This 3s timeout will catch results from automatic registrations as
* well.
*/
mm_obj_dbg (self, "not yet registered in a 3GPP network... will recheck soon");
g_timeout_add_seconds (3, (GSourceFunc)run_registration_checks, task);
}
static gboolean
run_registration_checks (GTask *task)
{
RegisterInNetworkContext *ctx;
ctx = g_task_get_task_data (task);
/* Get fresh registration state */
mm_iface_modem_3gpp_run_registration_checks (
ctx->self,
(GAsyncReadyCallback)run_registration_checks_ready,
task);
return G_SOURCE_REMOVE;
}
static void
register_in_network_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->register_in_network_finish (self, res, &error)) {
/* Propagate error when trying to lock to network */
register_in_network_context_complete_failed (task, error);
return;
}
/* Now try to gather current registration status until we're registered or
* the time goes off */
run_registration_checks (task);
}
void
mm_iface_modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
const gchar *operator_id,
gboolean force_registration,
guint max_registration_time,
GAsyncReadyCallback callback,
gpointer user_data)
{
RegisterInNetworkContext *ctx;
const gchar *current_operator_code;
GError *error = NULL;
GTask *task;
Private *priv;
MMModem3gppRegistrationState reg_state;
priv = get_private (self);
ctx = g_slice_new0 (RegisterInNetworkContext);
ctx->self = g_object_ref (self);
ctx->operator_id = (operator_id && operator_id[0]) ? g_strdup (operator_id) : NULL;
ctx->max_registration_time = max_registration_time;
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)register_in_network_context_free);
g_object_get (self,
MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &ctx->skeleton,
NULL);
if (!ctx->skeleton) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't get interface skeleton");
g_object_unref (task);
return;
}
/* Validate input MCC/MNC */
if (ctx->operator_id && !mm_3gpp_parse_operator_id (ctx->operator_id, NULL, NULL, NULL, &error)) {
g_assert (error != NULL);
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* (Try to) cancel previous registration request */
if (priv->pending_registration_cancellable) {
g_cancellable_cancel (priv->pending_registration_cancellable);
g_clear_object (&priv->pending_registration_cancellable);
}
current_operator_code = mm_gdbus_modem3gpp_get_operator_code (ctx->skeleton);
reg_state = mm_gdbus_modem3gpp_get_registration_state (ctx->skeleton);
/* Manual registration requested? */
if (ctx->operator_id) {
/* If already registered manually with the requested operator, we're done */
if (!force_registration &&
(g_strcmp0 (current_operator_code, ctx->operator_id) == 0) &&
REG_STATE_IS_REGISTERED (reg_state) &&
priv->manual_registration) {
mm_obj_dbg (self, "already registered manually in selected network '%s', manual registration not launched...",
current_operator_code);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
/* Manual registration to a new operator required */
mm_obj_dbg (self, "launching manual network registration (%s)...", ctx->operator_id);
g_free (priv->manual_registration_operator_id);
priv->manual_registration_operator_id = g_strdup (ctx->operator_id);
priv->manual_registration = TRUE;
}
/* Automatic registration requested? */
else {
/* If the modem is already registered and the last time it was asked
* automatic registration, we're done */
if (!force_registration &&
REG_STATE_IS_REGISTERED (reg_state) &&
!priv->manual_registration) {
mm_obj_dbg (self, "already registered automatically in network '%s',"
" automatic registration not launched...",
current_operator_code ? current_operator_code : "unknown");
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
/* Automatic registration to a new operator requested */
mm_obj_dbg (self, "launching automatic network registration...");
g_clear_pointer (&priv->manual_registration_operator_id, g_free);
priv->manual_registration = FALSE;
}
ctx->cancellable = g_cancellable_new ();
/* Keep an accessible reference to the cancellable, so that we can cancel
* previous request when needed */
priv->pending_registration_cancellable = g_object_ref (ctx->cancellable);
ctx->timer = g_timer_new ();
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->register_in_network (
self,
ctx->operator_id,
ctx->cancellable,
(GAsyncReadyCallback)register_in_network_ready,
task);
}
/*****************************************************************************/
/* Request to reregister using the last settings */
#define REREGISTER_IN_NETWORK_TIMEOUT 120
gboolean
mm_iface_modem_3gpp_reregister_in_network_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return mm_iface_modem_3gpp_register_in_network_finish (self, res, error);
}
void
mm_iface_modem_3gpp_reregister_in_network (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
Private *priv;
/* Relaunch registration using the last used settings */
priv = get_private (self);
mm_iface_modem_3gpp_register_in_network (self,
priv->manual_registration_operator_id,
TRUE, /* if already registered with same settings, force re-registration */
REREGISTER_IN_NETWORK_TIMEOUT,
callback,
user_data);
}
/*****************************************************************************/
typedef struct {
MmGdbusModem3gpp *skeleton;
GDBusMethodInvocation *invocation;
MMIfaceModem3gpp *self;
gchar *operator_id;
} HandleRegisterContext;
static void
handle_register_context_free (HandleRegisterContext *ctx)
{
g_object_unref (ctx->skeleton);
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_free (ctx->operator_id);
g_free (ctx);
}
static void
handle_register_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
HandleRegisterContext *ctx)
{
GError *error = NULL;
if (!mm_iface_modem_3gpp_register_in_network_finish (self, res, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
else
mm_gdbus_modem3gpp_complete_register (ctx->skeleton, ctx->invocation);
handle_register_context_free (ctx);
}
static void
handle_register_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleRegisterContext *ctx)
{
MMModemState modem_state;
GError *error = NULL;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_register_context_free (ctx);
return;
}
g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->register_in_network != NULL);
g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->register_in_network_finish != NULL);
modem_state = MM_MODEM_STATE_UNKNOWN;
g_object_get (self,
MM_IFACE_MODEM_STATE, &modem_state,
NULL);
switch (modem_state) {
case MM_MODEM_STATE_FAILED:
case MM_MODEM_STATE_UNKNOWN:
case MM_MODEM_STATE_LOCKED:
/* We should never have such request (interface wasn't exported yet) */
g_assert_not_reached ();
break;
case MM_MODEM_STATE_INITIALIZING:
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot register modem: "
"device not fully initialized yet");
break;
case MM_MODEM_STATE_ENABLED:
case MM_MODEM_STATE_SEARCHING:
case MM_MODEM_STATE_REGISTERED:
mm_iface_modem_3gpp_register_in_network (MM_IFACE_MODEM_3GPP (self),
ctx->operator_id,
FALSE, /* if already registered with same settings, do nothing */
60,
(GAsyncReadyCallback)handle_register_ready,
ctx);
return;
case MM_MODEM_STATE_DISABLING:
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot register modem: "
"currently being disabled");
break;
case MM_MODEM_STATE_ENABLING:
case MM_MODEM_STATE_DISABLED:
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot register modem: "
"not yet enabled");
break;
case MM_MODEM_STATE_DISCONNECTING:
case MM_MODEM_STATE_CONNECTING:
case MM_MODEM_STATE_CONNECTED:
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot register modem: "
"modem is connected");
break;
default:
g_assert_not_reached ();
}
handle_register_context_free (ctx);
}
static gboolean
handle_register (MmGdbusModem3gpp *skeleton,
GDBusMethodInvocation *invocation,
const gchar *operator_id,
MMIfaceModem3gpp *self)
{
HandleRegisterContext *ctx;
ctx = g_new (HandleRegisterContext, 1);
ctx->skeleton = g_object_ref (skeleton);
ctx->invocation = g_object_ref (invocation);
ctx->self = g_object_ref (self);
ctx->operator_id = g_strdup (operator_id);
mm_base_modem_authorize (MM_BASE_MODEM (self),
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
(GAsyncReadyCallback)handle_register_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
typedef struct {
MmGdbusModem3gpp *skeleton;
GDBusMethodInvocation *invocation;
MMIfaceModem3gpp *self;
} HandleScanContext;
static void
handle_scan_context_free (HandleScanContext *ctx)
{
g_object_unref (ctx->skeleton);
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_free (ctx);
}
static GVariant *
scan_networks_build_result (GList *info_list)
{
GList *l;
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
for (l = info_list; l; l = g_list_next (l)) {
MM3gppNetworkInfo *info = l->data;
if (!info->operator_code) {
g_warn_if_reached ();
continue;
}
g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}"));
g_variant_builder_add (&builder, "{sv}",
"operator-code", g_variant_new_string (info->operator_code));
g_variant_builder_add (&builder, "{sv}",
"status", g_variant_new_uint32 (info->status));
g_variant_builder_add (&builder, "{sv}",
"access-technology", g_variant_new_uint32 (info->access_tech));
if (info->operator_long)
g_variant_builder_add (&builder, "{sv}",
"operator-long", g_variant_new_string (info->operator_long));
if (info->operator_short)
g_variant_builder_add (&builder, "{sv}",
"operator-short", g_variant_new_string (info->operator_short));
g_variant_builder_close (&builder);
}
return g_variant_ref (g_variant_builder_end (&builder));
}
static void
handle_scan_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
HandleScanContext *ctx)
{
GError *error = NULL;
GList *info_list;
info_list = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->scan_networks_finish (self, res, &error);
if (error)
g_dbus_method_invocation_take_error (ctx->invocation, error);
else {
GVariant *dict_array;
dict_array = scan_networks_build_result (info_list);
mm_gdbus_modem3gpp_complete_scan (ctx->skeleton,
ctx->invocation,
dict_array);
g_variant_unref (dict_array);
}
mm_3gpp_network_info_list_free (info_list);
handle_scan_context_free (ctx);
}
static void
handle_scan_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleScanContext *ctx)
{
MMModemState modem_state;
GError *error = NULL;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_scan_context_free (ctx);
return;
}
/* If scanning is not implemented, report an error */
if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->scan_networks ||
!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->scan_networks_finish) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Cannot scan networks: operation not supported");
handle_scan_context_free (ctx);
return;
}
modem_state = MM_MODEM_STATE_UNKNOWN;
g_object_get (self,
MM_IFACE_MODEM_STATE, &modem_state,
NULL);
switch (modem_state) {
case MM_MODEM_STATE_FAILED:
case MM_MODEM_STATE_UNKNOWN:
case MM_MODEM_STATE_LOCKED:
/* We should never have such request (interface wasn't exported yet) */
g_assert_not_reached ();
break;
case MM_MODEM_STATE_INITIALIZING:
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot scan networks: "
"device not fully initialized yet");
break;
case MM_MODEM_STATE_DISABLED:
case MM_MODEM_STATE_DISABLING:
case MM_MODEM_STATE_ENABLING:
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot scan networks: not enabled yet");
break;
case MM_MODEM_STATE_ENABLED:
case MM_MODEM_STATE_SEARCHING:
case MM_MODEM_STATE_REGISTERED:
case MM_MODEM_STATE_DISCONNECTING:
case MM_MODEM_STATE_CONNECTING:
case MM_MODEM_STATE_CONNECTED:
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->scan_networks (
MM_IFACE_MODEM_3GPP (self),
(GAsyncReadyCallback)handle_scan_ready,
ctx);
return;
default:
g_assert_not_reached ();
}
handle_scan_context_free (ctx);
}
static gboolean
handle_scan (MmGdbusModem3gpp *skeleton,
GDBusMethodInvocation *invocation,
MMIfaceModem3gpp *self)
{
HandleScanContext *ctx;
ctx = g_new (HandleScanContext, 1);
ctx->skeleton = g_object_ref (skeleton);
ctx->invocation = g_object_ref (invocation);
ctx->self = g_object_ref (self);
mm_base_modem_authorize (MM_BASE_MODEM (self),
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
(GAsyncReadyCallback)handle_scan_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
typedef struct {
MmGdbusModem3gpp *skeleton;
GDBusMethodInvocation *invocation;
MMIfaceModem3gpp *self;
MMModem3gppEpsUeModeOperation mode;
} HandleSetEpsUeModeOperationContext;
static void
handle_set_eps_ue_mode_operation_context_free (HandleSetEpsUeModeOperationContext *ctx)
{
g_object_unref (ctx->skeleton);
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_slice_free (HandleSetEpsUeModeOperationContext, ctx);
}
static void
after_set_load_eps_ue_mode_operation_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
HandleSetEpsUeModeOperationContext *ctx)
{
MMModem3gppEpsUeModeOperation uemode;
GError *error = NULL;
uemode = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish (self, res, &error);
if (error) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_set_eps_ue_mode_operation_context_free (ctx);
return;
}
if (uemode != ctx->mode) {
g_dbus_method_invocation_return_error_literal (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"UE mode of operation for EPS wasn't updated");
handle_set_eps_ue_mode_operation_context_free (ctx);
return;
}
mm_gdbus_modem3gpp_set_eps_ue_mode_operation (ctx->skeleton, uemode);
mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation (ctx->skeleton, ctx->invocation);
handle_set_eps_ue_mode_operation_context_free (ctx);
}
static void
handle_set_eps_ue_mode_operation_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
HandleSetEpsUeModeOperationContext *ctx)
{
GError *error = NULL;
if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_set_eps_ue_mode_operation_context_free (ctx);
return;
}
if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation (
self,
(GAsyncReadyCallback)after_set_load_eps_ue_mode_operation_ready,
ctx);
return;
}
/* Assume we're ok */
mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation (ctx->skeleton, ctx->invocation);
handle_set_eps_ue_mode_operation_context_free (ctx);
}
static void
handle_set_eps_ue_mode_operation_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleSetEpsUeModeOperationContext *ctx)
{
GError *error = NULL;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_set_eps_ue_mode_operation_context_free (ctx);
return;
}
/* Check if we already are in the requested mode */
if (mm_gdbus_modem3gpp_get_eps_ue_mode_operation (ctx->skeleton) == ctx->mode) {
/* Nothing to do */
mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation (ctx->skeleton, ctx->invocation);
handle_set_eps_ue_mode_operation_context_free (ctx);
return;
}
/* If UE mode update is not implemented, report an error */
if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation ||
!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation_finish) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Cannot set UE mode of operation for EPS: operation not supported");
handle_set_eps_ue_mode_operation_context_free (ctx);
return;
}
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation (
MM_IFACE_MODEM_3GPP (self),
ctx->mode,
(GAsyncReadyCallback)handle_set_eps_ue_mode_operation_ready,
ctx);
}
static gboolean
handle_set_eps_ue_mode_operation (MmGdbusModem3gpp *skeleton,
GDBusMethodInvocation *invocation,
guint mode,
MMIfaceModem3gpp *self)
{
HandleSetEpsUeModeOperationContext *ctx;
ctx = g_slice_new (HandleSetEpsUeModeOperationContext);
ctx->skeleton = g_object_ref (skeleton);
ctx->invocation = g_object_ref (invocation);
ctx->self = g_object_ref (self);
ctx->mode = mode;
mm_base_modem_authorize (MM_BASE_MODEM (self),
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
(GAsyncReadyCallback)handle_set_eps_ue_mode_operation_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
typedef struct {
MmGdbusModem3gpp *skeleton;
GDBusMethodInvocation *invocation;
MMIfaceModem3gpp *self;
GVariant *dictionary;
MMBearerProperties *config;
} HandleSetInitialEpsBearerSettingsContext;
static void
handle_set_initial_eps_bearer_settings_context_free (HandleSetInitialEpsBearerSettingsContext *ctx)
{
g_clear_object (&ctx->config);
g_variant_unref (ctx->dictionary);
g_object_unref (ctx->skeleton);
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_slice_free (HandleSetInitialEpsBearerSettingsContext, ctx);
}
static void
log_initial_eps_bearer_settings (MMIfaceModem3gpp *self,
MMBearerProperties *properties)
{
const gchar *apn;
MMBearerAllowedAuth allowed_auth;
const gchar *user;
const gchar *password;
MMBearerIpFamily ip_family;
apn = mm_bearer_properties_get_apn (properties);
if (apn)
mm_obj_dbg (self, " APN: '%s'", apn);
allowed_auth = mm_bearer_properties_get_allowed_auth (properties);
if (allowed_auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN) {
g_autofree gchar *allowed_auth_str = NULL;
allowed_auth_str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth);
mm_obj_dbg (self, " allowed auth: '%s'", allowed_auth_str);
}
user = mm_bearer_properties_get_user (properties);
if (user)
mm_obj_dbg (self, " user: '%s'", user);
password = mm_bearer_properties_get_password (properties);
if (password)
mm_obj_dbg (self, " password: '%s'", password);
ip_family = mm_bearer_properties_get_ip_type (properties);
if (ip_family != MM_BEARER_IP_FAMILY_NONE) {
g_autofree gchar *ip_family_str = NULL;
ip_family_str = mm_bearer_ip_family_build_string_from_mask (ip_family);
mm_obj_dbg (self, " ip family: '%s'", ip_family_str);
}
}
static void
after_set_load_initial_eps_bearer_settings_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
HandleSetInitialEpsBearerSettingsContext *ctx)
{
GError *error = NULL;
MMBearerProperties *new_config;
new_config = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings_finish (self, res, &error);
if (error) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_set_initial_eps_bearer_settings_context_free (ctx);
return;
}
mm_obj_dbg (self, "Updated initial EPS bearer settings:");
log_initial_eps_bearer_settings (self, new_config);
/* When we request to set an empty/NULL APN, we instead select the default initial attach APN used by the modem,
which will probably not be an empty APN. If that's the case, we won't check for equality between the requested
APN and the APN which is now set. */
if (mm_bearer_properties_get_apn (ctx->config) && g_strcmp0 (mm_bearer_properties_get_apn (ctx->config), "") != 0 &&
!mm_bearer_properties_cmp (new_config, ctx->config, MM_BEARER_PROPERTIES_CMP_FLAGS_EPS)) {
mm_obj_dbg (self, "Requested initial EPS bearer settings:");
log_initial_eps_bearer_settings (self, ctx->config);
g_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Initial EPS bearer settings were not updated");
} else {
GVariant *dictionary;
dictionary = mm_bearer_properties_get_dictionary (new_config);
mm_gdbus_modem3gpp_set_initial_eps_bearer_settings (ctx->skeleton, dictionary);
if (dictionary)
g_variant_unref (dictionary);
mm_gdbus_modem3gpp_complete_set_initial_eps_bearer_settings (ctx->skeleton, ctx->invocation);
}
handle_set_initial_eps_bearer_settings_context_free (ctx);
g_object_unref (new_config);
}
static void
set_initial_eps_bearer_settings_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
HandleSetInitialEpsBearerSettingsContext *ctx)
{
GError *error = NULL;
if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_initial_eps_bearer_settings_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_set_initial_eps_bearer_settings_context_free (ctx);
return;
}
if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings (
self,
(GAsyncReadyCallback)after_set_load_initial_eps_bearer_settings_ready,
ctx);
return;
}
/* Assume we're ok */
mm_gdbus_modem3gpp_complete_set_initial_eps_bearer_settings (ctx->skeleton, ctx->invocation);
handle_set_initial_eps_bearer_settings_context_free (ctx);
}
static void
set_initial_eps_bearer_settings_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleSetInitialEpsBearerSettingsContext *ctx)
{
GError *error = NULL;
MMBearerProperties *old_config = NULL;
GVariant *old_dictionary;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_set_initial_eps_bearer_settings_context_free (ctx);
return;
}
/* If UE mode update is not implemented, report an error */
if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_initial_eps_bearer_settings ||
!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_initial_eps_bearer_settings_finish) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Cannot set initial EPS bearer settings: operation not supported");
handle_set_initial_eps_bearer_settings_context_free (ctx);
return;
}
ctx->config = mm_bearer_properties_new_from_dictionary (ctx->dictionary, &error);
if (!ctx->config) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_set_initial_eps_bearer_settings_context_free (ctx);
return;
}
old_dictionary = mm_gdbus_modem3gpp_get_initial_eps_bearer_settings (ctx->skeleton);
if (old_dictionary)
old_config = mm_bearer_properties_new_from_dictionary (old_dictionary, NULL);
if (old_config && mm_bearer_properties_cmp (ctx->config, old_config, MM_BEARER_PROPERTIES_CMP_FLAGS_EPS)) {
mm_obj_dbg (self, "Skipping initial eps bearer configuration. Same configuration provided");
mm_gdbus_modem3gpp_complete_set_initial_eps_bearer_settings (ctx->skeleton, ctx->invocation);
handle_set_initial_eps_bearer_settings_context_free (ctx);
} else {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_initial_eps_bearer_settings (
MM_IFACE_MODEM_3GPP (self),
ctx->config,
(GAsyncReadyCallback)set_initial_eps_bearer_settings_ready,
ctx);
}
g_clear_object (&old_config);
}
static gboolean
handle_set_initial_eps_bearer_settings (MmGdbusModem3gpp *skeleton,
GDBusMethodInvocation *invocation,
GVariant *dictionary,
MMIfaceModem3gpp *self)
{
HandleSetInitialEpsBearerSettingsContext *ctx;
ctx = g_slice_new0 (HandleSetInitialEpsBearerSettingsContext);
ctx->skeleton = g_object_ref (skeleton);
ctx->invocation = g_object_ref (invocation);
ctx->self = g_object_ref (self);
ctx->dictionary = g_variant_ref (dictionary);
mm_base_modem_authorize (MM_BASE_MODEM (self),
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
(GAsyncReadyCallback)set_initial_eps_bearer_settings_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
typedef struct {
MmGdbusModem3gpp *skeleton;
GDBusMethodInvocation *invocation;
MMIfaceModem3gpp *self;
GVariant *dictionary;
MMModem3gppFacility facility;
guint8 slot;
gchar *control_key;
} HandleDisableFacilityLockContext;
static void
handle_disable_facility_lock_context_free (HandleDisableFacilityLockContext *ctx)
{
g_object_unref (ctx->skeleton);
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_variant_unref (ctx->dictionary);
g_free (ctx->control_key);
g_free (ctx);
}
static void
update_lock_info_ready (MMIfaceModem *modem,
GAsyncResult *res,
HandleDisableFacilityLockContext *ctx)
{
GError *error = NULL;
mm_iface_modem_update_lock_info_finish (modem, res, &error);
if (error) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_disable_facility_lock_context_free (ctx);
return;
}
mm_gdbus_modem3gpp_complete_disable_facility_lock (ctx->skeleton, ctx->invocation);
handle_disable_facility_lock_context_free (ctx);
}
static void
handle_disable_facility_lock_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
HandleDisableFacilityLockContext *ctx)
{
MMModem3gppFacility facilities;
GError *error = NULL;
if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_facility_lock_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_disable_facility_lock_context_free (ctx);
return;
}
/* Update the Enabled Facility Locks property in the DBus interface */
facilities = mm_gdbus_modem3gpp_get_enabled_facility_locks (ctx->skeleton);
facilities &= ~ctx->facility;
mm_gdbus_modem3gpp_set_enabled_facility_locks (ctx->skeleton, facilities);
/* Recheck lock status after unlock code has been sent */
mm_iface_modem_update_lock_info (MM_IFACE_MODEM (self),
MM_MODEM_LOCK_UNKNOWN, /* ask */
(GAsyncReadyCallback)update_lock_info_ready,
ctx);
}
static void
disable_facility_lock_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleDisableFacilityLockContext *ctx)
{
GError *error = NULL;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_disable_facility_lock_context_free (ctx);
return;
}
/* If disable facility locks is not implemented, report an error */
if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_facility_lock ||
!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_facility_lock_finish) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Cannot disable facility locks: operation not supported");
handle_disable_facility_lock_context_free (ctx);
return;
}
/* Parse properties dictionary */
if (!g_variant_is_of_type (ctx->dictionary, G_VARIANT_TYPE ("(us)"))) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Cannot disable facility locks: invalid parameters");
handle_disable_facility_lock_context_free (ctx);
return;
}
/* Only modems with single slot or single configuration for all slots are supported */
ctx->slot = 1;
g_variant_get (ctx->dictionary, "(us)", &ctx->facility, &ctx->control_key);
/* Only four facility locks can be disabled:
* - MM_MODEM_3GPP_FACILITY_NET_PERS (network personalization)
* - MM_MODEM_3GPP_FACILITY_NET_SUB_PERS (network subset personalization)
* - MM_MODEM_3GPP_FACILITY_PROVIDER_PERS (service provider personalization)
* - MM_MODEM_3GPP_FACILITY_CORP_PERS (corporate personalization)
*/
if (ctx->facility != (ctx->facility & (MM_MODEM_3GPP_FACILITY_NET_PERS |
MM_MODEM_3GPP_FACILITY_NET_SUB_PERS |
MM_MODEM_3GPP_FACILITY_PROVIDER_PERS |
MM_MODEM_3GPP_FACILITY_CORP_PERS))) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Invalid type of facility lock to disable or empty key");
handle_disable_facility_lock_context_free (ctx);
return;
}
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_facility_lock (
MM_IFACE_MODEM_3GPP (self),
ctx->facility,
ctx->slot,
ctx->control_key,
(GAsyncReadyCallback)handle_disable_facility_lock_ready,
ctx);
}
static gboolean
handle_disable_facility_lock (MmGdbusModem3gpp *skeleton,
GDBusMethodInvocation *invocation,
GVariant *dictionary,
MMIfaceModem3gpp *self)
{
HandleDisableFacilityLockContext *ctx;
ctx = g_new0 (HandleDisableFacilityLockContext, 1);
ctx->skeleton = g_object_ref (skeleton);
ctx->invocation = g_object_ref (invocation);
ctx->self = g_object_ref (self);
ctx->dictionary = g_variant_ref (dictionary);
mm_base_modem_authorize (MM_BASE_MODEM (self),
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
(GAsyncReadyCallback)disable_facility_lock_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
gboolean
mm_iface_modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->run_registration_checks_finish != NULL);
return MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->run_registration_checks_finish (self, res, error);
}
void
mm_iface_modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
gboolean is_cs_supported = FALSE;
gboolean is_ps_supported = FALSE;
gboolean is_eps_supported = FALSE;
gboolean is_5gs_supported = FALSE;
g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->run_registration_checks != NULL);
g_object_get (self,
MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, &is_cs_supported,
MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, &is_ps_supported,
MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, &is_eps_supported,
MM_IFACE_MODEM_3GPP_5GS_NETWORK_SUPPORTED, &is_5gs_supported,
NULL);
mm_obj_dbg (self, "running registration checks (CS: '%s', PS: '%s', EPS: '%s', 5GS: '%s')",
is_cs_supported ? "yes" : "no",
is_ps_supported ? "yes" : "no",
is_eps_supported ? "yes" : "no",
is_5gs_supported ? "yes" : "no");
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->run_registration_checks (self,
is_cs_supported,
is_ps_supported,
is_eps_supported,
is_5gs_supported,
callback,
user_data);
}
/*****************************************************************************/
typedef struct {
MmGdbusModem3gpp *skeleton;
gboolean operator_code_loaded;
gboolean operator_name_loaded;
} ReloadCurrentRegistrationInfoContext;
static void
reload_current_registration_info_context_free (ReloadCurrentRegistrationInfoContext *ctx)
{
if (ctx->skeleton)
g_object_unref (ctx->skeleton);
g_slice_free (ReloadCurrentRegistrationInfoContext, ctx);
}
gboolean
mm_iface_modem_3gpp_reload_current_registration_info_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void reload_current_registration_info_context_step (GTask *task);
static void
load_operator_name_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
ReloadCurrentRegistrationInfoContext *ctx;
GError *error = NULL;
gchar *str;
ctx = g_task_get_task_data (task);
str = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_name_finish (self, res, &error);
if (error) {
mm_obj_warn (self, "couldn't load operator name: %s", error->message);
g_error_free (error);
}
if (ctx->skeleton)
mm_gdbus_modem3gpp_set_operator_name (ctx->skeleton, str);
g_free (str);
ctx->operator_name_loaded = TRUE;
reload_current_registration_info_context_step (task);
}
static void
load_operator_code_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
ReloadCurrentRegistrationInfoContext *ctx;
GError *error = NULL;
gchar *str;
ctx = g_task_get_task_data (task);
str = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_code_finish (self, res, &error);
if (error) {
mm_obj_warn (self, "couldn't load operator code: %s", error->message);
} else if (!mm_3gpp_parse_operator_id (str, NULL, NULL, NULL, &error)) {
mm_obj_dbg (self, "unexpected operator code string '%s': %s", str, error->message);
g_clear_pointer (&str, g_free);
}
g_clear_error (&error);
if (ctx->skeleton)
mm_gdbus_modem3gpp_set_operator_code (ctx->skeleton, str);
/* If we also implement the location interface, update the 3GPP location */
if (str && MM_IS_IFACE_MODEM_LOCATION (self))
mm_iface_modem_location_3gpp_update_operator_code (MM_IFACE_MODEM_LOCATION (self), str);
g_free (str);
ctx->operator_code_loaded = TRUE;
reload_current_registration_info_context_step (task);
}
static void
reload_current_registration_info_context_step (GTask *task)
{
MMIfaceModem3gpp *self;
ReloadCurrentRegistrationInfoContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
if (!ctx->operator_code_loaded) {
/* Launch operator code update */
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_code (
self,
(GAsyncReadyCallback)load_operator_code_ready,
task);
return;
}
if (!ctx->operator_name_loaded) {
/* Launch operator name update */
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_name (
self,
(GAsyncReadyCallback)load_operator_name_ready,
task);
return;
}
/* If all are loaded, all done */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
void
mm_iface_modem_3gpp_reload_current_registration_info (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
ReloadCurrentRegistrationInfoContext *ctx;
GTask *task;
ctx = g_slice_new0 (ReloadCurrentRegistrationInfoContext);
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)reload_current_registration_info_context_free);
g_object_get (self,
MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &ctx->skeleton,
NULL);
if (!ctx->skeleton) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't get interface skeleton");
g_object_unref (task);
return;
}
ctx->operator_code_loaded = !(MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_code &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_code_finish);
if (ctx->operator_code_loaded) {
mm_gdbus_modem3gpp_set_operator_code (ctx->skeleton, NULL);
if (MM_IS_IFACE_MODEM_LOCATION (self))
mm_iface_modem_location_3gpp_update_operator_code (MM_IFACE_MODEM_LOCATION (self), NULL);
}
ctx->operator_name_loaded = !(MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_name &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_name_finish);
if (ctx->operator_name_loaded)
mm_gdbus_modem3gpp_set_operator_name (ctx->skeleton, NULL);
reload_current_registration_info_context_step (task);
}
void
mm_iface_modem_3gpp_clear_current_operator (MMIfaceModem3gpp *self)
{
MmGdbusModem3gpp *skeleton = NULL;
g_object_get (self,
MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton,
NULL);
if (!skeleton)
return;
mm_gdbus_modem3gpp_set_operator_code (skeleton, NULL);
mm_gdbus_modem3gpp_set_operator_name (skeleton, NULL);
if (MM_IS_IFACE_MODEM_LOCATION (self))
mm_iface_modem_location_3gpp_update_operator_code (MM_IFACE_MODEM_LOCATION (self), NULL);
}
/*****************************************************************************/
void
mm_iface_modem_3gpp_update_access_technologies (MMIfaceModem3gpp *self,
MMModemAccessTechnology access_tech)
{
Private *priv;
MMModem3gppRegistrationState state;
priv = get_private (self);
g_object_get (self,
MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &state,
NULL);
/* Even if registration state didn't change, report access technology,
* but only if something valid to report */
if (REG_STATE_IS_REGISTERED (state) || priv->reloading_registration_info) {
if (access_tech != MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN)
mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self),
access_tech,
MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK);
} else
mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self),
MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN,
MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK);
}
void
mm_iface_modem_3gpp_update_location (MMIfaceModem3gpp *self,
gulong location_area_code,
gulong tracking_area_code,
gulong cell_id)
{
Private *priv;
MMModem3gppRegistrationState state;
priv = get_private (self);
if (!MM_IS_IFACE_MODEM_LOCATION (self))
return;
g_object_get (self,
MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &state,
NULL);
/* Even if registration state didn't change, report access technology or
* location updates, but only if something valid to report. For the case
* where we're registering (loading current registration info after a state
* change to registered), we also allow LAC/CID updates. */
if (REG_STATE_IS_REGISTERED (state) || priv->reloading_registration_info) {
if (location_area_code || tracking_area_code || cell_id)
mm_iface_modem_location_3gpp_update_lac_tac_ci (MM_IFACE_MODEM_LOCATION (self),
location_area_code,
tracking_area_code,
cell_id);
} else
mm_iface_modem_location_3gpp_clear (MM_IFACE_MODEM_LOCATION (self));
}
/*****************************************************************************/
static void
update_registration_reload_current_registration_info_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
gpointer user_data)
{
Private *priv;
MMModem3gppRegistrationState new_state;
priv = get_private (self);
new_state = GPOINTER_TO_UINT (user_data);
mm_obj_info (self, "3GPP registration state changed (registering -> %s)",
mm_modem_3gpp_registration_state_get_string (new_state));
mm_obj_dbg (self, "consolidated registration state: cs '%s', ps '%s', eps '%s', 5gs '%s' --> '%s'",
mm_modem_3gpp_registration_state_get_string (priv->state_cs),
mm_modem_3gpp_registration_state_get_string (priv->state_ps),
mm_modem_3gpp_registration_state_get_string (priv->state_eps),
mm_modem_3gpp_registration_state_get_string (priv->state_5gs),
mm_modem_3gpp_registration_state_get_string (new_state));
/* The property in the interface is bound to the property
* in the skeleton, so just updating here is enough */
g_object_set (self,
MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, new_state,
NULL);
mm_iface_modem_update_subsystem_state (MM_IFACE_MODEM (self),
SUBSYSTEM_3GPP,
MM_MODEM_STATE_REGISTERED,
MM_MODEM_STATE_CHANGE_REASON_UNKNOWN);
priv->reloading_registration_info = FALSE;
}
static void
update_non_registered_state (MMIfaceModem3gpp *self,
MMModem3gppRegistrationState old_state,
MMModem3gppRegistrationState new_state)
{
/* Not registered neither in home nor roaming network */
mm_iface_modem_3gpp_clear_current_operator (self);
/* The property in the interface is bound to the property
* in the skeleton, so just updating here is enough */
g_object_set (self,
MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, new_state,
NULL);
mm_iface_modem_update_subsystem_state (
MM_IFACE_MODEM (self),
SUBSYSTEM_3GPP,
(new_state == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING ?
MM_MODEM_STATE_SEARCHING :
MM_MODEM_STATE_ENABLED),
MM_MODEM_STATE_CHANGE_REASON_UNKNOWN);
}
static void
update_registration_state (MMIfaceModem3gpp *self,
MMModem3gppRegistrationState new_state,
gboolean deferrable)
{
Private *priv;
MMModem3gppRegistrationState old_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
priv = get_private (self);
g_object_get (self,
MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &old_state,
NULL);
/* Only set new state if different */
if (new_state == old_state)
return;
if (REG_STATE_IS_REGISTERED (new_state)) {
MMModemState modem_state;
/* If already reloading registration info, skip it */
if (priv->reloading_registration_info)
return;
/* If the modem isn't already enabled, this registration state update
* could be due to a previously scheduled initial registration check
* when the modem was being enabled. We need to ignore it as otherwise
* it may cause an incorrect transition of the registration state and
* modem state when the modem is being disabled or still going through
* enable steps */
modem_state = MM_MODEM_STATE_UNKNOWN;
g_object_get (self,
MM_IFACE_MODEM_STATE, &modem_state,
NULL);
if (modem_state < MM_MODEM_STATE_ENABLED) {
mm_obj_dbg (self, "3GPP registration state change ignored as modem isn't enabled");
return;
}
mm_obj_info (self, "3GPP registration state changed (%s -> registering)",
mm_modem_3gpp_registration_state_get_string (old_state));
/* Reload current registration info. ONLY update the state to REGISTERED
* after having loaded operator code/name/subscription state */
priv->reloading_registration_info = TRUE;
mm_iface_modem_3gpp_reload_current_registration_info (
self,
(GAsyncReadyCallback)update_registration_reload_current_registration_info_ready,
GUINT_TO_POINTER (new_state));
return;
}
mm_obj_info (self, "3GPP registration state changed (%s -> %s)",
mm_modem_3gpp_registration_state_get_string (old_state),
mm_modem_3gpp_registration_state_get_string (new_state));
mm_obj_dbg (self, "consolidated registration state: cs '%s', ps '%s', eps '%s', 5gs '%s' --> '%s'",
mm_modem_3gpp_registration_state_get_string (priv->state_cs),
mm_modem_3gpp_registration_state_get_string (priv->state_ps),
mm_modem_3gpp_registration_state_get_string (priv->state_eps),
mm_modem_3gpp_registration_state_get_string (priv->state_5gs),
mm_modem_3gpp_registration_state_get_string (new_state));
update_non_registered_state (self, old_state, new_state);
}
void
mm_iface_modem_3gpp_update_cs_registration_state (MMIfaceModem3gpp *self,
MMModem3gppRegistrationState state)
{
Private *priv;
gboolean supported = FALSE;
g_object_get (self,
MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, &supported,
NULL);
if (!supported)
return;
priv = get_private (self);
priv->state_cs = state;
update_registration_state (self, get_consolidated_reg_state (self), TRUE);
}
void
mm_iface_modem_3gpp_update_ps_registration_state (MMIfaceModem3gpp *self,
MMModem3gppRegistrationState state)
{
Private *priv;
gboolean supported = FALSE;
g_object_get (self,
MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, &supported,
NULL);
if (!supported)
return;
priv = get_private (self);
priv->state_ps = state;
update_registration_state (self, get_consolidated_reg_state (self), TRUE);
}
void
mm_iface_modem_3gpp_update_eps_registration_state (MMIfaceModem3gpp *self,
MMModem3gppRegistrationState state)
{
Private *priv;
gboolean supported = FALSE;
g_object_get (self,
MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, &supported,
NULL);
if (!supported)
return;
priv = get_private (self);
priv->state_eps = state;
update_registration_state (self, get_consolidated_reg_state (self), TRUE);
}
void
mm_iface_modem_3gpp_update_5gs_registration_state (MMIfaceModem3gpp *self,
MMModem3gppRegistrationState state)
{
Private *priv;
gboolean supported = FALSE;
g_object_get (self,
MM_IFACE_MODEM_3GPP_5GS_NETWORK_SUPPORTED, &supported,
NULL);
if (!supported)
return;
priv = get_private (self);
priv->state_5gs = state;
update_registration_state (self, get_consolidated_reg_state (self), TRUE);
}
/*****************************************************************************/
/* Periodic registration checks */
#define REGISTRATION_CHECK_TIMEOUT_SEC 30
static void
periodic_registration_checks_ready (MMIfaceModem3gpp *self,
GAsyncResult *res)
{
Private *priv;
GError *error = NULL;
priv = get_private (self);
mm_iface_modem_3gpp_run_registration_checks_finish (self, res, &error);
if (error) {
mm_obj_dbg (self, "couldn't refresh 3GPP registration status: %s", error->message);
g_error_free (error);
}
priv->check_running = FALSE;
}
static gboolean
periodic_registration_check (MMIfaceModem3gpp *self)
{
Private *priv;
priv = get_private (self);
/* Only launch a new one if not one running already */
if (!priv->check_running) {
priv->check_running = TRUE;
mm_iface_modem_3gpp_run_registration_checks (
self,
(GAsyncReadyCallback)periodic_registration_checks_ready,
NULL);
}
return G_SOURCE_CONTINUE;
}
static void
periodic_registration_check_disable (MMIfaceModem3gpp *self)
{
Private *priv;
priv = get_private (self);
/* Do nothing if already disabled */
if (!priv->check_timeout_source)
return;
g_source_remove (priv->check_timeout_source);
priv->check_timeout_source = 0;
mm_obj_dbg (self, "periodic 3GPP registration checks disabled");
}
static void
periodic_registration_check_enable (MMIfaceModem3gpp *self)
{
Private *priv;
priv = get_private (self);
/* Do nothing if already enabled */
if (priv->check_timeout_source)
return;
/* Create context and keep it as object data */
mm_obj_dbg (self, "periodic 3GPP registration checks enabled");
priv->check_timeout_source = g_timeout_add_seconds (REGISTRATION_CHECK_TIMEOUT_SEC,
(GSourceFunc)periodic_registration_check,
self);
}
/*****************************************************************************/
void
mm_iface_modem_3gpp_update_pco_list (MMIfaceModem3gpp *self,
const GList *pco_list)
{
MmGdbusModem3gpp *skeleton = NULL;
GVariantBuilder builder;
GVariant *variant;
const GList *iter;
g_object_get (self,
MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton,
NULL);
if (!skeleton)
return;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ubay)"));
for (iter = pco_list; iter; iter = g_list_next (iter)) {
g_variant_builder_add_value (&builder,
mm_pco_to_variant (MM_PCO (iter->data)));
}
variant = g_variant_ref_sink (g_variant_builder_end (&builder));
mm_gdbus_modem3gpp_set_pco (skeleton, variant);
g_variant_unref (variant);
g_object_unref (skeleton);
}
/*****************************************************************************/
void
mm_iface_modem_3gpp_update_initial_eps_bearer (MMIfaceModem3gpp *self,
MMBearerProperties *properties)
{
MmGdbusModem3gpp *skeleton = NULL;
MMBaseBearer *old_bearer = NULL;
g_object_get (self,
MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton,
MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER, &old_bearer,
NULL);
g_assert (skeleton);
/* skip update? */
if ((!old_bearer && !properties) ||
(old_bearer && properties &&
mm_bearer_properties_cmp (properties,
mm_base_bearer_peek_config (MM_BASE_BEARER (old_bearer)),
MM_BEARER_PROPERTIES_CMP_FLAGS_EPS)))
goto out;
if (properties) {
MMBaseBearer *new_bearer;
mm_obj_dbg (self, "updating initial EPS bearer...");
g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->create_initial_eps_bearer);
new_bearer = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->create_initial_eps_bearer (self, properties);
g_object_set (self,
MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER, new_bearer,
NULL);
mm_gdbus_modem3gpp_set_initial_eps_bearer (skeleton, mm_base_bearer_get_path (new_bearer));
g_object_unref (new_bearer);
} else {
mm_obj_dbg (self, "clearing initial EPS bearer...");
g_object_set (self,
MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER, NULL,
NULL);
mm_gdbus_modem3gpp_set_initial_eps_bearer (skeleton, NULL);
}
out:
g_clear_object (&old_bearer);
g_object_unref (skeleton);
}
static void
reload_initial_eps_bearer_ready (MMIfaceModem3gpp *self,
GAsyncResult *res)
{
g_autoptr(MMBearerProperties) properties = NULL;
g_autoptr(GError) error = NULL;
properties = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish (self, res, &error);
if (!properties) {
mm_obj_dbg (self, "couldn't load initial default bearer properties: %s", error->message);
return;
}
mm_iface_modem_3gpp_update_initial_eps_bearer (self, properties);
}
void
mm_iface_modem_3gpp_reload_initial_eps_bearer (MMIfaceModem3gpp *self)
{
gboolean eps_supported = FALSE;
g_object_get (self,
MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, &eps_supported,
NULL);
if (eps_supported &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer (
self,
(GAsyncReadyCallback)reload_initial_eps_bearer_ready,
NULL);
}
}
/*****************************************************************************/
typedef struct _DisablingContext DisablingContext;
static void interface_disabling_step (GTask *task);
typedef enum {
DISABLING_STEP_FIRST,
DISABLING_STEP_INITIAL_EPS_BEARER,
DISABLING_STEP_PERIODIC_REGISTRATION_CHECKS,
DISABLING_STEP_DISABLE_UNSOLICITED_REGISTRATION_EVENTS,
DISABLING_STEP_CLEANUP_UNSOLICITED_REGISTRATION_EVENTS,
DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS,
DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS,
DISABLING_STEP_REGISTRATION_STATE,
DISABLING_STEP_LAST
} DisablingStep;
struct _DisablingContext {
DisablingStep step;
MmGdbusModem *skeleton;
};
static void
disabling_context_free (DisablingContext *ctx)
{
if (ctx->skeleton)
g_object_unref (ctx->skeleton);
g_free (ctx);
}
gboolean
mm_iface_modem_3gpp_disable_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
#undef VOID_REPLY_READY_FN
#define VOID_REPLY_READY_FN(NAME,DISPLAY) \
static void \
NAME##_ready (MMIfaceModem3gpp *self, \
GAsyncResult *res, \
GTask *task) \
{ \
DisablingContext *ctx; \
GError *error = NULL; \
\
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->NAME##_finish (self, res, &error); \
if (error) { \
mm_obj_dbg (self, "couldn't %s: %s", DISPLAY, error->message); \
g_error_free (error); \
} \
\
/* Go on to next step */ \
ctx = g_task_get_task_data (task); \
ctx->step++; \
interface_disabling_step (task); \
}
VOID_REPLY_READY_FN (cleanup_unsolicited_events,
"cleanup unsolicited events")
VOID_REPLY_READY_FN (disable_unsolicited_events,
"disable unsolicited events")
VOID_REPLY_READY_FN (cleanup_unsolicited_registration_events,
"cleanup unsolicited registration events")
VOID_REPLY_READY_FN (disable_unsolicited_registration_events,
"disable unsolicited registration events")
static void
interface_disabling_step (GTask *task)
{
MMIfaceModem3gpp *self;
DisablingContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
switch (ctx->step) {
case DISABLING_STEP_FIRST:
ctx->step++;
/* fall through */
case DISABLING_STEP_INITIAL_EPS_BEARER:
mm_iface_modem_3gpp_update_initial_eps_bearer (self, NULL);
ctx->step++;
/* fall through */
case DISABLING_STEP_PERIODIC_REGISTRATION_CHECKS:
/* Disable periodic registration checks, if they were set */
periodic_registration_check_disable (self);
ctx->step++;
/* fall through */
case DISABLING_STEP_DISABLE_UNSOLICITED_REGISTRATION_EVENTS: {
gboolean cs_supported = FALSE;
gboolean ps_supported = FALSE;
gboolean eps_supported = FALSE;
g_object_get (self,
MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, &cs_supported,
MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, &ps_supported,
MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, &eps_supported,
NULL);
if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_registration_events &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_registration_events_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_registration_events (
self,
cs_supported,
ps_supported,
eps_supported,
(GAsyncReadyCallback)disable_unsolicited_registration_events_ready,
task);
return;
}
ctx->step++;
} /* fall through */
case DISABLING_STEP_CLEANUP_UNSOLICITED_REGISTRATION_EVENTS:
if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_registration_events &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_registration_events_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_registration_events (
self,
(GAsyncReadyCallback)cleanup_unsolicited_registration_events_ready,
task);
return;
}
ctx->step++;
/* fall through */
case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS:
if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_events &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_events (
self,
(GAsyncReadyCallback)cleanup_unsolicited_events_ready,
task);
return;
}
ctx->step++;
/* fall through */
case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS:
if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_events &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_events_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_events (
self,
(GAsyncReadyCallback)disable_unsolicited_events_ready,
task);
return;
}
ctx->step++;
/* fall through */
case DISABLING_STEP_REGISTRATION_STATE:
update_registration_state (self, MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, FALSE);
mm_iface_modem_3gpp_update_access_technologies (self, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
mm_iface_modem_3gpp_update_location (self, 0, 0, 0);
ctx->step++;
/* fall through */
case DISABLING_STEP_LAST:
/* We are done without errors! */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
default:
g_assert_not_reached ();
}
g_assert_not_reached ();
}
void
mm_iface_modem_3gpp_disable (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
DisablingContext *ctx;
GTask *task;
ctx = g_new0 (DisablingContext, 1);
ctx->step = DISABLING_STEP_FIRST;
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free);
g_object_get (self,
MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &ctx->skeleton,
NULL);
if (!ctx->skeleton) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't get interface skeleton");
g_object_unref (task);
return;
}
interface_disabling_step (task);
}
/*****************************************************************************/
typedef struct _EnablingContext EnablingContext;
static void interface_enabling_step (GTask *task);
typedef enum {
ENABLING_STEP_FIRST,
ENABLING_STEP_SETUP_UNSOLICITED_EVENTS,
ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS,
ENABLING_STEP_SETUP_UNSOLICITED_REGISTRATION_EVENTS,
ENABLING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS,
ENABLING_STEP_INITIAL_EPS_BEARER,
ENABLING_STEP_LAST
} EnablingStep;
struct _EnablingContext {
EnablingStep step;
MmGdbusModem3gpp *skeleton;
};
static void
enabling_context_free (EnablingContext *ctx)
{
if (ctx->skeleton)
g_object_unref (ctx->skeleton);
g_free (ctx);
}
gboolean
mm_iface_modem_3gpp_enable_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
EnablingContext *ctx;
GError *error = NULL;
ctx = g_task_get_task_data (task);
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error);
if (error) {
/* This error shouldn't be treated as critical */
mm_obj_dbg (self, "setting up unsolicited events failed: %s", error->message);
g_error_free (error);
/* If we get an error setting up unsolicited events, don't even bother trying to
* enable them. */
ctx->step = ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS + 1;
interface_enabling_step (task);
return;
}
/* Go on to next step */
ctx->step++;
interface_enabling_step (task);
}
static void
enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
EnablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error);
if (error) {
/* This error shouldn't be treated as critical */
mm_obj_dbg (self, "enabling unsolicited events failed: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
ctx = g_task_get_task_data (task);
ctx->step++;
interface_enabling_step (task);
}
static void
setup_unsolicited_registration_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
EnablingContext *ctx;
GError *error = NULL;
ctx = g_task_get_task_data (task);
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_registration_events_finish (self, res, &error);
if (error) {
/* This error shouldn't be treated as critical */
mm_obj_dbg (self, "setting up unsolicited registration events failed: %s", error->message);
g_error_free (error);
/* If we get an error setting up unsolicited events, don't even bother trying to
* enable them. */
ctx->step = ENABLING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS + 1;
interface_enabling_step (task);
/* If error, setup periodic registration checks */
periodic_registration_check_enable (self);
return;
}
/* Go on to next step */
ctx->step++;
interface_enabling_step (task);
}
static void
enable_unsolicited_registration_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
EnablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_registration_events_finish (self, res, &error);
if (error) {
/* This error shouldn't be treated as critical */
mm_obj_dbg (self, "enabling unsolicited registration events failed: %s", error->message);
g_error_free (error);
/* If error, setup periodic registration checks */
periodic_registration_check_enable (self);
}
/* Go on to next step */
ctx = g_task_get_task_data (task);
ctx->step++;
interface_enabling_step (task);
}
static void
load_initial_eps_bearer_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
MMBearerProperties *properties;
EnablingContext *ctx;
GError *error = NULL;
ctx = g_task_get_task_data (task);
properties = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish (self, res, &error);
if (!properties) {
mm_obj_dbg (self, "couldn't load initial default bearer properties: %s", error->message);
g_error_free (error);
goto out;
}
mm_iface_modem_3gpp_update_initial_eps_bearer (self, properties);
g_object_unref (properties);
out:
/* Go on to next step */
ctx->step++;
interface_enabling_step (task);
}
static void
interface_enabling_step (GTask *task)
{
MMIfaceModem3gpp *self;
EnablingContext *ctx;
/* Don't run new steps if we're cancelled */
if (g_task_return_error_if_cancelled (task)) {
g_object_unref (task);
return;
}
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
switch (ctx->step) {
case ENABLING_STEP_FIRST:
ctx->step++;
/* fall through */
case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS:
if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_events &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_events_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_events (
self,
(GAsyncReadyCallback)setup_unsolicited_events_ready,
task);
return;
}
ctx->step++;
/* fall through */
case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS:
if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_events &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_events_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_events (
self,
(GAsyncReadyCallback)enable_unsolicited_events_ready,
task);
return;
}
ctx->step++;
/* fall through */
case ENABLING_STEP_SETUP_UNSOLICITED_REGISTRATION_EVENTS:
if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_registration_events &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_registration_events_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_registration_events (
self,
(GAsyncReadyCallback)setup_unsolicited_registration_events_ready,
task);
return;
}
ctx->step++;
/* fall through */
case ENABLING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS: {
gboolean cs_supported = FALSE;
gboolean ps_supported = FALSE;
gboolean eps_supported = FALSE;
g_object_get (self,
MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, &cs_supported,
MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, &ps_supported,
MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, &eps_supported,
NULL);
if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_registration_events &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_registration_events_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_registration_events (
self,
cs_supported,
ps_supported,
eps_supported,
(GAsyncReadyCallback)enable_unsolicited_registration_events_ready,
task);
return;
}
ctx->step++;
} /* fall through */
case ENABLING_STEP_INITIAL_EPS_BEARER: {
gboolean eps_supported = FALSE;
g_object_get (self,
MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, &eps_supported,
NULL);
if (eps_supported &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer (
self,
(GAsyncReadyCallback)load_initial_eps_bearer_ready,
task);
return;
}
ctx->step++;
} /* fall through */
case ENABLING_STEP_LAST:
/* We are done without errors! */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
default:
break;
}
g_assert_not_reached ();
}
void
mm_iface_modem_3gpp_enable (MMIfaceModem3gpp *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
EnablingContext *ctx;
GTask *task;
ctx = g_new0 (EnablingContext, 1);
ctx->step = ENABLING_STEP_FIRST;
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free);
g_object_get (self,
MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &ctx->skeleton,
NULL);
if (!ctx->skeleton) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't get interface skeleton");
g_object_unref (task);
return;
}
interface_enabling_step (task);
}
/*****************************************************************************/
#if defined WITH_SYSTEMD_SUSPEND_RESUME
typedef struct _SyncingContext SyncingContext;
static void interface_syncing_step (GTask *task);
typedef enum {
SYNCING_STEP_FIRST,
SYNCING_STEP_REFRESH_3GPP_REGISTRATION,
SYNCING_STEP_REFRESH_EPS_BEARER,
SYNCING_STEP_LAST
} SyncingStep;
struct _SyncingContext {
SyncingStep step;
};
gboolean
mm_iface_modem_3gpp_sync_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
sync_eps_bearer_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
SyncingContext *ctx;
g_autoptr(MMBearerProperties) properties = NULL;
g_autoptr(GError) error = NULL;
ctx = g_task_get_task_data (task);
properties = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish (self, res, &error);
if (!properties)
mm_obj_dbg (self, "couldn't refresh initial EPS bearer status: %s", error->message);
else
mm_iface_modem_3gpp_update_initial_eps_bearer (self, properties);
/* Go on to next step */
ctx->step++;
interface_syncing_step (task);
}
static void
sync_eps_bearer (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
GTask *task)
{
SyncingContext *ctx;
gboolean eps_supported = FALSE;
g_object_get (self,
MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, &eps_supported,
NULL);
/* Refresh EPS bearer if supported */
if (eps_supported &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer (
self,
callback,
task);
return;
}
/* If EPS is unsupported, just go to the next step */
ctx = g_task_get_task_data (task);
ctx->step++;
interface_syncing_step (task);
}
static void
sync_registration_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
SyncingContext *ctx;
g_autoptr (GError) error = NULL;
ctx = g_task_get_task_data (task);
if (!mm_iface_modem_3gpp_run_registration_checks_finish (self, res, &error))
mm_obj_dbg (self, "couldn't synchronize 3GPP registration: %s", error->message);
/* Go on to next step */
ctx->step++;
interface_syncing_step(task);
}
static void
interface_syncing_step (GTask *task)
{
MMIfaceModem3gpp *self;
SyncingContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
switch (ctx->step) {
case SYNCING_STEP_FIRST:
ctx->step++;
/* fall through */
case SYNCING_STEP_REFRESH_3GPP_REGISTRATION:
/*
* Refresh registration info to verify that the modem is still registered.
* Wait until registration checks are complete before going to the next step.
*/
mm_iface_modem_3gpp_run_registration_checks (
self,
(GAsyncReadyCallback)sync_registration_ready,
task);
return;
case SYNCING_STEP_REFRESH_EPS_BEARER:
/*
* Refresh EPS bearer and wait until complete.
* We want to make sure that the modem is fully enabled again
* when we refresh the mobile data connection bearers.
*/
sync_eps_bearer (
self,
(GAsyncReadyCallback)sync_eps_bearer_ready,
task);
return;
case SYNCING_STEP_LAST:
/* We are done without errors! */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
default:
break;
}
g_assert_not_reached ();
}
void
mm_iface_modem_3gpp_sync (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
SyncingContext *ctx;
GTask *task;
/* Create SyncingContext */
ctx = g_new0 (SyncingContext, 1);
ctx->step = SYNCING_STEP_FIRST;
/* Create sync steps task and execute it */
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)g_free);
interface_syncing_step (task);
}
#endif
/*****************************************************************************/
typedef struct _InitializationContext InitializationContext;
static void interface_initialization_step (GTask *task);
typedef enum {
INITIALIZATION_STEP_FIRST,
INITIALIZATION_STEP_ENABLED_FACILITY_LOCKS,
INITIALIZATION_STEP_TEST_LOCKED,
INITIALIZATION_STEP_IMEI,
INITIALIZATION_STEP_EPS_UE_MODE_OPERATION,
INITIALIZATION_STEP_EPS_INITIAL_BEARER_SETTINGS,
INITIALIZATION_STEP_CONNECT_SIGNALS,
INITIALIZATION_STEP_LAST
} InitializationStep;
struct _InitializationContext {
MmGdbusModem3gpp *skeleton;
InitializationStep step;
};
static void
initialization_context_free (InitializationContext *ctx)
{
g_object_unref (ctx->skeleton);
g_free (ctx);
}
static void
sim_pin_lock_enabled_cb (MMBaseSim *self,
gboolean enabled,
MmGdbusModem3gpp *skeleton)
{
MMModem3gppFacility facilities;
facilities = mm_gdbus_modem3gpp_get_enabled_facility_locks (skeleton);
if (enabled)
facilities |= MM_MODEM_3GPP_FACILITY_SIM;
else
facilities &= ~MM_MODEM_3GPP_FACILITY_SIM;
mm_gdbus_modem3gpp_set_enabled_facility_locks (skeleton, facilities);
}
static void
load_initial_eps_bearer_settings_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
InitializationContext *ctx;
MMBearerProperties *config;
GError *error = NULL;
ctx = g_task_get_task_data (task);
config = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings_finish (self, res, &error);
if (!config) {
mm_obj_warn (self, "couldn't load initial EPS bearer settings: %s", error->message);
g_error_free (error);
} else {
GVariant *dictionary;
dictionary = mm_bearer_properties_get_dictionary (config);
mm_gdbus_modem3gpp_set_initial_eps_bearer_settings (ctx->skeleton, dictionary);
g_object_unref (config);
if (dictionary)
g_variant_unref (dictionary);
}
/* Go on to next step */
ctx->step++;
interface_initialization_step (task);
}
static void
load_eps_ue_mode_operation_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
InitializationContext *ctx;
MMModem3gppEpsUeModeOperation uemode;
GError *error = NULL;
ctx = g_task_get_task_data (task);
uemode = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish (self, res, &error);
mm_gdbus_modem3gpp_set_eps_ue_mode_operation (ctx->skeleton, uemode);
if (error) {
mm_obj_warn (self, "couldn't load UE mode of operation for EPS: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
ctx->step++;
interface_initialization_step (task);
}
static void
load_enabled_facility_locks_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
InitializationContext *ctx;
GError *error = NULL;
MMModem3gppFacility facilities;
ctx = g_task_get_task_data (task);
facilities = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_enabled_facility_locks_finish (self, res, &error);
mm_gdbus_modem3gpp_set_enabled_facility_locks (ctx->skeleton, facilities);
if (error) {
mm_obj_warn (self, "couldn't load facility locks: %s", error->message);
g_error_free (error);
} else {
MMBaseSim *sim = NULL;
/* We loaded the initial list of facility locks; but we do need to update
* the SIM PIN lock status when that changes. We'll connect to the signal
* which notifies about such update. There is no need to ref self as the
* SIM itself is an object which exists as long as self exists. */
g_object_get (self,
MM_IFACE_MODEM_SIM, &sim,
NULL);
g_signal_connect (sim,
MM_BASE_SIM_PIN_LOCK_ENABLED,
G_CALLBACK (sim_pin_lock_enabled_cb),
ctx->skeleton);
g_object_unref (sim);
}
/* Go on to next step */
ctx->step++;
interface_initialization_step (task);
}
static void
load_imei_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
InitializationContext *ctx;
GError *error = NULL;
gchar *imei;
ctx = g_task_get_task_data (task);
imei = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_imei_finish (self, res, &error);
mm_gdbus_modem3gpp_set_imei (ctx->skeleton, imei);
g_free (imei);
if (error) {
mm_obj_warn (self, "couldn't load IMEI: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
ctx->step++;
interface_initialization_step (task);
}
static void
interface_initialization_step (GTask *task)
{
MMIfaceModem3gpp *self;
InitializationContext *ctx;
MMModemState modem_state;
/* Don't run new steps if we're cancelled */
if (g_task_return_error_if_cancelled (task)) {
g_object_unref (task);
return;
}
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
switch (ctx->step) {
case INITIALIZATION_STEP_FIRST:
ctx->step++;
/* fall through */
case INITIALIZATION_STEP_ENABLED_FACILITY_LOCKS:
if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_enabled_facility_locks &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_enabled_facility_locks_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_enabled_facility_locks (
self,
(GAsyncReadyCallback)load_enabled_facility_locks_ready,
task);
return;
}
ctx->step++;
/* fall through */
case INITIALIZATION_STEP_TEST_LOCKED:
modem_state = MM_MODEM_STATE_UNKNOWN;
g_object_get (self,
MM_IFACE_MODEM_STATE, &modem_state,
NULL);
if (modem_state == MM_MODEM_STATE_LOCKED) {
/* Skip some steps and export the interface if modem is locked */
ctx->step = INITIALIZATION_STEP_LAST;
interface_initialization_step (task);
return;
}
ctx->step++;
/* fall through */
case INITIALIZATION_STEP_IMEI:
/* IMEI value is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have it loaded,
* don't try to load it again. */
if (!mm_gdbus_modem3gpp_get_imei (ctx->skeleton) &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_imei &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_imei_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_imei (
self,
(GAsyncReadyCallback)load_imei_ready,
task);
return;
}
ctx->step++;
/* fall through */
case INITIALIZATION_STEP_EPS_UE_MODE_OPERATION:
if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation (
self,
(GAsyncReadyCallback)load_eps_ue_mode_operation_ready,
task);
return;
}
ctx->step++;
/* fall through */
case INITIALIZATION_STEP_EPS_INITIAL_BEARER_SETTINGS:
if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings &&
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings_finish) {
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings (
self,
(GAsyncReadyCallback)load_initial_eps_bearer_settings_ready,
task);
return;
}
ctx->step++;
/* fall through */
case INITIALIZATION_STEP_CONNECT_SIGNALS:
/* We are done without errors! */
/* Handle method invocations */
g_signal_connect (ctx->skeleton,
"handle-register",
G_CALLBACK (handle_register),
self);
g_signal_connect (ctx->skeleton,
"handle-scan",
G_CALLBACK (handle_scan),
self);
g_signal_connect (ctx->skeleton,
"handle-set-eps-ue-mode-operation",
G_CALLBACK (handle_set_eps_ue_mode_operation),
self);
g_signal_connect (ctx->skeleton,
"handle-set-initial-eps-bearer-settings",
G_CALLBACK (handle_set_initial_eps_bearer_settings),
self);
ctx->step++;
/* fall through */
case INITIALIZATION_STEP_LAST:
/* Always connect the signal to unlock modem */
g_signal_connect (ctx->skeleton,
"handle-disable-facility-lock",
G_CALLBACK (handle_disable_facility_lock),
self);
/* Finally, export the new interface */
mm_gdbus_object_skeleton_set_modem3gpp (MM_GDBUS_OBJECT_SKELETON (self),
MM_GDBUS_MODEM3GPP (ctx->skeleton));
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
default:
break;
}
g_assert_not_reached ();
}
gboolean
mm_iface_modem_3gpp_initialize_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
void
mm_iface_modem_3gpp_initialize (MMIfaceModem3gpp *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
MmGdbusModem3gpp *skeleton = NULL;
InitializationContext *ctx;
GTask *task;
/* Did we already create it? */
g_object_get (self,
MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton,
NULL);
if (!skeleton) {
skeleton = mm_gdbus_modem3gpp_skeleton_new ();
/* Set all initial property defaults */
mm_gdbus_modem3gpp_set_imei (skeleton, NULL);
mm_gdbus_modem3gpp_set_operator_code (skeleton, NULL);
mm_gdbus_modem3gpp_set_operator_name (skeleton, NULL);
mm_gdbus_modem3gpp_set_enabled_facility_locks (skeleton, MM_MODEM_3GPP_FACILITY_NONE);
mm_gdbus_modem3gpp_set_subscription_state (skeleton, MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN);
mm_gdbus_modem3gpp_set_pco (skeleton, NULL);
mm_gdbus_modem3gpp_set_initial_eps_bearer (skeleton, NULL);
/* Bind our RegistrationState property */
g_object_bind_property (self, MM_IFACE_MODEM_3GPP_REGISTRATION_STATE,
skeleton, "registration-state",
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
g_object_set (self,
MM_IFACE_MODEM_3GPP_DBUS_SKELETON, skeleton,
NULL);
}
ctx = g_new0 (InitializationContext, 1);
ctx->step = INITIALIZATION_STEP_FIRST;
ctx->skeleton = skeleton;
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
/* Perform async initialization here */
interface_initialization_step (task);
}
void
mm_iface_modem_3gpp_shutdown (MMIfaceModem3gpp *self)
{
/* Unexport DBus interface and remove the skeleton */
mm_gdbus_object_skeleton_set_modem3gpp (MM_GDBUS_OBJECT_SKELETON (self), NULL);
g_object_set (self,
MM_IFACE_MODEM_3GPP_DBUS_SKELETON, NULL,
NULL);
}
/*****************************************************************************/
static void
iface_modem_3gpp_init (gpointer g_iface)
{
static gboolean initialized = FALSE;
if (initialized)
return;
/* Properties */
g_object_interface_install_property
(g_iface,
g_param_spec_object (MM_IFACE_MODEM_3GPP_DBUS_SKELETON,
"3GPP DBus skeleton",
"DBus skeleton for the 3GPP interface",
MM_GDBUS_TYPE_MODEM3GPP_SKELETON,
G_PARAM_READWRITE));
g_object_interface_install_property
(g_iface,
g_param_spec_enum (MM_IFACE_MODEM_3GPP_REGISTRATION_STATE,
"RegistrationState",
"Registration state of the modem",
MM_TYPE_MODEM_3GPP_REGISTRATION_STATE,
MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN,
G_PARAM_READWRITE));
g_object_interface_install_property
(g_iface,
g_param_spec_boolean (MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED,
"CS network supported",
"Whether the modem works in the CS network",
TRUE,
G_PARAM_READWRITE));
g_object_interface_install_property
(g_iface,
g_param_spec_boolean (MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED,
"PS network supported",
"Whether the modem works in the PS network",
TRUE,
G_PARAM_READWRITE));
g_object_interface_install_property
(g_iface,
g_param_spec_boolean (MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED,
"EPS network supported",
"Whether the modem works in the EPS network",
FALSE,
G_PARAM_READWRITE));
g_object_interface_install_property
(g_iface,
g_param_spec_boolean (MM_IFACE_MODEM_3GPP_5GS_NETWORK_SUPPORTED,
"5GS network supported",
"Whether the modem works in the 5GS network",
FALSE,
G_PARAM_READWRITE));
g_object_interface_install_property
(g_iface,
g_param_spec_flags (MM_IFACE_MODEM_3GPP_IGNORED_FACILITY_LOCKS,
"Ignored locks",
"Ignored facility locks",
MM_TYPE_MODEM_3GPP_FACILITY,
MM_MODEM_3GPP_FACILITY_NONE,
G_PARAM_READWRITE));
g_object_interface_install_property
(g_iface,
g_param_spec_object (MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER,
"Initial EPS bearer",
"Initial EPS bearer setup during registration",
MM_TYPE_BASE_BEARER,
G_PARAM_READWRITE));
initialized = TRUE;
}
GType
mm_iface_modem_3gpp_get_type (void)
{
static GType iface_modem_3gpp_type = 0;
if (!G_UNLIKELY (iface_modem_3gpp_type)) {
static const GTypeInfo info = {
sizeof (MMIfaceModem3gpp), /* class_size */
iface_modem_3gpp_init, /* base_init */
NULL, /* base_finalize */
};
iface_modem_3gpp_type = g_type_register_static (G_TYPE_INTERFACE,
"MMIfaceModem3gpp",
&info,
0);
g_type_interface_add_prerequisite (iface_modem_3gpp_type, MM_TYPE_IFACE_MODEM);
}
return iface_modem_3gpp_type;
}