Files
ModemManager/src/mm-iface-modem-3gpp.c
T
Aleksander Morgado 9c3ac2fb60 api,modem-3gpp: new 'InitialEpsBearer' property
This property contains the DBus path of a Bearer object of type
MM_BEARER_TYPE_DEFAULT_ATTACH, which is automatically exposed by the
modem when registered in the LTE network.

Unlike standard bearer objects created by the user, this bearer won't
allow any connection/disconnection request, as its status is bound to
the LTE registration exclusively.

The bearer settings exposed by the object include the APN details that
have been used during the initial packet network attach, which may be
defined by modem settings (e.g. if previously configured in the
firmware which APN to use for the given SIM card operator) or by the
network itself (e.g. if none configured, or if a network override is
required as when roaming).

The bearer object will be created as soon as the LTE attach status
details are known, and only while the modem is enabled. The
implementation allows modems to update the LTE attach status details
during runtime, so the bearer object with the settings may be
recreated during runtime as well.
2018-12-07 16:20:53 +00:00

2436 lines
87 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
* Copyright (C) 2011 - 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 REGISTRATION_CHECK_TIMEOUT_SEC 30
#define SUBSYSTEM_3GPP "3gpp"
#define REGISTRATION_STATE_CONTEXT_TAG "3gpp-registration-state-context-tag"
#define REGISTRATION_CHECK_CONTEXT_TAG "3gpp-registration-check-context-tag"
static GQuark registration_state_context_quark;
static GQuark registration_check_context_quark;
/*****************************************************************************/
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);
}
/*****************************************************************************/
typedef struct {
MMModem3gppRegistrationState cs;
MMModem3gppRegistrationState ps;
MMModem3gppRegistrationState eps;
gboolean manual_registration;
GCancellable *pending_registration_cancellable;
gboolean reloading_registration_info;
} RegistrationStateContext;
static void
registration_state_context_free (RegistrationStateContext *ctx)
{
if (ctx->pending_registration_cancellable) {
g_cancellable_cancel (ctx->pending_registration_cancellable);
g_object_unref (ctx->pending_registration_cancellable);
}
g_slice_free (RegistrationStateContext, ctx);
}
static RegistrationStateContext *
get_registration_state_context (MMIfaceModem3gpp *self)
{
RegistrationStateContext *ctx;
if (G_UNLIKELY (!registration_state_context_quark))
registration_state_context_quark = (g_quark_from_static_string (
REGISTRATION_STATE_CONTEXT_TAG));
ctx = g_object_get_qdata (G_OBJECT (self), registration_state_context_quark);
if (!ctx) {
/* Create context and keep it as object data */
ctx = g_slice_new0 (RegistrationStateContext);
ctx->cs = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
ctx->ps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
ctx->eps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
g_object_set_qdata_full (
G_OBJECT (self),
registration_state_context_quark,
ctx,
(GDestroyNotify)registration_state_context_free);
}
return ctx;
}
static gboolean
reg_state_is_registered (MMModem3gppRegistrationState state)
{
return 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;
}
static MMModem3gppRegistrationState
get_consolidated_reg_state (RegistrationStateContext *ctx)
{
/* 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 (ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
return ctx->cs;
if (ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
return ctx->ps;
if (ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
return ctx->eps;
/* Searching? */
if (ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING ||
ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING ||
ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING)
return MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING;
/* If one state is DENIED and the others are UNKNOWN, use DENIED */
if (ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED &&
ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN &&
ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN)
return ctx->cs;
if (ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN &&
ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED &&
ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN)
return ctx->ps;
if (ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN &&
ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN &&
ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED)
return ctx->eps;
/* Emergency services? */
if (ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY ||
ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY ||
ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY)
return MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY;
/* Support for additional registration states reported when on LTE.
*
* 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 (ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY ||
ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_SMS_ONLY ||
ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME_CSFB_NOT_PREFERRED ||
ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_CSFB_NOT_PREFERRED) {
mm_warn ("3GPP CSFB registration state is consolidated: %s",
mm_modem_3gpp_registration_state_get_string (ctx->cs));
return ctx->cs;
}
/* Idle? */
if (ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE ||
ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE ||
ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE)
return MM_MODEM_3GPP_REGISTRATION_STATE_IDLE;
/* Just unknown at this point */
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) {
RegistrationStateContext *registration_state_context;
/* Clear our cancellable if still around */
registration_state_context = get_registration_state_context (ctx->self);
if (registration_state_context->pending_registration_cancellable == ctx->cancellable)
g_clear_object (&registration_state_context->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 void run_registration_checks_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task);
static gboolean
run_registration_checks_again (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
run_registration_checks_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
RegisterInNetworkContext *ctx;
GError *error = NULL;
RegistrationStateContext *registration_state_context;
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_dbg ("3GPP registration check failed: '%s'", error->message);
register_in_network_context_complete_failed (task, error);
return;
}
registration_state_context = get_registration_state_context (ctx->self);
current_registration_state = get_consolidated_reg_state (registration_state_context);
/* 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_dbg ("Registration denied");
register_in_network_context_complete_failed (
task,
mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_NOT_ALLOWED));
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_dbg ("Modem is 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_dbg ("3GPP registration check timed out");
register_in_network_context_complete_failed (
task,
mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT));
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_dbg ("Modem not yet registered in a 3GPP network... will recheck soon");
g_timeout_add_seconds (3, (GSourceFunc)run_registration_checks_again, task);
}
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 */
mm_iface_modem_3gpp_run_registration_checks (
self,
(GAsyncReadyCallback)run_registration_checks_ready,
task);
}
void
mm_iface_modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
const gchar *operator_id,
guint max_registration_time,
GAsyncReadyCallback callback,
gpointer user_data)
{
RegisterInNetworkContext *ctx;
const gchar *current_operator_code;
RegistrationStateContext *registration_state_context;
GError *error = NULL;
GTask *task;
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, &error)) {
g_assert (error != NULL);
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Get registration state context */
registration_state_context = get_registration_state_context (self);
/* (Try to) cancel previous registration request */
if (registration_state_context->pending_registration_cancellable) {
g_cancellable_cancel (registration_state_context->pending_registration_cancellable);
g_clear_object (&registration_state_context->pending_registration_cancellable);
}
current_operator_code = mm_gdbus_modem3gpp_get_operator_code (ctx->skeleton);
/* Manual registration requested? */
if (ctx->operator_id) {
/* If already registered with the requested operator, we're done */
if (current_operator_code &&
g_str_equal (current_operator_code, ctx->operator_id)) {
registration_state_context->manual_registration = TRUE;
mm_dbg ("Already registered in selected network '%s'...",
current_operator_code);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
/* Manual registration to a new operator required */
mm_dbg ("Launching manual network registration (%s)...",
ctx->operator_id);
registration_state_context->manual_registration = TRUE;
}
/* Automatic registration requested? */
else {
MMModem3gppRegistrationState reg_state = mm_gdbus_modem3gpp_get_registration_state (ctx->skeleton);
/* If the modem is already registered and the last time it was asked
* automatic registration, we're done */
if ((current_operator_code || reg_state_is_registered (reg_state)) &&
!registration_state_context->manual_registration) {
mm_dbg ("Already registered in network '%s',"
" automatic registration not launched...",
current_operator_code);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
/* Automatic registration to a new operator requested */
mm_dbg ("Launching automatic network registration...");
registration_state_context->manual_registration = FALSE;
}
ctx->cancellable = g_cancellable_new ();
/* Keep an accessible reference to the cancellable, so that we can cancel
* previous request when needed */
registration_state_context->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);
}
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_GET_INTERFACE (self)->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,
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;
}
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;
}
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;
}
/*****************************************************************************/
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 cs_supported = FALSE;
gboolean ps_supported = FALSE;
gboolean eps_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, &cs_supported,
MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, &ps_supported,
MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, &eps_supported,
NULL);
mm_dbg ("Running registration checks (CS: '%s', PS: '%s', EPS: '%s')",
cs_supported ? "yes" : "no",
ps_supported ? "yes" : "no",
eps_supported ? "yes" : "no");
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->run_registration_checks (self,
cs_supported,
ps_supported,
eps_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_warn ("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;
guint16 mcc = 0;
guint16 mnc = 0;
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_warn ("Couldn't load Operator Code: '%s'", error->message);
} else if (!mm_3gpp_parse_operator_id (str, &mcc, &mnc, &error)) {
mm_dbg ("Unexpected MCC/MNC 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 (mcc && MM_IS_IFACE_MODEM_LOCATION (self))
mm_iface_modem_location_3gpp_update_mcc_mnc (MM_IFACE_MODEM_LOCATION (self), mcc, mnc);
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_mcc_mnc (MM_IFACE_MODEM_LOCATION (self), 0, 0);
}
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_mcc_mnc (MM_IFACE_MODEM_LOCATION (self), 0, 0);
}
/*****************************************************************************/
void
mm_iface_modem_3gpp_update_access_technologies (MMIfaceModem3gpp *self,
MMModemAccessTechnology access_tech)
{
MMModem3gppRegistrationState state;
RegistrationStateContext *ctx;
ctx = get_registration_state_context (self);
g_assert (ctx);
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) || ctx->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)
{
MMModem3gppRegistrationState state;
RegistrationStateContext *ctx;
ctx = get_registration_state_context (self);
g_assert (ctx);
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) || ctx->reloading_registration_info) {
if ((location_area_code > 0 || tracking_area_code > 0) && cell_id > 0)
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)
{
MMModem3gppRegistrationState new_state;
RegistrationStateContext *ctx;
new_state = GPOINTER_TO_UINT (user_data);
mm_info ("Modem %s: 3GPP Registration state changed (registering -> %s)",
g_dbus_object_get_object_path (G_DBUS_OBJECT (self)),
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);
ctx = get_registration_state_context (self);
ctx->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)
{
MMModem3gppRegistrationState old_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
RegistrationStateContext *ctx;
g_object_get (self,
MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &old_state,
NULL);
ctx = get_registration_state_context (self);
g_assert (ctx);
/* 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 (ctx->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_dbg ("Modem %s: 3GPP Registration state change ignored as modem isn't enabled",
g_dbus_object_get_object_path (G_DBUS_OBJECT (self)));
return;
}
mm_info ("Modem %s: 3GPP Registration state changed (%s -> registering)",
g_dbus_object_get_object_path (G_DBUS_OBJECT (self)),
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 */
ctx->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_info ("Modem %s: 3GPP Registration state changed (%s -> %s)",
g_dbus_object_get_object_path (G_DBUS_OBJECT (self)),
mm_modem_3gpp_registration_state_get_string (old_state),
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)
{
RegistrationStateContext *ctx;
gboolean supported = FALSE;
g_object_get (self,
MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, &supported,
NULL);
if (!supported)
return;
ctx = get_registration_state_context (self);
ctx->cs = state;
update_registration_state (self, get_consolidated_reg_state (ctx), TRUE);
}
void
mm_iface_modem_3gpp_update_ps_registration_state (MMIfaceModem3gpp *self,
MMModem3gppRegistrationState state)
{
RegistrationStateContext *ctx;
gboolean supported = FALSE;
g_object_get (self,
MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, &supported,
NULL);
if (!supported)
return;
ctx = get_registration_state_context (self);
ctx->ps = state;
update_registration_state (self, get_consolidated_reg_state (ctx), TRUE);
}
void
mm_iface_modem_3gpp_update_eps_registration_state (MMIfaceModem3gpp *self,
MMModem3gppRegistrationState state)
{
RegistrationStateContext *ctx;
gboolean supported = FALSE;
g_object_get (self,
MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, &supported,
NULL);
if (!supported)
return;
ctx = get_registration_state_context (self);
ctx->eps = state;
update_registration_state (self, get_consolidated_reg_state (ctx), TRUE);
}
/*****************************************************************************/
typedef struct {
guint timeout_source;
gboolean running;
} RegistrationCheckContext;
static void
registration_check_context_free (RegistrationCheckContext *ctx)
{
if (ctx->timeout_source)
g_source_remove (ctx->timeout_source);
g_free (ctx);
}
static void
periodic_registration_checks_ready (MMIfaceModem3gpp *self,
GAsyncResult *res)
{
RegistrationCheckContext *ctx;
GError *error = NULL;
mm_iface_modem_3gpp_run_registration_checks_finish (self, res, &error);
if (error) {
mm_dbg ("Couldn't refresh 3GPP registration status: '%s'", error->message);
g_error_free (error);
}
/* Remove the running tag */
ctx = g_object_get_qdata (G_OBJECT (self), registration_check_context_quark);
if (ctx)
ctx->running = FALSE;
}
static gboolean
periodic_registration_check (MMIfaceModem3gpp *self)
{
RegistrationCheckContext *ctx;
/* Only launch a new one if not one running already */
ctx = g_object_get_qdata (G_OBJECT (self), registration_check_context_quark);
if (!ctx->running) {
ctx->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)
{
if (G_UNLIKELY (!registration_check_context_quark))
registration_check_context_quark = (g_quark_from_static_string (
REGISTRATION_CHECK_CONTEXT_TAG));
/* Overwriting the data will free the previous context */
g_object_set_qdata (G_OBJECT (self),
registration_check_context_quark,
NULL);
mm_dbg ("Periodic 3GPP registration checks disabled");
}
static void
periodic_registration_check_enable (MMIfaceModem3gpp *self)
{
RegistrationCheckContext *ctx;
if (G_UNLIKELY (!registration_check_context_quark))
registration_check_context_quark = (g_quark_from_static_string (
REGISTRATION_CHECK_CONTEXT_TAG));
ctx = g_object_get_qdata (G_OBJECT (self), registration_check_context_quark);
/* If context is already there, we're already enabled */
if (ctx)
return;
/* Create context and keep it as object data */
mm_dbg ("Periodic 3GPP registration checks enabled");
ctx = g_new0 (RegistrationCheckContext, 1);
ctx->timeout_source = g_timeout_add_seconds (REGISTRATION_CHECK_TIMEOUT_SEC,
(GSourceFunc)periodic_registration_check,
self);
g_object_set_qdata_full (G_OBJECT (self),
registration_check_context_quark,
ctx,
(GDestroyNotify)registration_check_context_free);
}
/*****************************************************************************/
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 *attach = NULL;
gboolean skip_update = FALSE;
g_object_get (self,
MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton,
MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER, &attach,
NULL);
g_assert (skeleton);
if (attach) {
skip_update = (properties && mm_bearer_properties_cmp (properties, mm_base_bearer_peek_config (MM_BASE_BEARER (attach))));
g_object_unref (attach);
}
if (skip_update) {
mm_dbg ("skipping initial EPS bearer update: configuration didn't change");
} else if (properties) {
mm_dbg ("updating initial EPS bearer...");
g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->create_initial_eps_bearer);
attach = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->create_initial_eps_bearer (self, properties);
g_object_set (self,
MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER, attach,
NULL);
mm_gdbus_modem3gpp_set_initial_eps_bearer (skeleton, mm_base_bearer_get_path (attach));
g_object_unref (attach);
} else {
mm_dbg ("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);
}
g_object_unref (skeleton);
}
/*****************************************************************************/
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_dbg ("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:
/* Fall down to next step */
ctx->step++;
case DISABLING_STEP_INITIAL_EPS_BEARER:
mm_iface_modem_3gpp_update_initial_eps_bearer (self, NULL);
/* Fall down to next step */
ctx->step++;
case DISABLING_STEP_PERIODIC_REGISTRATION_CHECKS:
/* Disable periodic registration checks, if they were set */
periodic_registration_check_disable (self);
/* Fall down to next step */
ctx->step++;
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;
}
/* Fall down to next step */
ctx->step++;
}
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;
}
/* Fall down to next step */
ctx->step++;
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;
}
/* Fall down to next step */
ctx->step++;
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;
}
/* Fall down to next step */
ctx->step++;
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);
/* Fall down to next step */
ctx->step++;
case DISABLING_STEP_LAST:
/* We are done without errors! */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
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_dbg ("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_dbg ("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_dbg ("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_dbg ("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_dbg ("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:
/* Fall down to next step */
ctx->step++;
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;
}
/* Fall down to next step */
ctx->step++;
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;
}
/* Fall down to next step */
ctx->step++;
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;
}
/* Fall down to next step */
ctx->step++;
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;
}
/* Fall down to next step */
ctx->step++;
}
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;
}
/* Fall down to next step */
ctx->step++;
}
case ENABLING_STEP_LAST:
/* We are done without errors! */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
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);
}
/*****************************************************************************/
typedef struct _InitializationContext InitializationContext;
static void interface_initialization_step (GTask *task);
typedef enum {
INITIALIZATION_STEP_FIRST,
INITIALIZATION_STEP_IMEI,
INITIALIZATION_STEP_ENABLED_FACILITY_LOCKS,
INITIALIZATION_STEP_EPS_UE_MODE_OPERATION,
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_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_warn ("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_warn ("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_warn ("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;
/* 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:
/* Fall down to next step */
ctx->step++;
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;
}
/* Fall down to next step */
ctx->step++;
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;
}
/* Fall down to next step */
ctx->step++;
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;
}
/* Fall down to next step */
ctx->step++;
case INITIALIZATION_STEP_LAST:
/* 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);
/* 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;
}
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);
/* If the modem is *only* LTE, we assume that CS network is not
* supported */
if (mm_iface_modem_is_3gpp_lte_only (MM_IFACE_MODEM (self))) {
mm_dbg ("Modem is LTE-only, assuming CS network is not supported");
g_object_set (self,
MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, FALSE,
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)
{
/* Remove RegistrationCheckContext object to make sure any pending
* invocation of periodic_registration_check is cancelled before the
* DBus skeleton is removed. */
if (G_LIKELY (registration_check_context_quark))
g_object_set_qdata (G_OBJECT (self),
registration_check_context_quark,
NULL);
/* 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_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;
}