6063 lines
212 KiB
C
6063 lines
212 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 Google, Inc.
|
|
*/
|
|
|
|
#include <ModemManager.h>
|
|
#define _LIBMM_INSIDE_MM
|
|
#include <libmm-glib.h>
|
|
|
|
#include "mm-modem-helpers.h"
|
|
#include "mm-iface-modem.h"
|
|
#include "mm-iface-modem-3gpp.h"
|
|
#include "mm-iface-modem-cdma.h"
|
|
#include "mm-base-modem.h"
|
|
#include "mm-base-modem-at.h"
|
|
#include "mm-base-sim.h"
|
|
#include "mm-bearer-list.h"
|
|
#include "mm-private-boxed-types.h"
|
|
#include "mm-log-object.h"
|
|
#include "mm-context.h"
|
|
#if defined WITH_QMI
|
|
# include "mm-broadband-modem-qmi.h"
|
|
#endif
|
|
#if defined WITH_MBIM
|
|
# include "mm-broadband-modem-mbim.h"
|
|
#endif
|
|
|
|
#define SIGNAL_QUALITY_RECENT_TIMEOUT_SEC 60
|
|
|
|
#define SIGNAL_CHECK_INITIAL_RETRIES 5
|
|
#define SIGNAL_CHECK_INITIAL_TIMEOUT_SEC 3
|
|
#define SIGNAL_CHECK_TIMEOUT_SEC 30
|
|
|
|
#define STATE_UPDATE_CONTEXT_TAG "state-update-context-tag"
|
|
#define SIGNAL_QUALITY_UPDATE_CONTEXT_TAG "signal-quality-update-context-tag"
|
|
#define SIGNAL_CHECK_CONTEXT_TAG "signal-check-context-tag"
|
|
#define RESTART_INITIALIZE_IDLE_TAG "restart-initialize-tag"
|
|
|
|
static GQuark state_update_context_quark;
|
|
static GQuark signal_quality_update_context_quark;
|
|
static GQuark signal_check_context_quark;
|
|
static GQuark restart_initialize_idle_quark;
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean
|
|
mm_iface_modem_check_for_sim_swap_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
explicit_check_for_sim_swap_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish (self, res, &error)) {
|
|
mm_obj_warn (self, "SIM swap check failed: %s", error->message);
|
|
g_task_return_error (task, error);
|
|
} else {
|
|
mm_obj_dbg (self, "SIM swap check completed");
|
|
g_task_return_boolean (task, TRUE);
|
|
}
|
|
g_object_unref (task);
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_check_for_sim_swap (MMIfaceModem *self,
|
|
guint slot_index,
|
|
const gchar *iccid,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
/* Slot index 0 is used when slot is not known, assumingly on a modem with just one SIM slot */
|
|
if (slot_index != 0) {
|
|
MmGdbusModem *skeleton;
|
|
guint primary_slot;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
|
|
g_assert (skeleton);
|
|
|
|
primary_slot = mm_gdbus_modem_get_primary_sim_slot (MM_GDBUS_MODEM (skeleton));
|
|
g_object_unref (skeleton);
|
|
|
|
/* Check that it's really the primary slot whose iccid has changed */
|
|
if (primary_slot && primary_slot != slot_index) {
|
|
mm_obj_dbg (self, "checking for SIM swap ignored: status changed in slot %u, but primary is %u", slot_index, primary_slot);
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish) {
|
|
mm_obj_dbg (self, "start checking for SIM swap in slot %u", slot_index);
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap (
|
|
self,
|
|
iccid,
|
|
(GAsyncReadyCallback)explicit_check_for_sim_swap_ready,
|
|
task);
|
|
return;
|
|
}
|
|
|
|
g_task_return_boolean (task, FALSE);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
mm_iface_modem_bind_simple_status (MMIfaceModem *self,
|
|
MMSimpleStatus *status)
|
|
{
|
|
MmGdbusModem *skeleton;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
if (!skeleton)
|
|
return;
|
|
|
|
g_object_bind_property (skeleton, "state",
|
|
status, MM_SIMPLE_PROPERTY_STATE,
|
|
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
|
|
|
|
g_object_bind_property (skeleton, "signal-quality",
|
|
status, MM_SIMPLE_PROPERTY_SIGNAL_QUALITY,
|
|
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
|
|
|
|
g_object_bind_property (skeleton, "current-bands",
|
|
status, MM_SIMPLE_PROPERTY_CURRENT_BANDS,
|
|
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
|
|
|
|
g_object_bind_property (skeleton, "access-technologies",
|
|
status, MM_SIMPLE_PROPERTY_ACCESS_TECHNOLOGIES,
|
|
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
|
|
|
|
g_object_unref (skeleton);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Helper method to wait for a final state */
|
|
|
|
#define MODEM_STATE_IS_INTERMEDIATE(state) \
|
|
(state == MM_MODEM_STATE_INITIALIZING || \
|
|
state == MM_MODEM_STATE_DISABLING || \
|
|
state == MM_MODEM_STATE_ENABLING || \
|
|
state == MM_MODEM_STATE_DISCONNECTING || \
|
|
state == MM_MODEM_STATE_CONNECTING)
|
|
|
|
typedef struct {
|
|
MMModemState final_state;
|
|
gulong state_changed_id;
|
|
guint state_changed_wait_id;
|
|
} WaitForFinalStateContext;
|
|
|
|
static void
|
|
wait_for_final_state_context_complete (GTask *task,
|
|
MMModemState state,
|
|
GError *error)
|
|
{
|
|
MMIfaceModem *self;
|
|
WaitForFinalStateContext *ctx;
|
|
|
|
self = g_task_get_source_object (task);
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
/* The callback associated with 'task' may update the modem state.
|
|
* Disconnect the signal handler for modem state changes before completing
|
|
* 'task' in order to prevent state_changed from being invoked, which
|
|
* invokes wait_for_final_state_context_complete again. */
|
|
if (ctx->state_changed_id) {
|
|
g_signal_handler_disconnect (self, ctx->state_changed_id);
|
|
ctx->state_changed_id = 0;
|
|
}
|
|
|
|
/* Remove any outstanding timeout on waiting for state change. */
|
|
if (ctx->state_changed_wait_id) {
|
|
g_source_remove (ctx->state_changed_wait_id);
|
|
ctx->state_changed_wait_id = 0;
|
|
}
|
|
|
|
if (error)
|
|
g_task_return_error (task, error);
|
|
else
|
|
g_task_return_int (task, state);
|
|
|
|
g_object_unref (task);
|
|
}
|
|
|
|
MMModemState
|
|
mm_iface_modem_wait_for_final_state_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
GError *inner_error = NULL;
|
|
gssize value;
|
|
|
|
value = g_task_propagate_int (G_TASK (res), &inner_error);
|
|
if (inner_error) {
|
|
g_propagate_error (error, inner_error);
|
|
return MM_MODEM_STATE_UNKNOWN;
|
|
}
|
|
return (MMModemState)value;
|
|
}
|
|
|
|
static gboolean
|
|
state_changed_wait_expired (GTask *task)
|
|
{
|
|
GError *error;
|
|
|
|
error = g_error_new (MM_CORE_ERROR,
|
|
MM_CORE_ERROR_RETRY,
|
|
"Too much time waiting to get to a final state");
|
|
wait_for_final_state_context_complete (task, MM_MODEM_STATE_UNKNOWN, error);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
state_changed (MMIfaceModem *self,
|
|
GParamSpec *spec,
|
|
GTask *task)
|
|
{
|
|
WaitForFinalStateContext *ctx;
|
|
MMModemState state = MM_MODEM_STATE_UNKNOWN;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_STATE, &state,
|
|
NULL);
|
|
|
|
/* Are we in a final state already? */
|
|
if (MODEM_STATE_IS_INTERMEDIATE (state))
|
|
return;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
/* If we want a specific final state and this is not the one we were
|
|
* looking for, then skip */
|
|
if (ctx->final_state != MM_MODEM_STATE_UNKNOWN &&
|
|
state != MM_MODEM_STATE_UNKNOWN &&
|
|
state != ctx->final_state)
|
|
return;
|
|
|
|
/* Done! */
|
|
wait_for_final_state_context_complete (task, state, NULL);
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_wait_for_final_state (MMIfaceModem *self,
|
|
MMModemState final_state,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
MMModemState state = MM_MODEM_STATE_UNKNOWN;
|
|
WaitForFinalStateContext *ctx;
|
|
GTask *task;
|
|
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_STATE, &state,
|
|
NULL);
|
|
|
|
/* Are we in a final state already? */
|
|
if (!MODEM_STATE_IS_INTERMEDIATE (state)) {
|
|
/* Is this the state we actually wanted? */
|
|
if (final_state == MM_MODEM_STATE_UNKNOWN ||
|
|
(state != MM_MODEM_STATE_UNKNOWN && state == final_state)) {
|
|
g_task_return_int (task, state);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
/* Otherwise, we'll need to wait for the exact one we want */
|
|
}
|
|
|
|
ctx = g_new0 (WaitForFinalStateContext, 1);
|
|
ctx->final_state = final_state;
|
|
|
|
g_task_set_task_data (task, ctx, g_free);
|
|
|
|
/* Want to get notified when modem state changes */
|
|
ctx->state_changed_id = g_signal_connect (self,
|
|
"notify::" MM_IFACE_MODEM_STATE,
|
|
G_CALLBACK (state_changed),
|
|
task);
|
|
/* But we don't want to wait forever */
|
|
ctx->state_changed_wait_id = g_timeout_add_seconds (10,
|
|
(GSourceFunc)state_changed_wait_expired,
|
|
task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Helper to return an error when the modem is in failed state and so it
|
|
* cannot process a given method invocation
|
|
*/
|
|
|
|
static gboolean
|
|
abort_invocation_if_state_not_reached (MMIfaceModem *self,
|
|
GDBusMethodInvocation *invocation,
|
|
MMModemState minimum_required)
|
|
{
|
|
MMModemState state = MM_MODEM_STATE_UNKNOWN;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_STATE, &state,
|
|
NULL);
|
|
|
|
if (state >= minimum_required)
|
|
return FALSE;
|
|
|
|
g_dbus_method_invocation_return_error (invocation,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_WRONG_STATE,
|
|
"modem in %s state",
|
|
mm_modem_state_get_string (state));
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Helper method to load unlock required, considering retries */
|
|
|
|
#define MAX_RETRIES 6
|
|
|
|
typedef struct {
|
|
guint retries;
|
|
guint pin_check_timeout_id;
|
|
} InternalLoadUnlockRequiredContext;
|
|
|
|
static MMModemLock
|
|
internal_load_unlock_required_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
GError *inner_error = NULL;
|
|
gssize value;
|
|
|
|
value = g_task_propagate_int (G_TASK (res), &inner_error);
|
|
if (inner_error) {
|
|
g_propagate_error (error, inner_error);
|
|
return MM_MODEM_LOCK_UNKNOWN;
|
|
}
|
|
return (MMModemLock)value;
|
|
}
|
|
|
|
static void internal_load_unlock_required_context_step (GTask *task);
|
|
|
|
static gboolean
|
|
load_unlock_required_again (GTask *task)
|
|
{
|
|
InternalLoadUnlockRequiredContext *ctx;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
ctx->pin_check_timeout_id = 0;
|
|
/* Retry the step */
|
|
internal_load_unlock_required_context_step (task);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
load_unlock_required_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InternalLoadUnlockRequiredContext *ctx;
|
|
GError *error = NULL;
|
|
MMModemLock lock;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
lock = MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required_finish (self, res, &error);
|
|
if (error) {
|
|
mm_obj_dbg (self, "couldn't check if unlock required: %s", error->message);
|
|
|
|
/* For several kinds of errors, just return them directly */
|
|
if (error->domain == MM_SERIAL_ERROR ||
|
|
g_error_matches (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_CANCELLED) ||
|
|
g_error_matches (error,
|
|
MM_MOBILE_EQUIPMENT_ERROR,
|
|
MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) ||
|
|
g_error_matches (error,
|
|
MM_MOBILE_EQUIPMENT_ERROR,
|
|
MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) ||
|
|
g_error_matches (error,
|
|
MM_MOBILE_EQUIPMENT_ERROR,
|
|
MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) {
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
/* For the remaining ones, retry if possible */
|
|
if (ctx->retries < MAX_RETRIES) {
|
|
ctx->retries++;
|
|
mm_obj_dbg (self, "retrying (%u) unlock required check", ctx->retries);
|
|
|
|
g_assert (ctx->pin_check_timeout_id == 0);
|
|
ctx->pin_check_timeout_id = g_timeout_add_seconds (2,
|
|
(GSourceFunc)load_unlock_required_again,
|
|
task);
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
|
|
/* If reached max retries and still reporting error... default to SIM error */
|
|
g_error_free (error);
|
|
g_task_return_new_error (task,
|
|
MM_MOBILE_EQUIPMENT_ERROR,
|
|
MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE,
|
|
"Couldn't get SIM lock status after %u retries",
|
|
MAX_RETRIES);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
/* Got the lock value, return it */
|
|
g_task_return_int (task, lock);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
internal_load_unlock_required_context_step (GTask *task)
|
|
{
|
|
MMIfaceModem *self;
|
|
InternalLoadUnlockRequiredContext *ctx;
|
|
|
|
self = g_task_get_source_object (task);
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
g_assert (ctx->pin_check_timeout_id == 0);
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required (
|
|
self,
|
|
(ctx->retries == MAX_RETRIES), /* last_attempt? */
|
|
(GAsyncReadyCallback)load_unlock_required_ready,
|
|
task);
|
|
}
|
|
|
|
static void
|
|
internal_load_unlock_required (MMIfaceModem *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
InternalLoadUnlockRequiredContext *ctx;
|
|
GTask *task;
|
|
|
|
ctx = g_new0 (InternalLoadUnlockRequiredContext, 1);
|
|
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
g_task_set_task_data (task, ctx, g_free);
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required ||
|
|
!MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required_finish) {
|
|
/* Just assume that no lock is required */
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
internal_load_unlock_required_context_step (task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
bearer_list_updated (MMBearerList *bearer_list,
|
|
GParamSpec *pspec,
|
|
MMIfaceModem *self)
|
|
{
|
|
MmGdbusModem *skeleton;
|
|
gchar **paths;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
if (!skeleton)
|
|
return;
|
|
|
|
paths = mm_bearer_list_get_paths (bearer_list);
|
|
mm_gdbus_modem_set_bearers (skeleton, (const gchar *const *)paths);
|
|
g_strfreev (paths);
|
|
|
|
g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static MMModemState get_consolidated_subsystem_state (MMIfaceModem *self);
|
|
|
|
typedef struct {
|
|
MMBaseBearer *self;
|
|
guint others_connected;
|
|
} CountOthersConnectedContext;
|
|
|
|
static void
|
|
bearer_list_count_others_connected (MMBaseBearer *bearer,
|
|
CountOthersConnectedContext *ctx)
|
|
{
|
|
/* We can safely compare pointers here */
|
|
if (bearer != ctx->self &&
|
|
mm_base_bearer_get_status (bearer) == MM_BEARER_STATUS_CONNECTED) {
|
|
ctx->others_connected++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
bearer_status_changed (MMBaseBearer *bearer,
|
|
GParamSpec *pspec,
|
|
MMIfaceModem *self)
|
|
{
|
|
CountOthersConnectedContext ctx;
|
|
MMBearerList *list = NULL;
|
|
MMModemState state = MM_MODEM_STATE_UNKNOWN;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_STATE, &state,
|
|
MM_IFACE_MODEM_BEARER_LIST, &list,
|
|
NULL);
|
|
if (!list)
|
|
return;
|
|
|
|
if (state == MM_MODEM_STATE_DISABLING ||
|
|
state == MM_MODEM_STATE_ENABLING) {
|
|
/* Don't log modem bearer-specific status changes if we're disabling
|
|
* or enabling */
|
|
g_object_unref (list);
|
|
return;
|
|
}
|
|
|
|
ctx.self = bearer;
|
|
ctx.others_connected = 0;
|
|
|
|
/* We now count how many *other* bearers are connected */
|
|
mm_bearer_list_foreach (list,
|
|
(MMBearerListForeachFunc)bearer_list_count_others_connected,
|
|
&ctx);
|
|
|
|
/* If no other bearers are connected, change modem state */
|
|
if (!ctx.others_connected) {
|
|
MMModemState new_state = MM_MODEM_STATE_UNKNOWN;
|
|
|
|
switch (mm_base_bearer_get_status (bearer)) {
|
|
case MM_BEARER_STATUS_CONNECTED:
|
|
new_state = MM_MODEM_STATE_CONNECTED;
|
|
break;
|
|
case MM_BEARER_STATUS_CONNECTING:
|
|
new_state = MM_MODEM_STATE_CONNECTING;
|
|
break;
|
|
case MM_BEARER_STATUS_DISCONNECTING:
|
|
new_state = MM_MODEM_STATE_DISCONNECTING;
|
|
break;
|
|
case MM_BEARER_STATUS_DISCONNECTED:
|
|
new_state = get_consolidated_subsystem_state (self);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
mm_iface_modem_update_state (self,
|
|
new_state,
|
|
MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED);
|
|
}
|
|
|
|
g_object_unref (list);
|
|
}
|
|
|
|
typedef struct {
|
|
MMBearerList *list;
|
|
} CreateBearerContext;
|
|
|
|
static void
|
|
create_bearer_context_free (CreateBearerContext *ctx)
|
|
{
|
|
if (ctx->list)
|
|
g_object_unref (ctx->list);
|
|
g_slice_free (CreateBearerContext, ctx);
|
|
}
|
|
|
|
MMBaseBearer *
|
|
mm_iface_modem_create_bearer_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_pointer (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
create_bearer_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
CreateBearerContext *ctx;
|
|
MMBaseBearer *bearer;
|
|
GError *error = NULL;
|
|
|
|
bearer = MM_IFACE_MODEM_GET_INTERFACE (self)->create_bearer_finish (self, res, &error);
|
|
if (error) {
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
if (!mm_bearer_list_add_bearer (ctx->list, bearer, &error)) {
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
g_object_unref (bearer);
|
|
return;
|
|
}
|
|
|
|
/* If bearer properly created and added to the list, follow its
|
|
* status */
|
|
g_signal_connect (bearer,
|
|
"notify::" MM_BASE_BEARER_STATUS,
|
|
(GCallback)bearer_status_changed,
|
|
self);
|
|
g_task_return_pointer (task, bearer, g_object_unref);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_create_bearer (MMIfaceModem *self,
|
|
MMBearerProperties *properties,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
CreateBearerContext *ctx;
|
|
GTask *task;
|
|
|
|
ctx = g_slice_new (CreateBearerContext);
|
|
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify)create_bearer_context_free);
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_BEARER_LIST, &ctx->list,
|
|
NULL);
|
|
if (!ctx->list) {
|
|
g_task_return_new_error (
|
|
task,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"Cannot add new bearer: bearer list not found");
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
if (mm_bearer_list_get_count (ctx->list) == mm_bearer_list_get_max (ctx->list)) {
|
|
g_task_return_new_error (
|
|
task,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_TOO_MANY,
|
|
"Cannot add new bearer: already reached maximum (%u)",
|
|
mm_bearer_list_get_count (ctx->list));
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->create_bearer (
|
|
self,
|
|
properties,
|
|
(GAsyncReadyCallback)create_bearer_ready,
|
|
task);
|
|
}
|
|
|
|
typedef struct {
|
|
MmGdbusModem *skeleton;
|
|
GDBusMethodInvocation *invocation;
|
|
MMIfaceModem *self;
|
|
GVariant *dictionary;
|
|
} HandleCreateBearerContext;
|
|
|
|
static void
|
|
handle_create_bearer_context_free (HandleCreateBearerContext *ctx)
|
|
{
|
|
g_variant_unref (ctx->dictionary);
|
|
g_object_unref (ctx->skeleton);
|
|
g_object_unref (ctx->invocation);
|
|
g_object_unref (ctx->self);
|
|
g_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_create_bearer_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
HandleCreateBearerContext *ctx)
|
|
{
|
|
MMBaseBearer *bearer;
|
|
GError *error = NULL;
|
|
|
|
bearer = mm_iface_modem_create_bearer_finish (self, res, &error);
|
|
if (!bearer)
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
else {
|
|
mm_gdbus_modem_complete_create_bearer (ctx->skeleton,
|
|
ctx->invocation,
|
|
mm_base_bearer_get_path (bearer));
|
|
g_object_unref (bearer);
|
|
}
|
|
|
|
handle_create_bearer_context_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_create_bearer_auth_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
HandleCreateBearerContext *ctx)
|
|
{
|
|
MMBearerProperties *properties;
|
|
GError *error = NULL;
|
|
|
|
if (!mm_base_modem_authorize_finish (self, res, &error)) {
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
handle_create_bearer_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
if (abort_invocation_if_state_not_reached (ctx->self, ctx->invocation, MM_MODEM_STATE_LOCKED)) {
|
|
handle_create_bearer_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
properties = mm_bearer_properties_new_from_dictionary (ctx->dictionary, &error);
|
|
if (!properties) {
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
handle_create_bearer_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
mm_iface_modem_create_bearer (
|
|
ctx->self,
|
|
properties,
|
|
(GAsyncReadyCallback)handle_create_bearer_ready,
|
|
ctx);
|
|
g_object_unref (properties);
|
|
}
|
|
|
|
static gboolean
|
|
handle_create_bearer (MmGdbusModem *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
GVariant *dictionary,
|
|
MMIfaceModem *self)
|
|
{
|
|
HandleCreateBearerContext *ctx;
|
|
|
|
ctx = g_new (HandleCreateBearerContext, 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)handle_create_bearer_auth_ready,
|
|
ctx);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
MmGdbusModem *skeleton;
|
|
GDBusMethodInvocation *invocation;
|
|
MMIfaceModem *self;
|
|
gchar *cmd;
|
|
guint timeout;
|
|
} HandleCommandContext;
|
|
|
|
static void
|
|
handle_command_context_free (HandleCommandContext *ctx)
|
|
{
|
|
g_object_unref (ctx->skeleton);
|
|
g_object_unref (ctx->invocation);
|
|
g_object_unref (ctx->self);
|
|
g_free (ctx->cmd);
|
|
g_free (ctx);
|
|
}
|
|
|
|
static void
|
|
command_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
HandleCommandContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
const gchar *result;
|
|
|
|
result = MM_IFACE_MODEM_GET_INTERFACE (self)->command_finish (self,
|
|
res,
|
|
&error);
|
|
if (error)
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
else
|
|
mm_gdbus_modem_complete_command (ctx->skeleton, ctx->invocation, result);
|
|
|
|
handle_command_context_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_command_auth_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
HandleCommandContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_base_modem_authorize_finish (self, res, &error)) {
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
handle_command_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
#if ! defined WITH_AT_COMMAND_VIA_DBUS
|
|
/* If we are not in Debug mode, report an error */
|
|
if (!mm_context_get_debug ()) {
|
|
g_dbus_method_invocation_return_error (ctx->invocation,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNAUTHORIZED,
|
|
"Cannot send AT command to modem: "
|
|
"operation only allowed in debug mode");
|
|
handle_command_context_free (ctx);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/* If command is not implemented, report an error */
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->command ||
|
|
!MM_IFACE_MODEM_GET_INTERFACE (self)->command_finish) {
|
|
g_dbus_method_invocation_return_error (ctx->invocation,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNSUPPORTED,
|
|
"Cannot send AT command to modem: "
|
|
"operation not supported");
|
|
handle_command_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->command (ctx->self,
|
|
ctx->cmd,
|
|
ctx->timeout,
|
|
(GAsyncReadyCallback)command_ready,
|
|
ctx);
|
|
}
|
|
|
|
static gboolean
|
|
handle_command (MmGdbusModem *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
const gchar *cmd,
|
|
guint timeout,
|
|
MMIfaceModem *self)
|
|
{
|
|
HandleCommandContext *ctx;
|
|
|
|
ctx = g_new (HandleCommandContext, 1);
|
|
ctx->skeleton = g_object_ref (skeleton);
|
|
ctx->invocation = g_object_ref (invocation);
|
|
ctx->self = g_object_ref (self);
|
|
ctx->cmd = g_strdup (cmd);
|
|
ctx->timeout = timeout;
|
|
|
|
mm_base_modem_authorize (MM_BASE_MODEM (self),
|
|
invocation,
|
|
MM_AUTHORIZATION_DEVICE_CONTROL,
|
|
(GAsyncReadyCallback)handle_command_auth_ready,
|
|
ctx);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
MmGdbusModem *skeleton;
|
|
GDBusMethodInvocation *invocation;
|
|
MMIfaceModem *self;
|
|
MMBearerList *list;
|
|
gchar *bearer_path;
|
|
MMBaseBearer *bearer;
|
|
} HandleDeleteBearerContext;
|
|
|
|
static void
|
|
handle_delete_bearer_context_free (HandleDeleteBearerContext *ctx)
|
|
{
|
|
if (ctx->bearer)
|
|
g_object_unref (ctx->bearer);
|
|
g_object_unref (ctx->skeleton);
|
|
g_object_unref (ctx->invocation);
|
|
g_object_unref (ctx->self);
|
|
if (ctx->list)
|
|
g_object_unref (ctx->list);
|
|
g_free (ctx->bearer_path);
|
|
g_free (ctx);
|
|
}
|
|
|
|
static void
|
|
delete_bearer_disconnect_ready (MMBaseBearer *bearer,
|
|
GAsyncResult *res,
|
|
HandleDeleteBearerContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_base_bearer_disconnect_finish (bearer, res, &error)) {
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
handle_delete_bearer_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
if (!mm_bearer_list_delete_bearer (ctx->list, ctx->bearer_path, &error))
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
else
|
|
mm_gdbus_modem_complete_delete_bearer (ctx->skeleton, ctx->invocation);
|
|
handle_delete_bearer_context_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_delete_bearer_auth_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
HandleDeleteBearerContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_base_modem_authorize_finish (self, res, &error)) {
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
handle_delete_bearer_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
if (abort_invocation_if_state_not_reached (ctx->self, ctx->invocation, MM_MODEM_STATE_LOCKED)) {
|
|
handle_delete_bearer_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
if (!g_str_has_prefix (ctx->bearer_path, MM_DBUS_BEARER_PREFIX)) {
|
|
g_dbus_method_invocation_return_error (ctx->invocation,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_INVALID_ARGS,
|
|
"Cannot delete bearer: invalid path '%s'",
|
|
ctx->bearer_path);
|
|
handle_delete_bearer_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
ctx->bearer = mm_bearer_list_find_by_path (ctx->list, ctx->bearer_path);
|
|
if (!ctx->bearer) {
|
|
g_dbus_method_invocation_return_error (ctx->invocation,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_INVALID_ARGS,
|
|
"Cannot delete bearer: no bearer found with path '%s'",
|
|
ctx->bearer_path);
|
|
handle_delete_bearer_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
mm_base_bearer_disconnect (ctx->bearer,
|
|
(GAsyncReadyCallback)delete_bearer_disconnect_ready,
|
|
ctx);
|
|
}
|
|
|
|
static gboolean
|
|
handle_delete_bearer (MmGdbusModem *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
const gchar *bearer,
|
|
MMIfaceModem *self)
|
|
{
|
|
HandleDeleteBearerContext *ctx;
|
|
|
|
ctx = g_new (HandleDeleteBearerContext, 1);
|
|
ctx->skeleton = g_object_ref (skeleton);
|
|
ctx->invocation = g_object_ref (invocation);
|
|
ctx->self = g_object_ref (self);
|
|
ctx->bearer_path = g_strdup (bearer);
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_BEARER_LIST, &ctx->list,
|
|
NULL);
|
|
|
|
mm_base_modem_authorize (MM_BASE_MODEM (self),
|
|
invocation,
|
|
MM_AUTHORIZATION_DEVICE_CONTROL,
|
|
(GAsyncReadyCallback)handle_delete_bearer_auth_ready,
|
|
ctx);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
handle_list_bearers (MmGdbusModem *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
MMIfaceModem *self)
|
|
{
|
|
GStrv paths;
|
|
MMBearerList *list = NULL;
|
|
|
|
if (abort_invocation_if_state_not_reached (self, invocation, MM_MODEM_STATE_LOCKED))
|
|
return TRUE;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_BEARER_LIST, &list,
|
|
NULL);
|
|
if (!list) {
|
|
g_dbus_method_invocation_return_error (invocation,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"Bearer list not found");
|
|
return TRUE;
|
|
}
|
|
|
|
paths = mm_bearer_list_get_paths (list);
|
|
mm_gdbus_modem_complete_list_bearers (skeleton,
|
|
invocation,
|
|
(const gchar *const *)paths);
|
|
|
|
g_strfreev (paths);
|
|
g_object_unref (list);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
MmGdbusModem *skeleton;
|
|
GDBusMethodInvocation *invocation;
|
|
MMIfaceModem *self;
|
|
guint requested_sim_slot;
|
|
} HandleSetPrimarySimSlotContext;
|
|
|
|
static void
|
|
handle_set_primary_sim_slot_context_free (HandleSetPrimarySimSlotContext *ctx)
|
|
{
|
|
g_clear_object (&ctx->skeleton);
|
|
g_clear_object (&ctx->invocation);
|
|
g_clear_object (&ctx->self);
|
|
g_free (ctx);
|
|
}
|
|
|
|
static void
|
|
set_primary_sim_slot_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
HandleSetPrimarySimSlotContext *ctx)
|
|
{
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot_finish (self, res, &error)) {
|
|
/* If the implementation returns EXISTS, we're already in the requested SIM slot,
|
|
* so we can safely return a success on the operation and skip the reprobing */
|
|
if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_EXISTS)) {
|
|
mm_obj_warn (self, "couldn't process primary SIM update request: %s", error->message);
|
|
g_dbus_method_invocation_take_error (ctx->invocation, g_steal_pointer (&error));
|
|
handle_set_primary_sim_slot_context_free (ctx);
|
|
return;
|
|
}
|
|
mm_obj_dbg (self, "ignoring SIM update request: %s", error->message);
|
|
} else {
|
|
/* Notify about the SIM swap, which will disable and reprobe the device.
|
|
* There is no need to update the PrimarySimSlot property, as this value will be
|
|
* reloaded automatically during the reprobe. */
|
|
mm_base_modem_process_sim_switch (MM_BASE_MODEM (self));
|
|
}
|
|
|
|
mm_gdbus_modem_complete_set_primary_sim_slot (ctx->skeleton, ctx->invocation);
|
|
handle_set_primary_sim_slot_context_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_set_primary_sim_slot_auth_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
HandleSetPrimarySimSlotContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
const gchar *const *sim_slot_paths;
|
|
|
|
if (!mm_base_modem_authorize_finish (self, res, &error)) {
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
handle_set_primary_sim_slot_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
/* If SIM switching is not implemented, report an error */
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot ||
|
|
!MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot_finish) {
|
|
g_dbus_method_invocation_return_error (ctx->invocation,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNSUPPORTED,
|
|
"Cannot switch sim: "
|
|
"operation not supported");
|
|
handle_set_primary_sim_slot_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
/* Validate SIM slot number */
|
|
sim_slot_paths = mm_gdbus_modem_get_sim_slots (ctx->skeleton);
|
|
if (ctx->requested_sim_slot > g_strv_length ((gchar **)sim_slot_paths)) {
|
|
g_dbus_method_invocation_return_error (ctx->invocation,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_INVALID_ARGS,
|
|
"Cannot switch sim: requested SIM slot number is out of bounds");
|
|
handle_set_primary_sim_slot_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot (MM_IFACE_MODEM (self),
|
|
ctx->requested_sim_slot,
|
|
(GAsyncReadyCallback)set_primary_sim_slot_ready,
|
|
ctx);
|
|
}
|
|
|
|
static gboolean
|
|
handle_set_primary_sim_slot (MmGdbusModem *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
guint sim_slot,
|
|
MMIfaceModem *self)
|
|
{
|
|
HandleSetPrimarySimSlotContext *ctx;
|
|
|
|
ctx = g_new0 (HandleSetPrimarySimSlotContext, 1);
|
|
ctx->skeleton = g_object_ref (skeleton);
|
|
ctx->invocation = g_object_ref (invocation);
|
|
ctx->self = g_object_ref (self);
|
|
ctx->requested_sim_slot = sim_slot;
|
|
|
|
mm_base_modem_authorize (MM_BASE_MODEM (self),
|
|
invocation,
|
|
MM_AUTHORIZATION_DEVICE_CONTROL,
|
|
(GAsyncReadyCallback)handle_set_primary_sim_slot_auth_ready,
|
|
ctx);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
mm_iface_modem_update_access_technologies (MMIfaceModem *self,
|
|
MMModemAccessTechnology new_access_tech,
|
|
guint32 mask)
|
|
{
|
|
MmGdbusModem *skeleton = NULL;
|
|
MMModemAccessTechnology old_access_tech;
|
|
MMModemAccessTechnology built_access_tech;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
|
|
/* Don't process updates if the interface is shut down */
|
|
if (!skeleton)
|
|
return;
|
|
|
|
old_access_tech = mm_gdbus_modem_get_access_technologies (skeleton);
|
|
|
|
/* Build the new access tech */
|
|
built_access_tech = old_access_tech;
|
|
built_access_tech &= ~mask;
|
|
built_access_tech |= new_access_tech;
|
|
|
|
if (built_access_tech != old_access_tech) {
|
|
gchar *old_access_tech_string;
|
|
gchar *new_access_tech_string;
|
|
|
|
mm_gdbus_modem_set_access_technologies (skeleton, built_access_tech);
|
|
|
|
/* Log */
|
|
old_access_tech_string = mm_modem_access_technology_build_string_from_mask (old_access_tech);
|
|
new_access_tech_string = mm_modem_access_technology_build_string_from_mask (built_access_tech);
|
|
mm_obj_dbg (self, "access technology changed (%s -> %s)",
|
|
old_access_tech_string,
|
|
new_access_tech_string);
|
|
g_free (old_access_tech_string);
|
|
g_free (new_access_tech_string);
|
|
}
|
|
|
|
g_object_unref (skeleton);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
guint recent_timeout_source;
|
|
} SignalQualityUpdateContext;
|
|
|
|
static void
|
|
signal_quality_update_context_free (SignalQualityUpdateContext *ctx)
|
|
{
|
|
if (ctx->recent_timeout_source)
|
|
g_source_remove (ctx->recent_timeout_source);
|
|
g_free (ctx);
|
|
}
|
|
|
|
static gboolean
|
|
expire_signal_quality (MMIfaceModem *self)
|
|
{
|
|
MmGdbusModem *skeleton = NULL;
|
|
SignalQualityUpdateContext *ctx;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
|
|
if (skeleton) {
|
|
GVariant *old;
|
|
guint signal_quality = 0;
|
|
gboolean recent = FALSE;
|
|
|
|
old = mm_gdbus_modem_get_signal_quality (skeleton);
|
|
g_variant_get (old,
|
|
"(ub)",
|
|
&signal_quality,
|
|
&recent);
|
|
|
|
/* If value is already not recent, we're done */
|
|
if (recent) {
|
|
mm_obj_dbg (self, "signal quality value not updated in %us, marking as not being recent",
|
|
SIGNAL_QUALITY_RECENT_TIMEOUT_SEC);
|
|
mm_gdbus_modem_set_signal_quality (skeleton,
|
|
g_variant_new ("(ub)",
|
|
signal_quality,
|
|
FALSE));
|
|
}
|
|
|
|
g_object_unref (skeleton);
|
|
}
|
|
|
|
/* Remove source id */
|
|
ctx = g_object_get_qdata (G_OBJECT (self), signal_quality_update_context_quark);
|
|
ctx->recent_timeout_source = 0;
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
update_signal_quality (MMIfaceModem *self,
|
|
guint signal_quality,
|
|
gboolean expire)
|
|
{
|
|
SignalQualityUpdateContext *ctx;
|
|
MmGdbusModem *skeleton = NULL;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
|
|
/* Don't process updates if the interface is shut down */
|
|
if (!skeleton)
|
|
return;
|
|
|
|
if (G_UNLIKELY (!signal_quality_update_context_quark))
|
|
signal_quality_update_context_quark = (g_quark_from_static_string (
|
|
SIGNAL_QUALITY_UPDATE_CONTEXT_TAG));
|
|
|
|
ctx = g_object_get_qdata (G_OBJECT (self), signal_quality_update_context_quark);
|
|
if (!ctx) {
|
|
/* Create context and keep it as object data */
|
|
ctx = g_new0 (SignalQualityUpdateContext, 1);
|
|
g_object_set_qdata_full (
|
|
G_OBJECT (self),
|
|
signal_quality_update_context_quark,
|
|
ctx,
|
|
(GDestroyNotify)signal_quality_update_context_free);
|
|
}
|
|
|
|
/* Note: we always set the new value, even if the signal quality level
|
|
* is the same, in order to provide an up to date 'recent' flag.
|
|
* The only exception being if 'expire' is FALSE; in that case we assume
|
|
* the value won't expire and therefore can be considered obsolete
|
|
* already. */
|
|
mm_gdbus_modem_set_signal_quality (skeleton,
|
|
g_variant_new ("(ub)",
|
|
signal_quality,
|
|
expire));
|
|
|
|
mm_obj_dbg (self, "signal quality updated (%u)", signal_quality);
|
|
|
|
/* Remove any previous expiration refresh timeout */
|
|
if (ctx->recent_timeout_source) {
|
|
g_source_remove (ctx->recent_timeout_source);
|
|
ctx->recent_timeout_source = 0;
|
|
}
|
|
|
|
/* If we got a new expirable value, setup new timeout */
|
|
if (expire)
|
|
ctx->recent_timeout_source = (g_timeout_add_seconds (
|
|
SIGNAL_QUALITY_RECENT_TIMEOUT_SEC,
|
|
(GSourceFunc)expire_signal_quality,
|
|
self));
|
|
|
|
g_object_unref (skeleton);
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_update_signal_quality (MMIfaceModem *self,
|
|
guint signal_quality)
|
|
{
|
|
update_signal_quality (self, signal_quality, TRUE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Signal info (quality and access technology) polling */
|
|
|
|
typedef enum {
|
|
SIGNAL_CHECK_STEP_NONE,
|
|
SIGNAL_CHECK_STEP_FIRST,
|
|
SIGNAL_CHECK_STEP_SIGNAL_QUALITY,
|
|
SIGNAL_CHECK_STEP_ACCESS_TECHNOLOGIES,
|
|
SIGNAL_CHECK_STEP_LAST,
|
|
} SignalCheckStep;
|
|
|
|
typedef struct {
|
|
gboolean enabled;
|
|
guint timeout_source;
|
|
|
|
/* We first attempt an initial loading, and once it's done we
|
|
* setup polling */
|
|
guint initial_retries;
|
|
gboolean initial_check_done;
|
|
|
|
/* Values polled in this iteration */
|
|
guint signal_quality;
|
|
MMModemAccessTechnology access_technologies;
|
|
guint access_technologies_mask;
|
|
|
|
/* If both signal and access tech polling are either unsupported
|
|
* or disabled, we'll automatically stop polling */
|
|
gboolean signal_quality_polling_supported;
|
|
gboolean signal_quality_polling_disabled;
|
|
gboolean access_technology_polling_supported;
|
|
gboolean access_technology_polling_disabled;
|
|
|
|
/* Steps triggered when polling active */
|
|
SignalCheckStep running_step;
|
|
} SignalCheckContext;
|
|
|
|
static void
|
|
signal_check_context_free (SignalCheckContext *ctx)
|
|
{
|
|
if (ctx->timeout_source)
|
|
g_source_remove (ctx->timeout_source);
|
|
g_slice_free (SignalCheckContext, ctx);
|
|
}
|
|
|
|
static SignalCheckContext *
|
|
get_signal_check_context (MMIfaceModem *self)
|
|
{
|
|
SignalCheckContext *ctx;
|
|
|
|
if (G_UNLIKELY (!signal_check_context_quark))
|
|
signal_check_context_quark = (g_quark_from_static_string (
|
|
SIGNAL_CHECK_CONTEXT_TAG));
|
|
|
|
ctx = g_object_get_qdata (G_OBJECT (self), signal_check_context_quark);
|
|
if (!ctx) {
|
|
/* Create context and attach it to the object */
|
|
ctx = g_slice_new0 (SignalCheckContext);
|
|
ctx->running_step = SIGNAL_CHECK_STEP_NONE;
|
|
|
|
/* Initially assume supported if load_access_technologies() is
|
|
* implemented. If the plugin reports an UNSUPPORTED error we'll clear
|
|
* this flag and no longer poll. */
|
|
ctx->access_technology_polling_supported = (MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies_finish);
|
|
|
|
/* Initially assume supported if load_signal_quality() is
|
|
* implemented. If the plugin reports an UNSUPPORTED error we'll clear
|
|
* this flag and no longer poll. */
|
|
ctx->signal_quality_polling_supported = (MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality_finish);
|
|
|
|
/* Get plugin-specific setup for the polling logic */
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, &ctx->signal_quality_polling_disabled,
|
|
MM_IFACE_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED, &ctx->access_technology_polling_disabled,
|
|
NULL);
|
|
|
|
g_object_set_qdata_full (G_OBJECT (self), signal_check_context_quark,
|
|
ctx, (GDestroyNotify) signal_check_context_free);
|
|
}
|
|
|
|
g_assert (ctx);
|
|
return ctx;
|
|
}
|
|
|
|
static void periodic_signal_check_disable (MMIfaceModem *self,
|
|
gboolean clear);
|
|
static gboolean periodic_signal_check_cb (MMIfaceModem *self);
|
|
static void peridic_signal_check_step (MMIfaceModem *self);
|
|
|
|
static void
|
|
access_technologies_check_ready (MMIfaceModem *self,
|
|
GAsyncResult *res)
|
|
{
|
|
GError *error = NULL;
|
|
SignalCheckContext *ctx;
|
|
|
|
ctx = get_signal_check_context (self);
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies_finish (
|
|
self,
|
|
res,
|
|
&ctx->access_technologies,
|
|
&ctx->access_technologies_mask,
|
|
&error)) {
|
|
/* Did the plugin report that polling access technology is unsupported? */
|
|
if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
|
|
mm_obj_dbg (self, "polling to refresh access technologies is unsupported");
|
|
ctx->access_technology_polling_supported = FALSE;
|
|
}
|
|
/* Ignore logging any message if the error is in 'in-progress' */
|
|
else if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS))
|
|
mm_obj_dbg (self, "couldn't refresh access technologies: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
/* We may have been disabled while this command was running. */
|
|
else if (ctx->enabled)
|
|
mm_iface_modem_update_access_technologies (self, ctx->access_technologies, ctx->access_technologies_mask);
|
|
|
|
/* Go on */
|
|
ctx->running_step++;
|
|
peridic_signal_check_step (self);
|
|
}
|
|
|
|
static void
|
|
signal_quality_check_ready (MMIfaceModem *self,
|
|
GAsyncResult *res)
|
|
{
|
|
GError *error = NULL;
|
|
SignalCheckContext *ctx;
|
|
|
|
ctx = get_signal_check_context (self);
|
|
|
|
ctx->signal_quality = MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality_finish (self, res, &error);
|
|
if (error) {
|
|
/* Did the plugin report that polling signal quality is unsupported? */
|
|
if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
|
|
mm_obj_dbg (self, "polling to refresh signal quality is unsupported");
|
|
ctx->signal_quality_polling_supported = FALSE;
|
|
}
|
|
/* Ignore logging any message if the error is in 'in-progress' */
|
|
else if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS))
|
|
mm_obj_dbg (self, "couldn't refresh signal quality: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
/* We may have been disabled while this command was running. */
|
|
else if (ctx->enabled)
|
|
update_signal_quality (self, ctx->signal_quality, TRUE);
|
|
|
|
/* Go on */
|
|
ctx->running_step++;
|
|
peridic_signal_check_step (self);
|
|
}
|
|
|
|
static void
|
|
peridic_signal_check_step (MMIfaceModem *self)
|
|
{
|
|
SignalCheckContext *ctx;
|
|
|
|
ctx = get_signal_check_context (self);
|
|
|
|
switch (ctx->running_step) {
|
|
case SIGNAL_CHECK_STEP_NONE:
|
|
g_assert_not_reached ();
|
|
|
|
case SIGNAL_CHECK_STEP_FIRST:
|
|
ctx->running_step++;
|
|
/* fall-through */
|
|
|
|
case SIGNAL_CHECK_STEP_SIGNAL_QUALITY:
|
|
if (ctx->enabled && ctx->signal_quality_polling_supported &&
|
|
(!ctx->initial_check_done || !ctx->signal_quality_polling_disabled)) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality (
|
|
self, (GAsyncReadyCallback)signal_quality_check_ready, NULL);
|
|
return;
|
|
}
|
|
ctx->running_step++;
|
|
/* fall-through */
|
|
|
|
case SIGNAL_CHECK_STEP_ACCESS_TECHNOLOGIES:
|
|
if (ctx->enabled && ctx->access_technology_polling_supported &&
|
|
(!ctx->initial_check_done || !ctx->access_technology_polling_disabled)) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies (
|
|
self, (GAsyncReadyCallback)access_technologies_check_ready, NULL);
|
|
return;
|
|
}
|
|
ctx->running_step++;
|
|
/* fall-through */
|
|
|
|
case SIGNAL_CHECK_STEP_LAST:
|
|
/* Flag as sequence finished */
|
|
ctx->running_step = SIGNAL_CHECK_STEP_NONE;
|
|
|
|
/* If we have been disabled while we were running the steps, we don't
|
|
* do anything else. */
|
|
if (!ctx->enabled) {
|
|
mm_obj_dbg (self, "periodic signal quality and access technology checks not rescheduled: disabled");
|
|
return;
|
|
}
|
|
|
|
/* Schedule when we poll next time.
|
|
* Initially we poll at a higher frequency until we get valid signal
|
|
* quality and access technology values. As soon as we get them, OR if
|
|
* we made too many retries at a high frequency, we fallback to the
|
|
* slower polling. */
|
|
if (!ctx->initial_check_done) {
|
|
gboolean signal_quality_ready;
|
|
gboolean access_technology_ready;
|
|
|
|
/* Signal quality is ready if unsupported or if we got a valid
|
|
* value reported */
|
|
signal_quality_ready = (!ctx->signal_quality_polling_supported || (ctx->signal_quality != 0));
|
|
|
|
/* Access technology is ready if unsupported or if we got a valid
|
|
* value reported */
|
|
access_technology_ready = (!ctx->access_technology_polling_supported ||
|
|
((ctx->access_technologies & ctx->access_technologies_mask) != MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN));
|
|
|
|
ctx->initial_check_done = ((signal_quality_ready && access_technology_ready) || (--ctx->initial_retries == 0));
|
|
}
|
|
|
|
/* After running the initial check, if both signal quality and access tech
|
|
* loading are either disabled or unsupported, we'll stop polling completely,
|
|
* because they may be loaded asynchronously by unsolicited messages */
|
|
if (ctx->initial_check_done &&
|
|
(!ctx->signal_quality_polling_supported || ctx->signal_quality_polling_disabled) &&
|
|
(!ctx->access_technology_polling_supported || ctx->access_technology_polling_disabled)) {
|
|
mm_obj_dbg (self, "periodic signal quality and access technology checks not rescheduled: unneeded or unsupported");
|
|
periodic_signal_check_disable (self, FALSE);
|
|
return;
|
|
}
|
|
|
|
mm_obj_dbg (self, "periodic signal quality and access technology checks scheduled");
|
|
g_assert (!ctx->timeout_source);
|
|
ctx->timeout_source = g_timeout_add_seconds (ctx->initial_check_done ? SIGNAL_CHECK_TIMEOUT_SEC : SIGNAL_CHECK_INITIAL_TIMEOUT_SEC,
|
|
(GSourceFunc) periodic_signal_check_cb,
|
|
self);
|
|
return;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
periodic_signal_check_cb (MMIfaceModem *self)
|
|
{
|
|
SignalCheckContext *ctx;
|
|
|
|
ctx = get_signal_check_context (self);
|
|
g_assert (ctx->enabled);
|
|
|
|
/* Start the sequence */
|
|
ctx->running_step = SIGNAL_CHECK_STEP_FIRST;
|
|
ctx->signal_quality = 0;
|
|
ctx->access_technologies = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
|
|
ctx->access_technologies_mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY;
|
|
peridic_signal_check_step (self);
|
|
|
|
/* Remove the timeout and clear the source id */
|
|
if (ctx->timeout_source)
|
|
ctx->timeout_source = 0;
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_refresh_signal (MMIfaceModem *self)
|
|
{
|
|
SignalCheckContext *ctx;
|
|
|
|
/* Don't refresh polling if we're not enabled */
|
|
ctx = get_signal_check_context (self);
|
|
if (!ctx->enabled) {
|
|
mm_obj_dbg (self, "periodic signal check refresh ignored: checks not enabled");
|
|
return;
|
|
}
|
|
|
|
/* Don't refresh if we're already doing it */
|
|
if (ctx->running_step != SIGNAL_CHECK_STEP_NONE) {
|
|
mm_obj_dbg (self, "periodic signal check refresh ignored: check already running");
|
|
return;
|
|
}
|
|
|
|
mm_obj_dbg (self, "periodic signal check refresh requested");
|
|
|
|
/* Remove the scheduled timeout as we're going to refresh
|
|
* right away */
|
|
if (ctx->timeout_source) {
|
|
g_source_remove (ctx->timeout_source);
|
|
ctx->timeout_source = 0;
|
|
}
|
|
|
|
/* Reset refresh rate and initial retries when we're asked to refresh signal
|
|
* so that we poll at a higher frequency */
|
|
ctx->initial_retries = SIGNAL_CHECK_INITIAL_RETRIES;
|
|
ctx->initial_check_done = FALSE;
|
|
|
|
/* Start sequence */
|
|
periodic_signal_check_cb (self);
|
|
}
|
|
|
|
static void
|
|
periodic_signal_check_disable (MMIfaceModem *self,
|
|
gboolean clear)
|
|
{
|
|
SignalCheckContext *ctx;
|
|
|
|
ctx = get_signal_check_context (self);
|
|
if (!ctx->enabled)
|
|
return;
|
|
|
|
/* Clear access technology and signal quality */
|
|
if (clear) {
|
|
update_signal_quality (self, 0, FALSE);
|
|
mm_iface_modem_update_access_technologies (self,
|
|
MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN,
|
|
MM_MODEM_ACCESS_TECHNOLOGY_ANY);
|
|
}
|
|
|
|
/* Remove scheduled timeout */
|
|
if (ctx->timeout_source) {
|
|
g_source_remove (ctx->timeout_source);
|
|
ctx->timeout_source = 0;
|
|
}
|
|
|
|
ctx->enabled = FALSE;
|
|
mm_obj_dbg (self, "periodic signal checks disabled");
|
|
}
|
|
|
|
static void
|
|
periodic_signal_check_enable (MMIfaceModem *self)
|
|
{
|
|
SignalCheckContext *ctx;
|
|
|
|
ctx = get_signal_check_context (self);
|
|
|
|
/* If polling access technology and signal quality not supported, don't even
|
|
* bother trying. */
|
|
if (!ctx->signal_quality_polling_supported && !ctx->access_technology_polling_supported) {
|
|
mm_obj_dbg (self, "not enabling periodic signal checks: unsupported");
|
|
return;
|
|
}
|
|
|
|
/* Log and flag as enabled */
|
|
if (!ctx->enabled) {
|
|
mm_obj_dbg (self, "periodic signal checks enabled");
|
|
ctx->enabled = TRUE;
|
|
}
|
|
|
|
/* And refresh, which will trigger the first check at high frequency */
|
|
mm_iface_modem_refresh_signal (self);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
bearer_list_count_connected (MMBaseBearer *bearer,
|
|
guint *count)
|
|
{
|
|
if (mm_base_bearer_get_status (bearer) == MM_BEARER_STATUS_CONNECTED)
|
|
(*count)++;
|
|
}
|
|
|
|
static void
|
|
__iface_modem_update_state_internal (MMIfaceModem *self,
|
|
MMModemState new_state,
|
|
MMModemStateChangeReason reason,
|
|
MMModemStateFailedReason failed_reason)
|
|
{
|
|
MMModemState old_state = MM_MODEM_STATE_UNKNOWN;
|
|
MmGdbusModem *skeleton = NULL;
|
|
MMBearerList *bearer_list = NULL;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_STATE, &old_state,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
MM_IFACE_MODEM_BEARER_LIST, &bearer_list,
|
|
NULL);
|
|
|
|
if (!skeleton || !bearer_list) {
|
|
if (skeleton)
|
|
g_object_unref (skeleton);
|
|
if (bearer_list)
|
|
g_object_unref (bearer_list);
|
|
return;
|
|
}
|
|
|
|
/* While connected we don't want registration status changes to change
|
|
* the modem's state away from CONNECTED. */
|
|
if ((new_state == MM_MODEM_STATE_ENABLED ||
|
|
new_state == MM_MODEM_STATE_SEARCHING ||
|
|
new_state == MM_MODEM_STATE_REGISTERED) &&
|
|
bearer_list &&
|
|
old_state > MM_MODEM_STATE_REGISTERED) {
|
|
guint connected = 0;
|
|
|
|
mm_bearer_list_foreach (bearer_list,
|
|
(MMBearerListForeachFunc)bearer_list_count_connected,
|
|
&connected);
|
|
if (connected > 0)
|
|
/* Don't update state */
|
|
new_state = old_state;
|
|
}
|
|
|
|
/* Enabled may really be searching or registered */
|
|
if (new_state == MM_MODEM_STATE_ENABLED)
|
|
new_state = get_consolidated_subsystem_state (self);
|
|
|
|
/* Update state only if different */
|
|
if (new_state != old_state) {
|
|
mm_obj_info (self, "state changed (%s -> %s)",
|
|
mm_modem_state_get_string (old_state),
|
|
mm_modem_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_STATE, new_state,
|
|
NULL);
|
|
|
|
/* Signal status change */
|
|
if (skeleton) {
|
|
/* Set failure reason */
|
|
if (failed_reason != mm_gdbus_modem_get_state_failed_reason (skeleton))
|
|
mm_gdbus_modem_set_state_failed_reason (skeleton, failed_reason);
|
|
|
|
/* Flush current change before signaling the state change,
|
|
* so that clients get the proper state already in the
|
|
* state-changed callback */
|
|
g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton));
|
|
mm_gdbus_modem_emit_state_changed (skeleton,
|
|
old_state,
|
|
new_state,
|
|
reason);
|
|
}
|
|
|
|
/* If we go to a registered/connected state (from unregistered), setup
|
|
* signal quality and access technologies periodic retrieval */
|
|
if (new_state >= MM_MODEM_STATE_REGISTERED && old_state < MM_MODEM_STATE_REGISTERED)
|
|
periodic_signal_check_enable (self);
|
|
/* If we go from a registered/connected state to unregistered,
|
|
* cleanup signal quality retrieval */
|
|
else if (old_state >= MM_MODEM_STATE_REGISTERED && new_state < MM_MODEM_STATE_REGISTERED)
|
|
periodic_signal_check_disable (self, TRUE);
|
|
}
|
|
|
|
if (skeleton)
|
|
g_object_unref (skeleton);
|
|
if (bearer_list)
|
|
g_object_unref (bearer_list);
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_update_state (MMIfaceModem *self,
|
|
MMModemState new_state,
|
|
MMModemStateChangeReason reason)
|
|
{
|
|
if (new_state == MM_MODEM_STATE_FAILED) {
|
|
mm_iface_modem_update_failed_state (self, MM_MODEM_STATE_FAILED_REASON_UNKNOWN);
|
|
return;
|
|
}
|
|
|
|
__iface_modem_update_state_internal (self, new_state, reason, MM_MODEM_STATE_FAILED_REASON_NONE);
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_update_failed_state (MMIfaceModem *self,
|
|
MMModemStateFailedReason failed_reason)
|
|
{
|
|
__iface_modem_update_state_internal (self, MM_MODEM_STATE_FAILED, MM_MODEM_STATE_CHANGE_REASON_FAILURE, failed_reason);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
gchar *subsystem;
|
|
MMModemState state;
|
|
} SubsystemState;
|
|
|
|
static void
|
|
subsystem_state_array_free (GArray *array)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < array->len; i++) {
|
|
SubsystemState *s;
|
|
|
|
s = &g_array_index (array, SubsystemState, i);
|
|
g_free (s->subsystem);
|
|
}
|
|
|
|
g_array_free (array, TRUE);
|
|
}
|
|
|
|
static MMModemState
|
|
get_consolidated_subsystem_state (MMIfaceModem *self)
|
|
{
|
|
/* Use as initial state ENABLED, which is the minimum one expected. Do not
|
|
* use the old modem state as initial state, as that would disallow reporting
|
|
* e.g. ENABLED if the old state was REGISTERED (as ENABLED < REGISTERED). */
|
|
MMModemState consolidated = MM_MODEM_STATE_ENABLED;
|
|
GArray *subsystem_states;
|
|
|
|
if (G_UNLIKELY (!state_update_context_quark))
|
|
state_update_context_quark = (g_quark_from_static_string (
|
|
STATE_UPDATE_CONTEXT_TAG));
|
|
|
|
subsystem_states = g_object_get_qdata (G_OBJECT (self),
|
|
state_update_context_quark);
|
|
|
|
/* Build consolidated state, expected fixes are:
|
|
* - Enabled (meaning unregistered) --> Searching|Registered
|
|
* - Searching --> Registered
|
|
*/
|
|
if (subsystem_states) {
|
|
guint i;
|
|
|
|
for (i = 0; i < subsystem_states->len; i++) {
|
|
SubsystemState *s;
|
|
|
|
s = &g_array_index (subsystem_states, SubsystemState, i);
|
|
if (s->state > consolidated)
|
|
consolidated = s->state;
|
|
}
|
|
}
|
|
|
|
return consolidated;
|
|
}
|
|
|
|
static MMModemState
|
|
get_updated_consolidated_state (MMIfaceModem *self,
|
|
MMModemState modem_state,
|
|
const gchar *subsystem,
|
|
MMModemState subsystem_state)
|
|
{
|
|
guint i;
|
|
GArray *subsystem_states;
|
|
|
|
/* Reported subsystem states will be REGISTRATION-related. This means
|
|
* that we would only expect a subset of the states being reported for
|
|
* the subsystem. Warn if we get others */
|
|
g_warn_if_fail (subsystem_state == MM_MODEM_STATE_ENABLED ||
|
|
subsystem_state == MM_MODEM_STATE_SEARCHING ||
|
|
subsystem_state == MM_MODEM_STATE_REGISTERED);
|
|
|
|
if (G_UNLIKELY (!state_update_context_quark))
|
|
state_update_context_quark = (g_quark_from_static_string (
|
|
STATE_UPDATE_CONTEXT_TAG));
|
|
|
|
subsystem_states = g_object_get_qdata (G_OBJECT (self),
|
|
state_update_context_quark);
|
|
if (!subsystem_states) {
|
|
subsystem_states = g_array_sized_new (FALSE,
|
|
FALSE,
|
|
sizeof (SubsystemState),
|
|
2);
|
|
g_object_set_qdata_full (G_OBJECT (self),
|
|
state_update_context_quark,
|
|
subsystem_states,
|
|
(GDestroyNotify)subsystem_state_array_free);
|
|
}
|
|
|
|
/* Store new subsystem state */
|
|
for (i = 0; i < subsystem_states->len; i++) {
|
|
SubsystemState *s;
|
|
|
|
s = &g_array_index (subsystem_states, SubsystemState, i);
|
|
if (g_str_equal (s->subsystem, subsystem)) {
|
|
s->state = subsystem_state;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If not found, insert new element */
|
|
if (i == subsystem_states->len) {
|
|
SubsystemState s;
|
|
|
|
mm_obj_dbg (self, "will start keeping track of state for subsystem '%s'", subsystem);
|
|
s.subsystem = g_strdup (subsystem);
|
|
s.state = subsystem_state;
|
|
g_array_append_val (subsystem_states, s);
|
|
}
|
|
|
|
return get_consolidated_subsystem_state (self);
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_update_subsystem_state (MMIfaceModem *self,
|
|
const gchar *subsystem,
|
|
MMModemState new_state,
|
|
MMModemStateChangeReason reason)
|
|
{
|
|
MMModemState consolidated;
|
|
MMModemState state = MM_MODEM_STATE_UNKNOWN;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_STATE, &state,
|
|
NULL);
|
|
|
|
/* We may have different subsystems being handled (e.g. 3GPP and CDMA), and
|
|
* the registration status value is unique, so if we get subsystem-specific
|
|
* state updates, we'll need to merge all to get a consolidated one. */
|
|
consolidated = get_updated_consolidated_state (self, state, subsystem, new_state);
|
|
|
|
/* Don't update registration-related states while disabling/enabling */
|
|
if (state == MM_MODEM_STATE_ENABLING ||
|
|
state == MM_MODEM_STATE_DISABLING)
|
|
return;
|
|
|
|
mm_iface_modem_update_state (self, consolidated, reason);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
MmGdbusModem *skeleton;
|
|
GDBusMethodInvocation *invocation;
|
|
MMIfaceModem *self;
|
|
gboolean enable;
|
|
} HandleEnableContext;
|
|
|
|
static void
|
|
handle_enable_context_free (HandleEnableContext *ctx)
|
|
{
|
|
g_object_unref (ctx->skeleton);
|
|
g_object_unref (ctx->invocation);
|
|
g_object_unref (ctx->self);
|
|
g_free (ctx);
|
|
}
|
|
|
|
static void
|
|
enable_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
HandleEnableContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (ctx->enable) {
|
|
if (!mm_base_modem_enable_finish (self, res, &error))
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
else
|
|
mm_gdbus_modem_complete_enable (ctx->skeleton, ctx->invocation);
|
|
} else {
|
|
if (!mm_base_modem_disable_finish (self, res, &error))
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
else
|
|
mm_gdbus_modem_complete_enable (ctx->skeleton, ctx->invocation);
|
|
}
|
|
|
|
handle_enable_context_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_enable_auth_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
HandleEnableContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_base_modem_authorize_finish (self, res, &error)) {
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
handle_enable_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
if (abort_invocation_if_state_not_reached (ctx->self, ctx->invocation, MM_MODEM_STATE_LOCKED)) {
|
|
handle_enable_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
if (ctx->enable)
|
|
mm_base_modem_enable (self,
|
|
(GAsyncReadyCallback)enable_ready,
|
|
ctx);
|
|
else
|
|
mm_base_modem_disable (self,
|
|
(GAsyncReadyCallback)enable_ready,
|
|
ctx);
|
|
}
|
|
|
|
static gboolean
|
|
handle_enable (MmGdbusModem *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
gboolean enable,
|
|
MMIfaceModem *self)
|
|
{
|
|
HandleEnableContext *ctx;
|
|
|
|
ctx = g_new (HandleEnableContext, 1);
|
|
ctx->skeleton = g_object_ref (skeleton);
|
|
ctx->invocation = g_object_ref (invocation);
|
|
ctx->self = g_object_ref (self);
|
|
ctx->enable = enable;
|
|
|
|
mm_base_modem_authorize (MM_BASE_MODEM (self),
|
|
invocation,
|
|
MM_AUTHORIZATION_DEVICE_CONTROL,
|
|
(GAsyncReadyCallback)handle_enable_auth_ready,
|
|
ctx);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
MmGdbusModem *skeleton;
|
|
GDBusMethodInvocation *invocation;
|
|
MMIfaceModem *self;
|
|
MMModemPowerState power_state;
|
|
} HandleSetPowerStateContext;
|
|
|
|
static void
|
|
handle_set_power_state_context_free (HandleSetPowerStateContext *ctx)
|
|
{
|
|
g_object_unref (ctx->skeleton);
|
|
g_object_unref (ctx->invocation);
|
|
g_object_unref (ctx->self);
|
|
g_slice_free (HandleSetPowerStateContext, ctx);
|
|
}
|
|
|
|
static void
|
|
set_power_state_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
HandleSetPowerStateContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_iface_modem_set_power_state_finish (self, res, &error))
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
else
|
|
mm_gdbus_modem_complete_set_power_state (ctx->skeleton, ctx->invocation);
|
|
handle_set_power_state_context_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_set_power_state_auth_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
HandleSetPowerStateContext *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_set_power_state_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
/* Only 'off', 'low' or 'up' expected */
|
|
if (ctx->power_state != MM_MODEM_POWER_STATE_LOW &&
|
|
ctx->power_state != MM_MODEM_POWER_STATE_ON &&
|
|
ctx->power_state != MM_MODEM_POWER_STATE_OFF) {
|
|
g_dbus_method_invocation_return_error (ctx->invocation,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_INVALID_ARGS,
|
|
"Cannot set '%s' power state",
|
|
mm_modem_power_state_get_string (ctx->power_state));
|
|
handle_set_power_state_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
modem_state = MM_MODEM_STATE_UNKNOWN;
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_STATE, &modem_state,
|
|
NULL);
|
|
|
|
/* Going into LOW or ON only allowed in disabled state */
|
|
if ((ctx->power_state == MM_MODEM_POWER_STATE_LOW ||
|
|
ctx->power_state == MM_MODEM_POWER_STATE_ON) &&
|
|
modem_state != MM_MODEM_STATE_DISABLED) {
|
|
g_dbus_method_invocation_return_error (ctx->invocation,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_WRONG_STATE,
|
|
"Cannot set power state: not in disabled state");
|
|
handle_set_power_state_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
/* Going into OFF, only allowed if locked, disabled or failed */
|
|
if (ctx->power_state == MM_MODEM_POWER_STATE_OFF &&
|
|
modem_state != MM_MODEM_STATE_FAILED &&
|
|
modem_state != MM_MODEM_STATE_LOCKED &&
|
|
modem_state != MM_MODEM_STATE_DISABLED) {
|
|
g_dbus_method_invocation_return_error (ctx->invocation,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_WRONG_STATE,
|
|
"Cannot set power state: modem either enabled or initializing");
|
|
handle_set_power_state_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
mm_iface_modem_set_power_state (MM_IFACE_MODEM (self),
|
|
ctx->power_state,
|
|
(GAsyncReadyCallback)set_power_state_ready,
|
|
ctx);
|
|
}
|
|
|
|
static gboolean
|
|
handle_set_power_state (MmGdbusModem *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
guint32 power_state,
|
|
MMIfaceModem *self)
|
|
{
|
|
HandleSetPowerStateContext *ctx;
|
|
|
|
ctx = g_slice_new (HandleSetPowerStateContext);
|
|
ctx->skeleton = g_object_ref (skeleton);
|
|
ctx->invocation = g_object_ref (invocation);
|
|
ctx->self = g_object_ref (self);
|
|
ctx->power_state = (MMModemPowerState)power_state;
|
|
|
|
mm_base_modem_authorize (MM_BASE_MODEM (self),
|
|
invocation,
|
|
MM_AUTHORIZATION_DEVICE_CONTROL,
|
|
(GAsyncReadyCallback)handle_set_power_state_auth_ready,
|
|
ctx);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
MmGdbusModem *skeleton;
|
|
GDBusMethodInvocation *invocation;
|
|
MMIfaceModem *self;
|
|
} HandleResetContext;
|
|
|
|
static void
|
|
handle_reset_context_free (HandleResetContext *ctx)
|
|
{
|
|
g_object_unref (ctx->skeleton);
|
|
g_object_unref (ctx->invocation);
|
|
g_object_unref (ctx->self);
|
|
g_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_reset_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
HandleResetContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->reset_finish (self, res, &error))
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
else
|
|
mm_gdbus_modem_complete_reset (ctx->skeleton, ctx->invocation);
|
|
|
|
handle_reset_context_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_reset_auth_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
HandleResetContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_base_modem_authorize_finish (self, res, &error)) {
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
handle_reset_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
/* If reseting is not implemented, report an error */
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->reset ||
|
|
!MM_IFACE_MODEM_GET_INTERFACE (self)->reset_finish) {
|
|
g_dbus_method_invocation_return_error (ctx->invocation,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNSUPPORTED,
|
|
"Cannot reset the modem: operation not supported");
|
|
handle_reset_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->reset (MM_IFACE_MODEM (self),
|
|
(GAsyncReadyCallback)handle_reset_ready,
|
|
ctx);
|
|
}
|
|
|
|
static gboolean
|
|
handle_reset (MmGdbusModem *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
MMIfaceModem *self)
|
|
{
|
|
HandleResetContext *ctx;
|
|
|
|
ctx = g_new (HandleResetContext, 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_reset_auth_ready,
|
|
ctx);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
MmGdbusModem *skeleton;
|
|
GDBusMethodInvocation *invocation;
|
|
MMIfaceModem *self;
|
|
gchar *code;
|
|
} HandleFactoryResetContext;
|
|
|
|
static void
|
|
handle_factory_reset_context_free (HandleFactoryResetContext *ctx)
|
|
{
|
|
g_object_unref (ctx->skeleton);
|
|
g_object_unref (ctx->invocation);
|
|
g_object_unref (ctx->self);
|
|
g_free (ctx->code);
|
|
g_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_factory_reset_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
HandleFactoryResetContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->factory_reset_finish (self, res, &error))
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
else
|
|
mm_gdbus_modem_complete_factory_reset (ctx->skeleton, ctx->invocation);
|
|
|
|
handle_factory_reset_context_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_factory_reset_auth_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
HandleFactoryResetContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_base_modem_authorize_finish (self, res, &error)) {
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
handle_factory_reset_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
/* If reseting is not implemented, report an error */
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->factory_reset ||
|
|
!MM_IFACE_MODEM_GET_INTERFACE (self)->factory_reset_finish) {
|
|
g_dbus_method_invocation_return_error (ctx->invocation,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNSUPPORTED,
|
|
"Cannot reset the modem to factory defaults: "
|
|
"operation not supported");
|
|
handle_factory_reset_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->factory_reset (MM_IFACE_MODEM (self),
|
|
ctx->code,
|
|
(GAsyncReadyCallback)handle_factory_reset_ready,
|
|
ctx);
|
|
}
|
|
|
|
static gboolean
|
|
handle_factory_reset (MmGdbusModem *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
const gchar *code,
|
|
MMIfaceModem *self)
|
|
{
|
|
HandleFactoryResetContext *ctx;
|
|
|
|
ctx = g_new (HandleFactoryResetContext, 1);
|
|
ctx->skeleton = g_object_ref (skeleton);
|
|
ctx->invocation = g_object_ref (invocation);
|
|
ctx->self = g_object_ref (self);
|
|
ctx->code = g_strdup (code);
|
|
|
|
mm_base_modem_authorize (MM_BASE_MODEM (self),
|
|
invocation,
|
|
MM_AUTHORIZATION_DEVICE_CONTROL,
|
|
(GAsyncReadyCallback)handle_factory_reset_auth_ready,
|
|
ctx);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Current capabilities setting
|
|
*
|
|
* Setting capabilities allowed also in FAILED state. Just imagine a
|
|
* 3GPP+3GPP2 modem in 3GPP-only mode without SIM, we should allow
|
|
* changing caps to 3GPP2, which doesn't require SIM
|
|
*/
|
|
|
|
typedef struct {
|
|
MmGdbusModem *skeleton;
|
|
GDBusMethodInvocation *invocation;
|
|
MMIfaceModem *self;
|
|
MMModemCapability capabilities;
|
|
} HandleSetCurrentCapabilitiesContext;
|
|
|
|
static void
|
|
handle_set_current_capabilities_context_free (HandleSetCurrentCapabilitiesContext *ctx)
|
|
{
|
|
g_object_unref (ctx->skeleton);
|
|
g_object_unref (ctx->invocation);
|
|
g_object_unref (ctx->self);
|
|
g_slice_free (HandleSetCurrentCapabilitiesContext, ctx);
|
|
}
|
|
|
|
static void
|
|
set_current_capabilities_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
HandleSetCurrentCapabilitiesContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_capabilities_finish (self, res, &error))
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
else {
|
|
/* Capabilities updated: explicitly refresh signal and access technology */
|
|
mm_iface_modem_refresh_signal (self);
|
|
mm_gdbus_modem_complete_set_current_capabilities (ctx->skeleton, ctx->invocation);
|
|
}
|
|
|
|
handle_set_current_capabilities_context_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_set_current_capabilities_auth_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
HandleSetCurrentCapabilitiesContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
gchar *capabilities_string;
|
|
GArray *supported;
|
|
gboolean matched = FALSE;
|
|
guint i;
|
|
|
|
if (!mm_base_modem_authorize_finish (self, res, &error)) {
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
handle_set_current_capabilities_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
/* Get list of supported capabilities */
|
|
supported = mm_common_capability_combinations_variant_to_garray (
|
|
mm_gdbus_modem_get_supported_capabilities (ctx->skeleton));
|
|
|
|
/* Don't allow capability switching if only one item given in the supported list */
|
|
if (supported->len == 1) {
|
|
g_dbus_method_invocation_return_error (ctx->invocation,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNSUPPORTED,
|
|
"Cannot change capabilities: only one combination supported");
|
|
handle_set_current_capabilities_context_free (ctx);
|
|
g_array_unref (supported);
|
|
return;
|
|
}
|
|
|
|
/* Check if the given combination is supported */
|
|
for (i = 0; !matched && i < supported->len; i++) {
|
|
MMModemCapability supported_capability;
|
|
|
|
supported_capability = g_array_index (supported, MMModemCapability, i);
|
|
if (supported_capability == ctx->capabilities)
|
|
matched = TRUE;
|
|
}
|
|
g_array_unref (supported);
|
|
|
|
if (!matched) {
|
|
g_dbus_method_invocation_return_error (ctx->invocation,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNSUPPORTED,
|
|
"The given combination of capabilities is not supported");
|
|
handle_set_current_capabilities_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
/* Check if we already are in the requested setup */
|
|
if (mm_gdbus_modem_get_current_capabilities (ctx->skeleton) == ctx->capabilities) {
|
|
/* Nothing to do */
|
|
mm_gdbus_modem_complete_set_current_capabilities (ctx->skeleton, ctx->invocation);
|
|
handle_set_current_capabilities_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
/* If setting current capabilities is not implemented, report an error */
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_capabilities ||
|
|
!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_capabilities_finish) {
|
|
g_dbus_method_invocation_return_error (ctx->invocation,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNSUPPORTED,
|
|
"Setting current capabilities not supported");
|
|
handle_set_current_capabilities_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
capabilities_string = mm_modem_capability_build_string_from_mask (ctx->capabilities);
|
|
mm_obj_dbg (self, "setting new list of capabilities: %s", capabilities_string);
|
|
g_free (capabilities_string);
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_capabilities (
|
|
MM_IFACE_MODEM (self),
|
|
ctx->capabilities,
|
|
(GAsyncReadyCallback)set_current_capabilities_ready,
|
|
ctx);
|
|
}
|
|
|
|
static gboolean
|
|
handle_set_current_capabilities (MmGdbusModem *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
guint capabilities,
|
|
MMIfaceModem *self)
|
|
{
|
|
HandleSetCurrentCapabilitiesContext *ctx;
|
|
|
|
ctx = g_slice_new (HandleSetCurrentCapabilitiesContext);
|
|
ctx->skeleton = g_object_ref (skeleton);
|
|
ctx->invocation = g_object_ref (invocation);
|
|
ctx->self = g_object_ref (self);
|
|
ctx->capabilities = capabilities;
|
|
|
|
mm_base_modem_authorize (MM_BASE_MODEM (self),
|
|
invocation,
|
|
MM_AUTHORIZATION_DEVICE_CONTROL,
|
|
(GAsyncReadyCallback)handle_set_current_capabilities_auth_ready,
|
|
ctx);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Current bands setting */
|
|
|
|
#define AFTER_SET_LOAD_CURRENT_BANDS_RETRIES 5
|
|
#define AFTER_SET_LOAD_CURRENT_BANDS_TIMEOUT_SECS 1
|
|
|
|
typedef struct {
|
|
MmGdbusModem *skeleton;
|
|
GArray *bands_array;
|
|
GArray *supported_bands_array; /* when ANY requested */
|
|
guint retries;
|
|
} SetCurrentBandsContext;
|
|
|
|
static void
|
|
set_current_bands_context_free (SetCurrentBandsContext *ctx)
|
|
{
|
|
if (ctx->skeleton)
|
|
g_object_unref (ctx->skeleton);
|
|
if (ctx->bands_array)
|
|
g_array_unref (ctx->bands_array);
|
|
if (ctx->supported_bands_array)
|
|
g_array_unref (ctx->supported_bands_array);
|
|
g_slice_free (SetCurrentBandsContext, ctx);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_set_current_bands_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
set_current_bands_complete_with_defaults (GTask *task)
|
|
{
|
|
SetCurrentBandsContext *ctx;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
/* Never show just 'any' in the interface */
|
|
if (ctx->bands_array->len == 1 && g_array_index (ctx->bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
|
|
g_assert (ctx->supported_bands_array);
|
|
g_array_unref (ctx->bands_array);
|
|
ctx->bands_array = g_array_ref (ctx->supported_bands_array);
|
|
}
|
|
|
|
mm_common_bands_garray_sort (ctx->bands_array);
|
|
mm_gdbus_modem_set_current_bands (ctx->skeleton, mm_common_bands_garray_to_variant (ctx->bands_array));
|
|
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void set_current_bands_reload_schedule (GTask *task);
|
|
|
|
static void
|
|
after_set_load_current_bands_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
SetCurrentBandsContext *ctx;
|
|
GArray *current_bands;
|
|
GError *error = NULL;
|
|
GArray *requested_bands = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
current_bands = MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands_finish (self, res, &error);
|
|
if (!current_bands) {
|
|
/* If we can retry, do it */
|
|
if (ctx->retries > 0) {
|
|
mm_obj_dbg (self, "couldn't load current bands: %s (will retry)", error->message);
|
|
g_clear_error (&error);
|
|
set_current_bands_reload_schedule (task);
|
|
goto out;
|
|
}
|
|
|
|
/* Errors when reloading bands won't be critical */
|
|
mm_obj_warn (self, "couldn't load current bands: %s", error->message);
|
|
g_clear_error (&error);
|
|
set_current_bands_complete_with_defaults (task);
|
|
goto out;
|
|
}
|
|
|
|
if ((ctx->bands_array->len == 1) && (g_array_index (ctx->bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY))
|
|
requested_bands = g_array_ref (ctx->supported_bands_array);
|
|
else
|
|
requested_bands = g_array_ref (ctx->bands_array);
|
|
|
|
/* Compare arrays */
|
|
if (!mm_common_bands_garray_cmp (current_bands, requested_bands)) {
|
|
gchar *requested_str;
|
|
gchar *current_str;
|
|
|
|
/* If we can retry, do it */
|
|
if (ctx->retries > 0) {
|
|
mm_obj_dbg (self, "reloaded current bands different to the requested ones (will retry)");
|
|
set_current_bands_reload_schedule (task);
|
|
goto out;
|
|
}
|
|
|
|
requested_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)requested_bands->data, requested_bands->len);
|
|
current_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)current_bands->data, current_bands->len);
|
|
error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"reloaded current bands (%s) different to the requested ones (%s)",
|
|
current_str, requested_str);
|
|
g_free (requested_str);
|
|
g_free (current_str);
|
|
}
|
|
|
|
/* Store as current the last loaded ones and set operation result */
|
|
mm_common_bands_garray_sort (current_bands);
|
|
mm_gdbus_modem_set_current_bands (ctx->skeleton, mm_common_bands_garray_to_variant (current_bands));
|
|
|
|
if (error)
|
|
g_task_return_error (task, error);
|
|
else
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
|
|
out:
|
|
g_array_unref (requested_bands);
|
|
g_array_unref (current_bands);
|
|
}
|
|
|
|
static gboolean
|
|
set_current_bands_reload (GTask *task)
|
|
{
|
|
MMIfaceModem *self;
|
|
SetCurrentBandsContext *ctx;
|
|
|
|
self = g_task_get_source_object (task);
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
g_assert (ctx->retries > 0);
|
|
ctx->retries--;
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands (
|
|
self,
|
|
(GAsyncReadyCallback)after_set_load_current_bands_ready,
|
|
task);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
set_current_bands_reload_schedule (GTask *task)
|
|
{
|
|
g_timeout_add_seconds (AFTER_SET_LOAD_CURRENT_BANDS_TIMEOUT_SECS,
|
|
(GSourceFunc) set_current_bands_reload,
|
|
task);
|
|
}
|
|
|
|
static void
|
|
set_current_bands_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_bands_finish (self, res, &error)) {
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands_finish) {
|
|
set_current_bands_reload (task);
|
|
return;
|
|
}
|
|
|
|
/* Default to the ones we requested */
|
|
set_current_bands_complete_with_defaults (task);
|
|
}
|
|
|
|
static gboolean
|
|
validate_bands (const GArray *supported_bands_array,
|
|
const GArray *bands_array,
|
|
GError **error)
|
|
{
|
|
/* When the array has more than one element, there MUST NOT include ANY or
|
|
* UNKNOWN */
|
|
if (bands_array->len > 1) {
|
|
guint i;
|
|
|
|
for (i = 0; i < bands_array->len; i++) {
|
|
MMModemBand band;
|
|
|
|
band = g_array_index (bands_array, MMModemBand, i);
|
|
if (band == MM_MODEM_BAND_UNKNOWN ||
|
|
band == MM_MODEM_BAND_ANY) {
|
|
g_set_error (error,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_INVALID_ARGS,
|
|
"Wrong list of bands: "
|
|
"'%s' should have been the only element in the list",
|
|
mm_modem_band_get_string (band));
|
|
return FALSE;
|
|
}
|
|
|
|
if (supported_bands_array->len > 1 ||
|
|
(g_array_index (supported_bands_array, MMModemBand, 0) != MM_MODEM_BAND_ANY &&
|
|
g_array_index (supported_bands_array, MMModemBand, 0) != MM_MODEM_BAND_UNKNOWN)) {
|
|
gboolean found = FALSE;
|
|
guint j;
|
|
|
|
/* The band given in allowed MUST be available in supported */
|
|
for (j = 0; !found && j < supported_bands_array->len; j++) {
|
|
if (band == g_array_index (supported_bands_array, MMModemBand, j))
|
|
found = TRUE;
|
|
}
|
|
|
|
if (!found) {
|
|
gchar *supported_bands_str;
|
|
|
|
supported_bands_str = (mm_common_build_bands_string (
|
|
(const MMModemBand *)(gconstpointer)supported_bands_array->data,
|
|
supported_bands_array->len));
|
|
g_set_error (error,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_INVALID_ARGS,
|
|
"Given allowed band (%s) is not supported (%s)",
|
|
mm_modem_band_get_string (band),
|
|
supported_bands_str);
|
|
g_free (supported_bands_str);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_set_current_bands (MMIfaceModem *self,
|
|
GArray *bands_array,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
SetCurrentBandsContext *ctx;
|
|
GArray *current_bands_array;
|
|
GError *error = NULL;
|
|
gchar *bands_string;
|
|
GTask *task;
|
|
|
|
/* If setting allowed bands is not implemented, report an error */
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_bands ||
|
|
!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_bands_finish) {
|
|
g_task_report_new_error (self,
|
|
callback,
|
|
user_data,
|
|
mm_iface_modem_set_current_bands,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNSUPPORTED,
|
|
"Setting allowed bands not supported");
|
|
return;
|
|
}
|
|
|
|
/* Setup context */
|
|
ctx = g_slice_new0 (SetCurrentBandsContext);
|
|
ctx->retries = AFTER_SET_LOAD_CURRENT_BANDS_RETRIES;
|
|
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_bands_context_free);
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_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;
|
|
}
|
|
|
|
bands_string = mm_common_build_bands_string ((const MMModemBand *)(gpointer)bands_array->data, bands_array->len);
|
|
|
|
/* Get list of supported bands */
|
|
ctx->supported_bands_array = (mm_common_bands_variant_to_garray (
|
|
mm_gdbus_modem_get_supported_bands (ctx->skeleton)));
|
|
|
|
/* Set ctx->bands_array to target list of bands before comparing with current list
|
|
* of bands. If input list of bands contains only ANY, target list of bands is set
|
|
* to list of supported bands excluding ANY. */
|
|
if (bands_array->len == 1 &&
|
|
g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
|
|
guint i;
|
|
|
|
for (i = 0; i < ctx->supported_bands_array->len; i++) {
|
|
MMModemBand band = g_array_index (ctx->supported_bands_array, MMModemBand, i);
|
|
|
|
if (band != MM_MODEM_BAND_ANY &&
|
|
band != MM_MODEM_BAND_UNKNOWN) {
|
|
if (!ctx->bands_array)
|
|
ctx->bands_array = g_array_sized_new (FALSE,
|
|
FALSE,
|
|
sizeof (MMModemBand),
|
|
ctx->supported_bands_array->len);
|
|
g_array_append_val (ctx->bands_array, band);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ctx->bands_array)
|
|
ctx->bands_array = g_array_ref (bands_array);
|
|
|
|
/* Simply return if target list of bands equals to current list of bands */
|
|
current_bands_array = (mm_common_bands_variant_to_garray (
|
|
mm_gdbus_modem_get_current_bands (ctx->skeleton)));
|
|
if (mm_common_bands_garray_cmp (ctx->bands_array, current_bands_array)) {
|
|
mm_obj_dbg (self, "requested list of bands (%s) is equal to the current ones, skipping re-set", bands_string);
|
|
g_free (bands_string);
|
|
g_array_unref (current_bands_array);
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
/* Done comparison with current list of bands. Always use input list of bands
|
|
* when setting bands */
|
|
if (ctx->bands_array != bands_array) {
|
|
g_array_unref (ctx->bands_array);
|
|
ctx->bands_array = g_array_ref (bands_array);
|
|
}
|
|
|
|
/* Validate input list of bands */
|
|
if (!validate_bands (ctx->supported_bands_array,
|
|
ctx->bands_array,
|
|
&error)) {
|
|
mm_obj_dbg (self, "requested list of bands (%s) cannot be handled", bands_string);
|
|
g_free (bands_string);
|
|
g_array_unref (current_bands_array);
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
mm_obj_dbg (self, "setting new list of bands: %s", bands_string);
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_bands (
|
|
self,
|
|
ctx->bands_array,
|
|
(GAsyncReadyCallback)set_current_bands_ready,
|
|
task);
|
|
|
|
g_array_unref (current_bands_array);
|
|
g_free (bands_string);
|
|
}
|
|
|
|
typedef struct {
|
|
MmGdbusModem *skeleton;
|
|
GDBusMethodInvocation *invocation;
|
|
MMIfaceModem *self;
|
|
GVariant *bands;
|
|
} HandleSetCurrentBandsContext;
|
|
|
|
static void
|
|
handle_set_current_bands_context_free (HandleSetCurrentBandsContext *ctx)
|
|
{
|
|
g_variant_unref (ctx->bands);
|
|
g_object_unref (ctx->skeleton);
|
|
g_object_unref (ctx->invocation);
|
|
g_object_unref (ctx->self);
|
|
g_slice_free (HandleSetCurrentBandsContext, ctx);
|
|
}
|
|
|
|
static void
|
|
handle_set_current_bands_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
HandleSetCurrentBandsContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_iface_modem_set_current_bands_finish (self, res, &error))
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
else {
|
|
/* Bands updated: explicitly refresh signal and access technology */
|
|
mm_iface_modem_refresh_signal (self);
|
|
mm_gdbus_modem_complete_set_current_bands (ctx->skeleton, ctx->invocation);
|
|
}
|
|
|
|
handle_set_current_bands_context_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_set_current_bands_auth_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
HandleSetCurrentBandsContext *ctx)
|
|
{
|
|
GArray *bands_array;
|
|
GError *error = NULL;
|
|
|
|
if (!mm_base_modem_authorize_finish (self, res, &error)) {
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
handle_set_current_bands_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
if (abort_invocation_if_state_not_reached (ctx->self, ctx->invocation, MM_MODEM_STATE_DISABLED)) {
|
|
handle_set_current_bands_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
bands_array = mm_common_bands_variant_to_garray (ctx->bands);
|
|
mm_iface_modem_set_current_bands (MM_IFACE_MODEM (self),
|
|
bands_array,
|
|
(GAsyncReadyCallback)handle_set_current_bands_ready,
|
|
ctx);
|
|
g_array_unref (bands_array);
|
|
}
|
|
|
|
static gboolean
|
|
handle_set_current_bands (MmGdbusModem *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
GVariant *bands_variant,
|
|
MMIfaceModem *self)
|
|
{
|
|
HandleSetCurrentBandsContext *ctx;
|
|
|
|
ctx = g_slice_new (HandleSetCurrentBandsContext);
|
|
ctx->skeleton = g_object_ref (skeleton);
|
|
ctx->invocation = g_object_ref (invocation);
|
|
ctx->self = g_object_ref (self);
|
|
ctx->bands = g_variant_ref (bands_variant);
|
|
|
|
mm_base_modem_authorize (MM_BASE_MODEM (self),
|
|
invocation,
|
|
MM_AUTHORIZATION_DEVICE_CONTROL,
|
|
(GAsyncReadyCallback)handle_set_current_bands_auth_ready,
|
|
ctx);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Set current modes */
|
|
|
|
#define AFTER_SET_LOAD_CURRENT_MODES_RETRIES 5
|
|
#define AFTER_SET_LOAD_CURRENT_MODES_TIMEOUT_SECS 1
|
|
|
|
typedef struct {
|
|
MmGdbusModem *skeleton;
|
|
MMModemMode allowed;
|
|
MMModemMode preferred;
|
|
guint retries;
|
|
} SetCurrentModesContext;
|
|
|
|
static void
|
|
set_current_modes_context_free (SetCurrentModesContext *ctx)
|
|
{
|
|
if (ctx->skeleton)
|
|
g_object_unref (ctx->skeleton);
|
|
g_slice_free (SetCurrentModesContext, ctx);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_set_current_modes_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void set_current_modes_reload_schedule (GTask *task);
|
|
|
|
static void
|
|
after_set_load_current_modes_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
SetCurrentModesContext *ctx;
|
|
MMModemMode allowed = MM_MODEM_MODE_NONE;
|
|
MMModemMode preferred = MM_MODEM_MODE_NONE;
|
|
GError *error = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes_finish (self,
|
|
res,
|
|
&allowed,
|
|
&preferred,
|
|
&error)) {
|
|
/* If we can retry, do it */
|
|
if (ctx->retries > 0) {
|
|
mm_obj_dbg (self, "couldn't load current allowed/preferred modes: %s", error->message);
|
|
g_error_free (error);
|
|
set_current_modes_reload_schedule (task);
|
|
return;
|
|
}
|
|
|
|
/* Errors when getting allowed/preferred won't be critical */
|
|
mm_obj_warn (self, "couldn't load current allowed/preferred modes: %s", error->message);
|
|
g_clear_error (&error);
|
|
|
|
/* If errors getting allowed modes, default to the ones we asked for */
|
|
mm_gdbus_modem_set_current_modes (ctx->skeleton, g_variant_new ("(uu)", ctx->allowed, ctx->preferred));
|
|
goto out;
|
|
}
|
|
|
|
/* Store as current the last loaded ones and set operation result */
|
|
mm_gdbus_modem_set_current_modes (ctx->skeleton, g_variant_new ("(uu)", allowed, preferred));
|
|
|
|
/* Compare modes. If the requested one was ANY, we won't consider an error if the
|
|
* result differs. */
|
|
if (((allowed != ctx->allowed) || (preferred != ctx->preferred)) && (ctx->allowed != MM_MODEM_MODE_ANY)) {
|
|
gchar *requested_allowed_str;
|
|
gchar *requested_preferred_str;
|
|
gchar *current_allowed_str;
|
|
gchar *current_preferred_str;
|
|
|
|
/* If we can retry, do it */
|
|
if (ctx->retries > 0) {
|
|
mm_obj_dbg (self, "reloaded current modes different to the requested ones (will retry)");
|
|
set_current_modes_reload_schedule (task);
|
|
return;
|
|
}
|
|
|
|
requested_allowed_str = mm_modem_mode_build_string_from_mask (ctx->allowed);
|
|
requested_preferred_str = mm_modem_mode_build_string_from_mask (ctx->preferred);
|
|
current_allowed_str = mm_modem_mode_build_string_from_mask (allowed);
|
|
current_preferred_str = mm_modem_mode_build_string_from_mask (preferred);
|
|
|
|
error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"reloaded modes (allowed '%s' and preferred '%s') different "
|
|
"to the requested ones (allowed '%s' and preferred '%s')",
|
|
requested_allowed_str, requested_preferred_str,
|
|
current_allowed_str, current_preferred_str);
|
|
|
|
g_free (requested_allowed_str);
|
|
g_free (requested_preferred_str);
|
|
g_free (current_allowed_str);
|
|
g_free (current_preferred_str);
|
|
}
|
|
|
|
out:
|
|
if (error)
|
|
g_task_return_error (task, error);
|
|
else
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static gboolean
|
|
set_current_modes_reload (GTask *task)
|
|
{
|
|
MMIfaceModem *self;
|
|
SetCurrentModesContext *ctx;
|
|
|
|
self = g_task_get_source_object (task);
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
g_assert (ctx->retries > 0);
|
|
ctx->retries--;
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes (
|
|
self,
|
|
(GAsyncReadyCallback)after_set_load_current_modes_ready,
|
|
task);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
set_current_modes_reload_schedule (GTask *task)
|
|
{
|
|
g_timeout_add_seconds (AFTER_SET_LOAD_CURRENT_MODES_TIMEOUT_SECS,
|
|
(GSourceFunc) set_current_modes_reload,
|
|
task);
|
|
}
|
|
|
|
static void
|
|
set_current_modes_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
SetCurrentModesContext *ctx;
|
|
GError *error = NULL;
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_modes_finish (self, res, &error)) {
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes_finish) {
|
|
set_current_modes_reload (task);
|
|
return;
|
|
}
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
/* Default to the ones we requested */
|
|
mm_gdbus_modem_set_current_modes (ctx->skeleton,
|
|
g_variant_new ("(uu)",
|
|
ctx->allowed,
|
|
ctx->preferred));
|
|
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_set_current_modes (MMIfaceModem *self,
|
|
MMModemMode allowed,
|
|
MMModemMode preferred,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GArray *supported;
|
|
SetCurrentModesContext *ctx;
|
|
MMModemMode current_allowed = MM_MODEM_MODE_ANY;
|
|
MMModemMode current_preferred = MM_MODEM_MODE_NONE;
|
|
guint i;
|
|
GTask *task;
|
|
|
|
/* If setting allowed modes is not implemented, report an error */
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_modes ||
|
|
!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_modes_finish) {
|
|
g_task_report_new_error (self,
|
|
callback,
|
|
user_data,
|
|
mm_iface_modem_set_current_modes,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNSUPPORTED,
|
|
"Setting allowed modes not supported");
|
|
return;
|
|
}
|
|
|
|
/* Setup context */
|
|
ctx = g_slice_new0 (SetCurrentModesContext);
|
|
ctx->retries = AFTER_SET_LOAD_CURRENT_MODES_RETRIES;
|
|
ctx->allowed = allowed;
|
|
ctx->preferred = preferred;
|
|
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_modes_context_free);
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_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;
|
|
}
|
|
|
|
/* Get list of supported modes */
|
|
supported = mm_common_mode_combinations_variant_to_garray (
|
|
mm_gdbus_modem_get_supported_modes (ctx->skeleton));
|
|
|
|
/* Don't allow mode switching if only one item given in the supported list */
|
|
if (supported->len == 1) {
|
|
g_task_return_new_error (task,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNSUPPORTED,
|
|
"Cannot change modes: only one combination supported");
|
|
g_object_unref (task);
|
|
g_array_unref (supported);
|
|
return;
|
|
}
|
|
|
|
if (allowed == MM_MODEM_MODE_ANY &&
|
|
preferred == MM_MODEM_MODE_NONE) {
|
|
/* Allow allowed=ANY & preferred=NONE, all plugins should support it */
|
|
} else {
|
|
gboolean matched = FALSE;
|
|
|
|
/* Check if the given combination is supported */
|
|
for (i = 0; !matched && i < supported->len; i++) {
|
|
MMModemModeCombination *supported_mode;
|
|
|
|
supported_mode = &g_array_index (supported, MMModemModeCombination, i);
|
|
if ((supported_mode->allowed == MM_MODEM_MODE_ANY &&
|
|
supported_mode->preferred == MM_MODEM_MODE_NONE) ||
|
|
(supported_mode->allowed == allowed &&
|
|
supported_mode->preferred == preferred)) {
|
|
matched = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!matched) {
|
|
g_task_return_new_error (task,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNSUPPORTED,
|
|
"The given combination of allowed and preferred modes is not supported");
|
|
g_object_unref (task);
|
|
g_array_unref (supported);
|
|
return;
|
|
}
|
|
}
|
|
|
|
g_array_unref (supported);
|
|
|
|
/* Check if we already are in the requested setup */
|
|
g_variant_get (mm_gdbus_modem_get_current_modes (ctx->skeleton),
|
|
"(uu)",
|
|
¤t_allowed,
|
|
¤t_preferred);
|
|
if (current_allowed == allowed &&
|
|
current_preferred == preferred) {
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
/* Ensure preferred, if given, is a subset of allowed */
|
|
if ((allowed ^ preferred) & preferred) {
|
|
gchar *preferred_str;
|
|
gchar *allowed_str;
|
|
|
|
preferred_str = mm_modem_mode_build_string_from_mask (preferred);
|
|
allowed_str = mm_modem_mode_build_string_from_mask (allowed);
|
|
g_task_return_new_error (task,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNSUPPORTED,
|
|
"Preferred mode (%s) is not allowed (%s)",
|
|
preferred_str,
|
|
allowed_str);
|
|
g_object_unref (task);
|
|
g_free (preferred_str);
|
|
g_free (allowed_str);
|
|
return;
|
|
}
|
|
|
|
ctx->allowed = allowed;
|
|
ctx->preferred = preferred;
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_modes (self,
|
|
allowed,
|
|
preferred,
|
|
(GAsyncReadyCallback)set_current_modes_ready,
|
|
task);
|
|
}
|
|
|
|
typedef struct {
|
|
MmGdbusModem *skeleton;
|
|
GDBusMethodInvocation *invocation;
|
|
MMIfaceModem *self;
|
|
MMModemMode allowed;
|
|
MMModemMode preferred;
|
|
} HandleSetCurrentModesContext;
|
|
|
|
static void
|
|
handle_set_current_modes_context_free (HandleSetCurrentModesContext *ctx)
|
|
{
|
|
g_object_unref (ctx->skeleton);
|
|
g_object_unref (ctx->invocation);
|
|
g_object_unref (ctx->self);
|
|
g_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_set_current_modes_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
HandleSetCurrentModesContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_iface_modem_set_current_modes_finish (self, res, &error))
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
else {
|
|
/* Modes updated: explicitly refresh signal and access technology */
|
|
mm_iface_modem_refresh_signal (self);
|
|
mm_gdbus_modem_complete_set_current_modes (ctx->skeleton, ctx->invocation);
|
|
}
|
|
|
|
handle_set_current_modes_context_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_set_current_modes_auth_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
HandleSetCurrentModesContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_base_modem_authorize_finish (self, res, &error)) {
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
handle_set_current_modes_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
if (abort_invocation_if_state_not_reached (ctx->self, ctx->invocation, MM_MODEM_STATE_DISABLED)) {
|
|
handle_set_current_modes_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
mm_iface_modem_set_current_modes (MM_IFACE_MODEM (self),
|
|
ctx->allowed,
|
|
ctx->preferred,
|
|
(GAsyncReadyCallback)handle_set_current_modes_ready,
|
|
ctx);
|
|
}
|
|
|
|
static gboolean
|
|
handle_set_current_modes (MmGdbusModem *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
GVariant *variant,
|
|
MMIfaceModem *self)
|
|
{
|
|
HandleSetCurrentModesContext *ctx;
|
|
|
|
ctx = g_new (HandleSetCurrentModesContext, 1);
|
|
ctx->skeleton = g_object_ref (skeleton);
|
|
ctx->invocation = g_object_ref (invocation);
|
|
ctx->self = g_object_ref (self);
|
|
|
|
g_variant_get (variant,
|
|
"(uu)",
|
|
&ctx->allowed,
|
|
&ctx->preferred);
|
|
|
|
mm_base_modem_authorize (MM_BASE_MODEM (self),
|
|
invocation,
|
|
MM_AUTHORIZATION_DEVICE_CONTROL,
|
|
(GAsyncReadyCallback)handle_set_current_modes_auth_ready,
|
|
ctx);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
reinitialize_ready (MMBaseModem *self,
|
|
GAsyncResult *res)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
mm_base_modem_initialize_finish (self, res, &error);
|
|
if (error) {
|
|
mm_obj_warn (self, "reinitialization failed: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
restart_initialize_idle (MMIfaceModem *self)
|
|
{
|
|
g_object_set_qdata (G_OBJECT (self), restart_initialize_idle_quark, NULL);
|
|
|
|
/* If no wait needed, just go on */
|
|
mm_base_modem_initialize (MM_BASE_MODEM (self),
|
|
(GAsyncReadyCallback) reinitialize_ready,
|
|
NULL);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
restart_initialize_idle_cancel (gpointer idp)
|
|
{
|
|
g_source_remove (GPOINTER_TO_UINT (idp));
|
|
}
|
|
|
|
static void
|
|
set_lock_status (MMIfaceModem *self,
|
|
MmGdbusModem *skeleton,
|
|
MMModemLock lock)
|
|
{
|
|
MMModemLock old_lock;
|
|
|
|
old_lock = mm_gdbus_modem_get_unlock_required (skeleton);
|
|
mm_gdbus_modem_set_unlock_required (skeleton, lock);
|
|
|
|
/* We don't care about SIM-PIN2/SIM-PUK2 since the device is
|
|
* operational without it. */
|
|
if (lock == MM_MODEM_LOCK_NONE ||
|
|
lock == MM_MODEM_LOCK_SIM_PIN2 ||
|
|
lock == MM_MODEM_LOCK_SIM_PUK2) {
|
|
/* Notify transition from INITIALIZING/LOCKED to DISABLED */
|
|
if (old_lock != MM_MODEM_LOCK_NONE &&
|
|
old_lock != MM_MODEM_LOCK_SIM_PIN2 &&
|
|
old_lock != MM_MODEM_LOCK_SIM_PUK2) {
|
|
/* Only restart initialization if leaving LOCKED.
|
|
* If this is the case, we do NOT update the state yet, we wait
|
|
* to be completely re-initialized to do so. */
|
|
if (old_lock != MM_MODEM_LOCK_UNKNOWN) {
|
|
guint id;
|
|
|
|
if (G_UNLIKELY (!restart_initialize_idle_quark))
|
|
restart_initialize_idle_quark = (g_quark_from_static_string (RESTART_INITIALIZE_IDLE_TAG));
|
|
|
|
id = g_idle_add ((GSourceFunc)restart_initialize_idle, self);
|
|
g_object_set_qdata_full (G_OBJECT (self),
|
|
restart_initialize_idle_quark,
|
|
GUINT_TO_POINTER (id),
|
|
(GDestroyNotify)restart_initialize_idle_cancel);
|
|
}
|
|
}
|
|
} else {
|
|
if (old_lock == MM_MODEM_LOCK_UNKNOWN) {
|
|
/* Notify transition from INITIALIZING to LOCKED */
|
|
mm_iface_modem_update_state (self,
|
|
MM_MODEM_STATE_LOCKED,
|
|
MM_MODEM_STATE_CHANGE_REASON_UNKNOWN);
|
|
}
|
|
}
|
|
}
|
|
|
|
MMModemLock
|
|
mm_iface_modem_get_unlock_required (MMIfaceModem *self)
|
|
{
|
|
MmGdbusModem *skeleton = NULL;
|
|
MMModemLock lock;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
if (skeleton) {
|
|
lock = mm_gdbus_modem_get_unlock_required (skeleton);
|
|
g_object_unref (skeleton);
|
|
} else
|
|
lock = MM_MODEM_LOCK_UNKNOWN;
|
|
|
|
return lock;
|
|
}
|
|
|
|
MMUnlockRetries *
|
|
mm_iface_modem_get_unlock_retries (MMIfaceModem *self)
|
|
{
|
|
MmGdbusModem *skeleton = NULL;
|
|
MMUnlockRetries *unlock_retries;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
if (skeleton) {
|
|
GVariant *dictionary;
|
|
|
|
dictionary = mm_gdbus_modem_get_unlock_retries (skeleton);
|
|
unlock_retries = mm_unlock_retries_new_from_dictionary (dictionary);
|
|
g_object_unref (skeleton);
|
|
} else
|
|
unlock_retries = mm_unlock_retries_new ();
|
|
|
|
return unlock_retries;
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_update_unlock_retries (MMIfaceModem *self,
|
|
MMUnlockRetries *unlock_retries)
|
|
{
|
|
MmGdbusModem *skeleton = NULL;
|
|
GVariant *previous_dictionary;
|
|
MMUnlockRetries *previous_unlock_retries;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
if (!skeleton)
|
|
return;
|
|
|
|
previous_dictionary = mm_gdbus_modem_get_unlock_retries (skeleton);
|
|
previous_unlock_retries = mm_unlock_retries_new_from_dictionary (previous_dictionary);
|
|
|
|
/* If they are different, update */
|
|
if (!mm_unlock_retries_cmp (unlock_retries, previous_unlock_retries)) {
|
|
GVariant *new_dictionary;
|
|
|
|
new_dictionary = mm_unlock_retries_get_dictionary (unlock_retries);
|
|
mm_gdbus_modem_set_unlock_retries (skeleton, new_dictionary);
|
|
g_variant_unref (new_dictionary);
|
|
}
|
|
|
|
g_object_unref (previous_unlock_retries);
|
|
g_object_unref (skeleton);
|
|
}
|
|
|
|
typedef enum {
|
|
UPDATE_LOCK_INFO_CONTEXT_STEP_FIRST = 0,
|
|
UPDATE_LOCK_INFO_CONTEXT_STEP_LOCK,
|
|
UPDATE_LOCK_INFO_CONTEXT_STEP_AFTER_UNLOCK,
|
|
UPDATE_LOCK_INFO_CONTEXT_STEP_RETRIES,
|
|
UPDATE_LOCK_INFO_CONTEXT_STEP_LAST
|
|
} UpdateLockInfoContextStep;
|
|
|
|
typedef struct {
|
|
UpdateLockInfoContextStep step;
|
|
MmGdbusModem *skeleton;
|
|
MMModemLock lock;
|
|
GError *saved_error;
|
|
} UpdateLockInfoContext;
|
|
|
|
static void
|
|
update_lock_info_context_free (UpdateLockInfoContext *ctx)
|
|
{
|
|
g_assert (ctx->saved_error == NULL);
|
|
|
|
if (ctx->skeleton)
|
|
g_object_unref (ctx->skeleton);
|
|
g_slice_free (UpdateLockInfoContext, ctx);
|
|
}
|
|
|
|
MMModemLock
|
|
mm_iface_modem_update_lock_info_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
GError *inner_error = NULL;
|
|
gssize value;
|
|
|
|
value = g_task_propagate_int (G_TASK (res), &inner_error);
|
|
if (inner_error) {
|
|
g_propagate_error (error, inner_error);
|
|
return MM_MODEM_LOCK_UNKNOWN;
|
|
}
|
|
return (MMModemLock)value;
|
|
}
|
|
|
|
static void update_lock_info_context_step (GTask *task);
|
|
|
|
static void
|
|
load_unlock_retries_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
UpdateLockInfoContext *ctx;
|
|
GError *error = NULL;
|
|
MMUnlockRetries *unlock_retries;
|
|
|
|
unlock_retries = MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_retries_finish (self, res, &error);
|
|
if (!unlock_retries) {
|
|
mm_obj_warn (self, "couldn't load unlock retries: %s", error->message);
|
|
g_error_free (error);
|
|
} else {
|
|
/* Update the dictionary in the DBus interface */
|
|
mm_iface_modem_update_unlock_retries (self, unlock_retries);
|
|
g_object_unref (unlock_retries);
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx = g_task_get_task_data (task);
|
|
ctx->step++;
|
|
update_lock_info_context_step (task);
|
|
}
|
|
|
|
static void
|
|
modem_after_sim_unlock_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
UpdateLockInfoContext *ctx;
|
|
GError *error = NULL;
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_sim_unlock_finish (self, res, &error)) {
|
|
mm_obj_warn (self, "after SIM unlock failed: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx = g_task_get_task_data (task);
|
|
ctx->step++;
|
|
update_lock_info_context_step (task);
|
|
}
|
|
|
|
static void
|
|
internal_load_unlock_required_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
UpdateLockInfoContext *ctx;
|
|
GError *error = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
ctx->lock = internal_load_unlock_required_finish (self, res, &error);
|
|
if (error) {
|
|
/* Treat several SIM related, serial and other core errors as critical
|
|
* and abort the checks. These will end up moving the modem to a FAILED
|
|
* state. */
|
|
if (error->domain == MM_SERIAL_ERROR ||
|
|
g_error_matches (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_CANCELLED)) {
|
|
ctx->saved_error = error;
|
|
ctx->step = UPDATE_LOCK_INFO_CONTEXT_STEP_LAST;
|
|
update_lock_info_context_step (task);
|
|
return;
|
|
}
|
|
|
|
if (g_error_matches (error,
|
|
MM_MOBILE_EQUIPMENT_ERROR,
|
|
MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) ||
|
|
g_error_matches (error,
|
|
MM_MOBILE_EQUIPMENT_ERROR,
|
|
MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) ||
|
|
g_error_matches (error,
|
|
MM_MOBILE_EQUIPMENT_ERROR,
|
|
MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) {
|
|
/* SIM errors are only critical in 3GPP-only devices */
|
|
if (!mm_iface_modem_is_cdma (self)) {
|
|
ctx->saved_error = error;
|
|
ctx->step = UPDATE_LOCK_INFO_CONTEXT_STEP_LAST;
|
|
update_lock_info_context_step (task);
|
|
return;
|
|
}
|
|
|
|
/* For mixed 3GPP+3GPP2 devices, skip SIM errors */
|
|
mm_obj_dbg (self, "skipping SIM error in 3GPP2-capable device, assuming no lock is needed");
|
|
g_error_free (error);
|
|
ctx->lock = MM_MODEM_LOCK_NONE;
|
|
} else {
|
|
mm_obj_dbg (self, "couldn't check if unlock required: %s", error->message);
|
|
g_error_free (error);
|
|
ctx->lock = MM_MODEM_LOCK_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx->step++;
|
|
update_lock_info_context_step (task);
|
|
}
|
|
|
|
static void
|
|
update_lock_info_context_step (GTask *task)
|
|
{
|
|
MMIfaceModem *self;
|
|
UpdateLockInfoContext *ctx;
|
|
|
|
self = g_task_get_source_object (task);
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
switch (ctx->step) {
|
|
case UPDATE_LOCK_INFO_CONTEXT_STEP_FIRST:
|
|
/* We need the skeleton around */
|
|
if (!ctx->skeleton) {
|
|
ctx->saved_error = g_error_new (MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"Couldn't get interface skeleton");
|
|
ctx->step = UPDATE_LOCK_INFO_CONTEXT_STEP_LAST;
|
|
update_lock_info_context_step (task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case UPDATE_LOCK_INFO_CONTEXT_STEP_LOCK:
|
|
/* Don't re-ask if already known */
|
|
if (ctx->lock == MM_MODEM_LOCK_UNKNOWN) {
|
|
/* If we're already unlocked, we're done */
|
|
internal_load_unlock_required (
|
|
self,
|
|
(GAsyncReadyCallback)internal_load_unlock_required_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case UPDATE_LOCK_INFO_CONTEXT_STEP_AFTER_UNLOCK:
|
|
/* If we get that no lock is required, run the after SIM unlock step
|
|
* in order to wait for the SIM to get ready. Skip waiting on
|
|
* CDMA-only modems where we don't support a SIM. */
|
|
if (!mm_iface_modem_is_cdma_only (self) &&
|
|
(ctx->lock == MM_MODEM_LOCK_NONE ||
|
|
ctx->lock == MM_MODEM_LOCK_SIM_PIN2 ||
|
|
ctx->lock == MM_MODEM_LOCK_SIM_PUK2)) {
|
|
if (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_sim_unlock != NULL &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_sim_unlock_finish != NULL) {
|
|
mm_obj_dbg (self, "SIM is ready, running after SIM unlock step...");
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_sim_unlock (
|
|
self,
|
|
(GAsyncReadyCallback)modem_after_sim_unlock_ready,
|
|
task);
|
|
return;
|
|
}
|
|
|
|
/* If no way to run after SIM unlock step, we're done */
|
|
mm_obj_dbg (self, "SIM is ready, and no need for the after SIM unlock step...");
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case UPDATE_LOCK_INFO_CONTEXT_STEP_RETRIES:
|
|
/* Load unlock retries if possible */
|
|
if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_retries &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_retries_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_retries (
|
|
self,
|
|
(GAsyncReadyCallback)load_unlock_retries_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case UPDATE_LOCK_INFO_CONTEXT_STEP_LAST:
|
|
if (ctx->saved_error) {
|
|
/* Return saved error */
|
|
g_task_return_error (task, ctx->saved_error);
|
|
ctx->saved_error = NULL;
|
|
} else {
|
|
/* Update lock status and modem status if needed */
|
|
set_lock_status (self, ctx->skeleton, ctx->lock);
|
|
g_task_return_int (task, ctx->lock);
|
|
}
|
|
|
|
g_object_unref (task);
|
|
return;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_update_lock_info (MMIfaceModem *self,
|
|
MMModemLock known_lock,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
UpdateLockInfoContext *ctx;
|
|
GTask *task;
|
|
|
|
ctx = g_slice_new0 (UpdateLockInfoContext);
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &ctx->skeleton,
|
|
NULL);
|
|
|
|
/* If the given lock is known, we will avoid re-asking for it */
|
|
ctx->lock = known_lock;
|
|
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify)update_lock_info_context_free);
|
|
|
|
update_lock_info_context_step (task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Set power state sequence */
|
|
|
|
typedef struct {
|
|
MmGdbusModem *skeleton;
|
|
MMModemPowerState power_state;
|
|
MMModemPowerState previous_cached_power_state;
|
|
MMModemPowerState previous_real_power_state;
|
|
} SetPowerStateContext;
|
|
|
|
static void
|
|
set_power_state_context_free (SetPowerStateContext *ctx)
|
|
{
|
|
if (ctx->skeleton)
|
|
g_object_unref (ctx->skeleton);
|
|
g_slice_free (SetPowerStateContext, ctx);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_set_power_state_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
modem_after_power_up_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up_finish (self, res, &error);
|
|
if (error)
|
|
g_task_return_error (task, error);
|
|
else
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
modem_power_up_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
SetPowerStateContext *ctx;
|
|
GError *error = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up_finish (self, res, &error);
|
|
if (error) {
|
|
/* If the real and cached ones are different, set the real one */
|
|
if (ctx->previous_cached_power_state != ctx->previous_real_power_state)
|
|
mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->previous_real_power_state);
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
mm_obj_dbg (self, "set in full-power mode...");
|
|
mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->power_state);
|
|
|
|
/* If we have something to do just after power-up, do it */
|
|
if (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up (
|
|
self,
|
|
(GAsyncReadyCallback)modem_after_power_up_ready,
|
|
task);
|
|
return;
|
|
}
|
|
|
|
/* Otherwise, we're done */
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
modem_power_down_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
SetPowerStateContext *ctx;
|
|
GError *error = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down_finish (self, res, &error);
|
|
if (error) {
|
|
/* If the real and cached ones are different, set the real one */
|
|
if (ctx->previous_cached_power_state != ctx->previous_real_power_state)
|
|
mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->previous_real_power_state);
|
|
g_task_return_error (task, error);
|
|
} else {
|
|
mm_obj_dbg (self, "set in low-power mode...");
|
|
mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->power_state);
|
|
g_task_return_boolean (task, TRUE);
|
|
}
|
|
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
modem_power_off_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
SetPowerStateContext *ctx;
|
|
GError *error = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_off_finish (self, res, &error);
|
|
if (error) {
|
|
/* If the real and cached ones are different, set the real one */
|
|
if (ctx->previous_cached_power_state != ctx->previous_real_power_state)
|
|
mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->previous_real_power_state);
|
|
g_task_return_error (task, error);
|
|
} else {
|
|
mm_obj_info (self, "powered off... may no longer be accessible");
|
|
mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->power_state);
|
|
g_task_return_boolean (task, TRUE);
|
|
}
|
|
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
set_power_state (GTask *task)
|
|
{
|
|
MMIfaceModem *self;
|
|
SetPowerStateContext *ctx;
|
|
|
|
self = g_task_get_source_object (task);
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
/* Already done if we're in the desired power state */
|
|
if (ctx->previous_real_power_state == ctx->power_state) {
|
|
mm_obj_dbg (self, "no need to change power state: already in '%s' power state",
|
|
mm_modem_power_state_get_string (ctx->power_state));
|
|
/* If the real and cached ones are different, set the real one */
|
|
if (ctx->previous_cached_power_state != ctx->previous_real_power_state)
|
|
mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->previous_real_power_state);
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
/* Fully powering off the modem? */
|
|
if (ctx->power_state == MM_MODEM_POWER_STATE_OFF) {
|
|
/* Error if unsupported */
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_off ||
|
|
!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_off_finish) {
|
|
g_task_return_new_error (task,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNSUPPORTED,
|
|
"Powering off is not supported by this modem");
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_off (
|
|
MM_IFACE_MODEM (self),
|
|
(GAsyncReadyCallback)modem_power_off_ready,
|
|
task);
|
|
return;
|
|
}
|
|
|
|
/* Going into low power mode? */
|
|
if (ctx->power_state == MM_MODEM_POWER_STATE_LOW) {
|
|
/* Error if unsupported */
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down ||
|
|
!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down_finish) {
|
|
g_task_return_new_error (task,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNSUPPORTED,
|
|
"Going into low-power mode is not supported by this modem");
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down (
|
|
MM_IFACE_MODEM (self),
|
|
(GAsyncReadyCallback)modem_power_down_ready,
|
|
task);
|
|
return;
|
|
}
|
|
|
|
/* Going out of low power mode? */
|
|
if (ctx->power_state == MM_MODEM_POWER_STATE_ON) {
|
|
/* Error if unsupported */
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up ||
|
|
!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up_finish) {
|
|
g_task_return_new_error (task,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNSUPPORTED,
|
|
"Going into full-power mode is not supported by this modem");
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up (
|
|
MM_IFACE_MODEM (self),
|
|
(GAsyncReadyCallback)modem_power_up_ready,
|
|
task);
|
|
return;
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
static void
|
|
set_power_state_load_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
SetPowerStateContext *ctx;
|
|
GError *error = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
ctx->previous_real_power_state = MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state_finish (self, res, &error);
|
|
if (error) {
|
|
mm_obj_dbg (self, "couldn't reload current power state: %s", error->message);
|
|
g_error_free (error);
|
|
/* Default to the cached one */
|
|
ctx->previous_real_power_state = ctx->previous_cached_power_state;
|
|
}
|
|
|
|
/* And keep on */
|
|
set_power_state (task);
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_set_power_state (MMIfaceModem *self,
|
|
MMModemPowerState power_state,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
SetPowerStateContext *ctx;
|
|
GTask *task;
|
|
|
|
ctx = g_slice_new0 (SetPowerStateContext);
|
|
ctx->power_state = power_state;
|
|
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify)set_power_state_context_free);
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_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->previous_cached_power_state = mm_gdbus_modem_get_power_state (ctx->skeleton);
|
|
|
|
/* We cannot really rely on the power state value that we had cached before,
|
|
* as the real power status of the modem may also be changed by rfkill. So,
|
|
* before updating the current power state, re-check which is the real power
|
|
* state. */
|
|
if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state (
|
|
self,
|
|
(GAsyncReadyCallback)set_power_state_load_ready,
|
|
task);
|
|
return;
|
|
}
|
|
|
|
/* If there is no way to load power state, just keep on assuming the cached
|
|
* one is also the real one */
|
|
ctx->previous_real_power_state = ctx->previous_cached_power_state;
|
|
set_power_state (task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* MODEM DISABLING */
|
|
|
|
gboolean
|
|
mm_iface_modem_disable_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_disable (MMIfaceModem *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
|
|
/* Just complete, nothing to do */
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* MODEM ENABLING */
|
|
|
|
typedef struct _EnablingContext EnablingContext;
|
|
static void interface_enabling_step (GTask *task);
|
|
|
|
typedef enum {
|
|
ENABLING_STEP_FIRST,
|
|
ENABLING_STEP_SET_POWER_STATE,
|
|
ENABLING_STEP_CHECK_FOR_SIM_SWAP,
|
|
ENABLING_STEP_FLOW_CONTROL,
|
|
ENABLING_STEP_LAST
|
|
} EnablingStep;
|
|
|
|
struct _EnablingContext {
|
|
EnablingStep step;
|
|
MmGdbusModem *skeleton;
|
|
};
|
|
|
|
static void
|
|
enabling_context_free (EnablingContext *ctx)
|
|
{
|
|
if (ctx->skeleton)
|
|
g_object_unref (ctx->skeleton);
|
|
g_free (ctx);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_enable_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
enabling_set_power_state_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
EnablingContext *ctx;
|
|
GError *error = NULL;
|
|
|
|
if (!mm_iface_modem_set_power_state_finish (self, res, &error)) {
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx = g_task_get_task_data (task);
|
|
ctx->step++;
|
|
interface_enabling_step (task);
|
|
}
|
|
|
|
static void
|
|
check_for_sim_swap_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
EnablingContext *ctx;
|
|
GError *error = NULL;
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish (self, res, &error)) {
|
|
mm_obj_warn (self, "failed to check if SIM was swapped: %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_flow_control_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
EnablingContext *ctx;
|
|
GError *error = NULL;
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->setup_flow_control_finish (self, res, &error);
|
|
if (error) {
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx = g_task_get_task_data (task);
|
|
ctx->step++;
|
|
interface_enabling_step (task);
|
|
}
|
|
|
|
static const MMModemCharset best_charsets[] = {
|
|
MM_MODEM_CHARSET_UTF8,
|
|
MM_MODEM_CHARSET_UCS2,
|
|
MM_MODEM_CHARSET_8859_1,
|
|
MM_MODEM_CHARSET_IRA,
|
|
MM_MODEM_CHARSET_GSM,
|
|
MM_MODEM_CHARSET_UNKNOWN
|
|
};
|
|
|
|
static void
|
|
interface_enabling_step (GTask *task)
|
|
{
|
|
MMIfaceModem *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_SET_POWER_STATE:
|
|
mm_iface_modem_set_power_state (self,
|
|
MM_MODEM_POWER_STATE_ON,
|
|
(GAsyncReadyCallback)enabling_set_power_state_ready,
|
|
task);
|
|
return;
|
|
|
|
case ENABLING_STEP_CHECK_FOR_SIM_SWAP:
|
|
if (MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap (
|
|
self,
|
|
NULL,
|
|
(GAsyncReadyCallback)check_for_sim_swap_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case ENABLING_STEP_FLOW_CONTROL:
|
|
if (MM_IFACE_MODEM_GET_INTERFACE (self)->setup_flow_control &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->setup_flow_control_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->setup_flow_control (
|
|
self,
|
|
(GAsyncReadyCallback)setup_flow_control_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_enable (MMIfaceModem *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_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);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* MODEM INITIALIZATION */
|
|
|
|
typedef struct _InitializationContext InitializationContext;
|
|
static void interface_initialization_step (GTask *task);
|
|
|
|
typedef enum {
|
|
INITIALIZATION_STEP_FIRST,
|
|
INITIALIZATION_STEP_CURRENT_CAPABILITIES,
|
|
INITIALIZATION_STEP_SUPPORTED_CAPABILITIES,
|
|
INITIALIZATION_STEP_SUPPORTED_CHARSETS,
|
|
INITIALIZATION_STEP_CHARSET,
|
|
INITIALIZATION_STEP_BEARERS,
|
|
INITIALIZATION_STEP_MANUFACTURER,
|
|
INITIALIZATION_STEP_MODEL,
|
|
INITIALIZATION_STEP_REVISION,
|
|
INITIALIZATION_STEP_CARRIER_CONFIG,
|
|
INITIALIZATION_STEP_HARDWARE_REVISION,
|
|
INITIALIZATION_STEP_EQUIPMENT_ID,
|
|
INITIALIZATION_STEP_DEVICE_ID,
|
|
INITIALIZATION_STEP_SUPPORTED_MODES,
|
|
INITIALIZATION_STEP_SUPPORTED_BANDS,
|
|
INITIALIZATION_STEP_SUPPORTED_IP_FAMILIES,
|
|
INITIALIZATION_STEP_POWER_STATE,
|
|
INITIALIZATION_STEP_SIM_HOT_SWAP,
|
|
INITIALIZATION_STEP_SIM_SLOTS,
|
|
INITIALIZATION_STEP_UNLOCK_REQUIRED,
|
|
INITIALIZATION_STEP_SIM,
|
|
INITIALIZATION_STEP_SETUP_CARRIER_CONFIG,
|
|
INITIALIZATION_STEP_OWN_NUMBERS,
|
|
INITIALIZATION_STEP_CURRENT_MODES,
|
|
INITIALIZATION_STEP_CURRENT_BANDS,
|
|
INITIALIZATION_STEP_LAST
|
|
} InitializationStep;
|
|
|
|
struct _InitializationContext {
|
|
InitializationStep step;
|
|
MmGdbusModem *skeleton;
|
|
MMModemCharset supported_charsets;
|
|
const MMModemCharset *current_charset;
|
|
GError *fatal_error;
|
|
};
|
|
|
|
static void
|
|
initialization_context_free (InitializationContext *ctx)
|
|
{
|
|
g_assert (ctx->fatal_error == NULL);
|
|
g_object_unref (ctx->skeleton);
|
|
g_free (ctx);
|
|
}
|
|
|
|
#undef STR_REPLY_READY_FN
|
|
#define STR_REPLY_READY_FN(NAME,DISPLAY) \
|
|
static void \
|
|
load_##NAME##_ready (MMIfaceModem *self, \
|
|
GAsyncResult *res, \
|
|
GTask *task) \
|
|
{ \
|
|
InitializationContext *ctx; \
|
|
GError *error = NULL; \
|
|
gchar *val; \
|
|
\
|
|
ctx = g_task_get_task_data (task); \
|
|
\
|
|
val = MM_IFACE_MODEM_GET_INTERFACE (self)->load_##NAME##_finish (self, res, &error); \
|
|
mm_gdbus_modem_set_##NAME (ctx->skeleton, val); \
|
|
g_free (val); \
|
|
\
|
|
if (error) { \
|
|
mm_obj_warn (self, "couldn't load %s: %s", DISPLAY, error->message); \
|
|
g_error_free (error); \
|
|
} \
|
|
\
|
|
/* Go on to next step */ \
|
|
ctx->step++; \
|
|
interface_initialization_step (task); \
|
|
}
|
|
|
|
#undef UINT_REPLY_READY_FN
|
|
#define UINT_REPLY_READY_FN(NAME,DISPLAY) \
|
|
static void \
|
|
load_##NAME##_ready (MMIfaceModem *self, \
|
|
GAsyncResult *res, \
|
|
GTask *task) \
|
|
{ \
|
|
InitializationContext *ctx; \
|
|
GError *error = NULL; \
|
|
\
|
|
ctx = g_task_get_task_data (task); \
|
|
\
|
|
mm_gdbus_modem_set_##NAME ( \
|
|
ctx->skeleton, \
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_##NAME##_finish (self, res, &error)); \
|
|
\
|
|
if (error) { \
|
|
mm_obj_warn (self, "couldn't load %s: %s", DISPLAY, error->message); \
|
|
g_error_free (error); \
|
|
} \
|
|
\
|
|
/* Go on to next step */ \
|
|
ctx->step++; \
|
|
interface_initialization_step (task); \
|
|
}
|
|
|
|
static void
|
|
current_capabilities_internal_load_unlock_required_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
GError *error = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
internal_load_unlock_required_finish (self, res, &error);
|
|
if (error) {
|
|
/* These SIM errors indicate that there is NO valid SIM available. So,
|
|
* remove all 3GPP caps from the current capabilities */
|
|
if (g_error_matches (error,
|
|
MM_MOBILE_EQUIPMENT_ERROR,
|
|
MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) ||
|
|
g_error_matches (error,
|
|
MM_MOBILE_EQUIPMENT_ERROR,
|
|
MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) ||
|
|
g_error_matches (error,
|
|
MM_MOBILE_EQUIPMENT_ERROR,
|
|
MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) {
|
|
MMModemCapability caps;
|
|
|
|
mm_obj_dbg (self, "multimode device without SIM, no 3GPP capabilities");
|
|
caps = mm_gdbus_modem_get_current_capabilities (ctx->skeleton);
|
|
caps &= ~MM_MODEM_CAPABILITY_3GPP;
|
|
|
|
/* CDMA-EVDO must still be around */
|
|
g_assert (caps & MM_MODEM_CAPABILITY_CDMA_EVDO);
|
|
mm_gdbus_modem_set_current_capabilities (ctx->skeleton, caps);
|
|
}
|
|
|
|
g_error_free (error);
|
|
}
|
|
|
|
/* Keep on */
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
load_current_capabilities_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
MMModemCapability caps;
|
|
GError *error = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
caps = MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_capabilities_finish (self, res, &error);
|
|
if (error) {
|
|
g_propagate_error (&ctx->fatal_error, error);
|
|
g_prefix_error (&ctx->fatal_error, "couldn't load current capabilities: ");
|
|
/* Jump to the last step */
|
|
ctx->step = INITIALIZATION_STEP_LAST;
|
|
interface_initialization_step (task);
|
|
return;
|
|
}
|
|
|
|
/* By default CS/PS/CDMA1X/EVDO network registration checks are the only
|
|
* ones enabled, so fix them up based on capabilities, enabling EPS or 5GS
|
|
* checks if required, and disabling CS/PS/CDMA1X/EVDO if required. */
|
|
if (caps & MM_MODEM_CAPABILITY_LTE) {
|
|
mm_obj_dbg (self, "setting EPS network as supported");
|
|
g_object_set (G_OBJECT (self),
|
|
MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, TRUE,
|
|
NULL);
|
|
}
|
|
if (caps & MM_MODEM_CAPABILITY_5GNR) {
|
|
mm_obj_dbg (self, "setting 5GS network as supported");
|
|
g_object_set (G_OBJECT (self),
|
|
MM_IFACE_MODEM_3GPP_5GS_NETWORK_SUPPORTED, TRUE,
|
|
NULL);
|
|
}
|
|
if (!(caps & MM_MODEM_CAPABILITY_CDMA_EVDO)) {
|
|
mm_obj_dbg (self, "setting CDMA1x/EVDO networks as unsupported");
|
|
g_object_set (G_OBJECT (self),
|
|
MM_IFACE_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED, FALSE,
|
|
MM_IFACE_MODEM_CDMA_EVDO_NETWORK_SUPPORTED, FALSE,
|
|
NULL);
|
|
}
|
|
if (!(caps & MM_MODEM_CAPABILITY_GSM_UMTS)) {
|
|
mm_obj_dbg (self, "setting CS/PS networks as unsupported");
|
|
g_object_set (G_OBJECT (self),
|
|
MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, FALSE,
|
|
MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, FALSE,
|
|
NULL);
|
|
}
|
|
|
|
/* Update current caps right away, even if we may fix them during the
|
|
* multimode device check. No big deal in updating them twice, as we're not
|
|
* exposed in DBus yet. */
|
|
mm_gdbus_modem_set_current_capabilities (ctx->skeleton, caps);
|
|
|
|
/* If the device is a multimode device (3GPP+3GPP2) check whether we have a
|
|
* SIM or not. */
|
|
if ((caps & MM_MODEM_CAPABILITY_CDMA_EVDO) && (caps & MM_MODEM_CAPABILITY_3GPP)) {
|
|
mm_obj_dbg (self, "checking if multimode device has a SIM...");
|
|
internal_load_unlock_required (
|
|
self,
|
|
(GAsyncReadyCallback)current_capabilities_internal_load_unlock_required_ready,
|
|
task);
|
|
return;
|
|
}
|
|
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
load_supported_capabilities_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
GArray *supported_capabilities;
|
|
GError *error = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
supported_capabilities = MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_capabilities_finish (self, res, &error);
|
|
if (error) {
|
|
g_propagate_error (&ctx->fatal_error, error);
|
|
g_prefix_error (&ctx->fatal_error, "couldn't load supported capabilities: ");
|
|
/* Jump to the last step */
|
|
ctx->step = INITIALIZATION_STEP_LAST;
|
|
interface_initialization_step (task);
|
|
return;
|
|
}
|
|
|
|
/* Update supported caps */
|
|
mm_gdbus_modem_set_supported_capabilities (ctx->skeleton,
|
|
mm_common_capability_combinations_garray_to_variant (supported_capabilities));
|
|
g_array_unref (supported_capabilities);
|
|
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
STR_REPLY_READY_FN (manufacturer, "manufacturer")
|
|
STR_REPLY_READY_FN (model, "model")
|
|
STR_REPLY_READY_FN (revision, "revision")
|
|
STR_REPLY_READY_FN (hardware_revision, "hardware revision")
|
|
STR_REPLY_READY_FN (equipment_identifier, "equipment identifier")
|
|
STR_REPLY_READY_FN (device_identifier, "device identifier")
|
|
|
|
static void
|
|
load_supported_charsets_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
GError *error = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
ctx->supported_charsets =
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets_finish (self, res, &error);
|
|
if (error) {
|
|
mm_obj_warn (self, "couldn't load supported charsets: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
setup_charset_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
GError *error = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset_finish (self, res, &error)) {
|
|
mm_obj_dbg (self, "couldn't set charset '%s': %s",
|
|
mm_modem_charset_to_string (*ctx->current_charset),
|
|
error->message);
|
|
g_error_free (error);
|
|
|
|
/* Will retry step with some other charset type */
|
|
} else
|
|
/* Done, Go on to next step */
|
|
ctx->step++;
|
|
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
load_supported_modes_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
GError *error = NULL;
|
|
GArray *modes_array;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
modes_array = MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_modes_finish (self, res, &error);
|
|
if (modes_array != NULL) {
|
|
mm_gdbus_modem_set_supported_modes (ctx->skeleton,
|
|
mm_common_mode_combinations_garray_to_variant (modes_array));
|
|
g_array_unref (modes_array);
|
|
}
|
|
|
|
if (error) {
|
|
mm_obj_warn (self, "couldn't load supported modes: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
load_supported_bands_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
GError *error = NULL;
|
|
GArray *bands_array;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
bands_array = MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_bands_finish (self, res, &error);
|
|
if (bands_array) {
|
|
mm_common_bands_garray_sort (bands_array);
|
|
mm_gdbus_modem_set_supported_bands (ctx->skeleton,
|
|
mm_common_bands_garray_to_variant (bands_array));
|
|
g_array_unref (bands_array);
|
|
}
|
|
|
|
if (error) {
|
|
mm_obj_warn (self, "couldn't load supported bands: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
load_supported_ip_families_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
GError *error = NULL;
|
|
MMBearerIpFamily ip_families;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
ip_families = MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_ip_families_finish (self, res, &error);
|
|
|
|
if (ip_families != MM_BEARER_IP_FAMILY_NONE)
|
|
mm_gdbus_modem_set_supported_ip_families (ctx->skeleton, ip_families);
|
|
|
|
if (error) {
|
|
mm_obj_warn (self, "couldn't load supported IP families: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
UINT_REPLY_READY_FN (power_state, "power state")
|
|
|
|
static void
|
|
setup_sim_hot_swap_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->setup_sim_hot_swap_finish (self, res, &error);
|
|
if (error)
|
|
mm_obj_warn (self, "SIM hot swap setup failed: %s", error->message);
|
|
else {
|
|
mm_obj_dbg (self, "SIM hot swap setup succeeded");
|
|
g_object_set (self,
|
|
MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, TRUE,
|
|
NULL);
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
load_sim_slots_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
g_autoptr(GPtrArray) sim_slots = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
guint primary_sim_slot = 0;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_sim_slots_finish (self,
|
|
res,
|
|
&sim_slots,
|
|
&primary_sim_slot,
|
|
&error))
|
|
mm_obj_warn (self, "couldn't query SIM slots: %s", error->message);
|
|
|
|
if (sim_slots) {
|
|
MMBaseSim *primary_sim = NULL;
|
|
GPtrArray *sim_slot_paths_array;
|
|
g_auto(GStrv) sim_slot_paths = NULL;
|
|
guint i;
|
|
|
|
g_assert (primary_sim_slot);
|
|
g_assert_cmpuint (primary_sim_slot, <=, sim_slots->len);
|
|
|
|
sim_slot_paths_array = g_ptr_array_new ();
|
|
for (i = 0; i < sim_slots->len; i++) {
|
|
MMBaseSim *sim;
|
|
const gchar *sim_path;
|
|
|
|
sim = MM_BASE_SIM (g_ptr_array_index (sim_slots, i));
|
|
if (!sim) {
|
|
g_ptr_array_add (sim_slot_paths_array, g_strdup ("/"));
|
|
continue;
|
|
}
|
|
|
|
sim_path = mm_base_sim_get_path (sim);
|
|
g_ptr_array_add (sim_slot_paths_array, g_strdup (sim_path));
|
|
}
|
|
g_ptr_array_add (sim_slot_paths_array, NULL);
|
|
sim_slot_paths = (GStrv) g_ptr_array_free (sim_slot_paths_array, FALSE);
|
|
|
|
mm_gdbus_modem_set_sim_slots (ctx->skeleton, (const gchar *const *)sim_slot_paths);
|
|
mm_gdbus_modem_set_primary_sim_slot (ctx->skeleton, primary_sim_slot);
|
|
|
|
/* If loading SIM slots is supported, we also expose already the primary active SIM object */
|
|
if (primary_sim_slot) {
|
|
primary_sim = g_ptr_array_index (sim_slots, primary_sim_slot - 1);
|
|
if (primary_sim)
|
|
g_object_bind_property (primary_sim, MM_BASE_SIM_PATH,
|
|
ctx->skeleton, "sim",
|
|
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
|
|
}
|
|
g_object_set (self,
|
|
MM_IFACE_MODEM_SIM, primary_sim,
|
|
MM_IFACE_MODEM_SIM_SLOTS, sim_slots,
|
|
NULL);
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
modem_update_lock_info_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
/* NOTE: we already propagated the lock state, no need to do it again */
|
|
mm_iface_modem_update_lock_info_finish (self, res, &ctx->fatal_error);
|
|
if (ctx->fatal_error) {
|
|
g_prefix_error (&ctx->fatal_error,
|
|
"Couldn't check unlock status: ");
|
|
/* Jump to the last step */
|
|
ctx->step = INITIALIZATION_STEP_LAST;
|
|
} else
|
|
/* Go on to next step */
|
|
ctx->step++;
|
|
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
sim_new_ready (GAsyncInitable *initable,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
MMIfaceModem *self;
|
|
InitializationContext *ctx;
|
|
MMBaseSim *sim;
|
|
GError *error = NULL;
|
|
|
|
self = g_task_get_source_object (task);
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
sim = MM_IFACE_MODEM_GET_INTERFACE (self)->create_sim_finish (self, res, &error);
|
|
if (error) {
|
|
mm_obj_warn (self, "couldn't create SIM: %s", error->message);
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
/* We may get error with !sim, when the implementation doesn't want to
|
|
* handle any (e.g. CDMA) */
|
|
if (sim) {
|
|
g_object_bind_property (sim, MM_BASE_SIM_PATH,
|
|
ctx->skeleton, "sim",
|
|
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
|
|
|
|
g_object_set (self,
|
|
MM_IFACE_MODEM_SIM, sim,
|
|
NULL);
|
|
g_object_unref (sim);
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
sim_reinit_ready (MMBaseSim *sim,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
MMIfaceModem *self;
|
|
InitializationContext *ctx;
|
|
GError *error = NULL;
|
|
|
|
self = g_task_get_source_object (task);
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
if (!mm_base_sim_initialize_finish (sim, res, &error)) {
|
|
mm_obj_warn (self, "SIM re-initialization failed: %s",
|
|
error ? error->message : "Unknown error");
|
|
g_clear_error (&error);
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
setup_carrier_config_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
GError *error = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->setup_carrier_config_finish (self, res, &error)) {
|
|
mm_obj_warn (self, "couldn't setup carrier config: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
load_carrier_config_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
GError *error = NULL;
|
|
gchar *name = NULL;
|
|
gchar *revision = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_carrier_config_finish (self, res, &name, &revision, &error)) {
|
|
mm_obj_warn (self, "couldn't load carrier config: %s", error->message);
|
|
g_error_free (error);
|
|
} else {
|
|
mm_gdbus_modem_set_carrier_configuration (ctx->skeleton, name);
|
|
mm_gdbus_modem_set_carrier_configuration_revision (ctx->skeleton, revision);
|
|
g_free (name);
|
|
g_free (revision);
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_update_own_numbers (MMIfaceModem *self,
|
|
const GStrv own_numbers)
|
|
{
|
|
MmGdbusModem *skeleton = NULL;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
if (skeleton) {
|
|
mm_gdbus_modem_set_own_numbers (skeleton, (const gchar * const *)own_numbers);
|
|
g_object_unref (skeleton);
|
|
}
|
|
}
|
|
|
|
static void
|
|
load_own_numbers_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
GError *error = NULL;
|
|
GStrv str_list;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
str_list = MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers_finish (self, res, &error);
|
|
if (error) {
|
|
mm_obj_warn (self, "couldn't load list of own numbers: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
if (str_list) {
|
|
mm_gdbus_modem_set_own_numbers (ctx->skeleton, (const gchar *const *) str_list);
|
|
g_strfreev (str_list);
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
load_current_modes_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
MMModemMode allowed = MM_MODEM_MODE_NONE;
|
|
MMModemMode preferred = MM_MODEM_MODE_NONE;
|
|
GError *error = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes_finish (self,
|
|
res,
|
|
&allowed,
|
|
&preferred,
|
|
&error)) {
|
|
/* Errors when getting allowed/preferred won't be critical */
|
|
mm_obj_warn (self, "couldn't load current allowed/preferred modes: %s", error->message);
|
|
g_error_free (error);
|
|
} else
|
|
mm_gdbus_modem_set_current_modes (ctx->skeleton, g_variant_new ("(uu)", allowed, preferred));
|
|
|
|
/* Done, Go on to next step */
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
load_current_bands_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
GArray *current_bands;
|
|
GError *error = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
current_bands = MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands_finish (self, res, &error);
|
|
if (!current_bands) {
|
|
/* Errors when getting current bands won't be critical */
|
|
mm_obj_warn (self, "couldn't load current bands: %s", error->message);
|
|
g_error_free (error);
|
|
} else {
|
|
GArray *filtered_bands;
|
|
GArray *supported_bands;
|
|
|
|
supported_bands = (mm_common_bands_variant_to_garray (
|
|
mm_gdbus_modem_get_supported_bands (ctx->skeleton)));
|
|
filtered_bands = mm_filter_current_bands (supported_bands, current_bands);
|
|
|
|
g_array_unref (current_bands);
|
|
if (supported_bands)
|
|
g_array_unref (supported_bands);
|
|
|
|
if (filtered_bands) {
|
|
mm_common_bands_garray_sort (filtered_bands);
|
|
mm_gdbus_modem_set_current_bands (ctx->skeleton,
|
|
mm_common_bands_garray_to_variant (filtered_bands));
|
|
g_array_unref (filtered_bands);
|
|
}
|
|
}
|
|
|
|
/* Done, Go on to next step */
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
interface_initialization_step (GTask *task)
|
|
{
|
|
MMIfaceModem *self;
|
|
InitializationContext *ctx;
|
|
|
|
self = g_task_get_source_object (task);
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
/* Don't run new steps if we're cancelled */
|
|
if (g_task_return_error_if_cancelled (task)) {
|
|
/* Simply ignore any fatal error encountered as the initialization is cancelled anyway. */
|
|
if (ctx->fatal_error) {
|
|
g_error_free (ctx->fatal_error);
|
|
ctx->fatal_error = NULL;
|
|
}
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
switch (ctx->step) {
|
|
case INITIALIZATION_STEP_FIRST:
|
|
/* Load device if not done before */
|
|
if (!mm_gdbus_modem_get_device (ctx->skeleton)) {
|
|
gchar *device;
|
|
|
|
g_object_get (self,
|
|
MM_BASE_MODEM_DEVICE, &device,
|
|
NULL);
|
|
mm_gdbus_modem_set_device (ctx->skeleton, device);
|
|
g_free (device);
|
|
}
|
|
/* Load driver if not done before */
|
|
if (!mm_gdbus_modem_get_drivers (ctx->skeleton)) {
|
|
gchar **drivers;
|
|
|
|
g_object_get (self,
|
|
MM_BASE_MODEM_DRIVERS, &drivers,
|
|
NULL);
|
|
mm_gdbus_modem_set_drivers (ctx->skeleton, (const gchar * const *)drivers);
|
|
g_strfreev (drivers);
|
|
}
|
|
/* Load plugin if not done before */
|
|
if (!mm_gdbus_modem_get_plugin (ctx->skeleton)) {
|
|
gchar *plugin;
|
|
|
|
g_object_get (self,
|
|
MM_BASE_MODEM_PLUGIN, &plugin,
|
|
NULL);
|
|
mm_gdbus_modem_set_plugin (ctx->skeleton, plugin);
|
|
g_free (plugin);
|
|
}
|
|
/* Load primary port if not done before */
|
|
if (!mm_gdbus_modem_get_primary_port (ctx->skeleton)) {
|
|
MMPort *primary = NULL;
|
|
|
|
#if defined WITH_QMI
|
|
if (MM_IS_BROADBAND_MODEM_QMI (self))
|
|
primary = MM_PORT (mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (self)));
|
|
#endif
|
|
#if defined WITH_MBIM
|
|
if (!primary && MM_IS_BROADBAND_MODEM_MBIM (self))
|
|
primary = MM_PORT (mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self)));
|
|
#endif
|
|
if (!primary)
|
|
primary = MM_PORT (mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)));
|
|
|
|
g_assert (primary != NULL);
|
|
mm_gdbus_modem_set_primary_port (ctx->skeleton, mm_port_get_device (primary));
|
|
}
|
|
/* Load ports if not done before */
|
|
if (!mm_gdbus_modem_get_ports (ctx->skeleton)) {
|
|
MMModemPortInfo *port_infos;
|
|
guint n_port_infos;
|
|
|
|
port_infos = mm_base_modem_get_port_infos (MM_BASE_MODEM (self), &n_port_infos);
|
|
mm_gdbus_modem_set_ports (ctx->skeleton, mm_common_ports_array_to_variant (port_infos, n_port_infos));
|
|
mm_modem_port_info_array_free (port_infos, n_port_infos);
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_CURRENT_CAPABILITIES:
|
|
/* Current capabilities may change during runtime, i.e. if new firmware reloaded; but we'll
|
|
* try to handle that by making sure the capabilities are cleared when the new firmware is
|
|
* reloaded. So if we're asked to re-initialize, if we already have current capabilities loaded,
|
|
* don't try to load them again. */
|
|
if (mm_gdbus_modem_get_current_capabilities (ctx->skeleton) == MM_MODEM_CAPABILITY_NONE &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_capabilities &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_capabilities_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_capabilities (
|
|
self,
|
|
(GAsyncReadyCallback)load_current_capabilities_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_SUPPORTED_CAPABILITIES: {
|
|
GArray *supported_capabilities;
|
|
|
|
supported_capabilities = (mm_common_capability_combinations_variant_to_garray (
|
|
mm_gdbus_modem_get_supported_capabilities (ctx->skeleton)));
|
|
|
|
/* Supported capabilities are meant to be loaded only once during the whole
|
|
* lifetime of the modem. Therefore, if we already have them loaded,
|
|
* don't try to load them again. */
|
|
if (supported_capabilities->len == 0 ||
|
|
g_array_index (supported_capabilities, MMModemCapability, 0) == MM_MODEM_CAPABILITY_NONE) {
|
|
MMModemCapability current;
|
|
|
|
if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_capabilities &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_capabilities_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_capabilities (
|
|
self,
|
|
(GAsyncReadyCallback)load_supported_capabilities_ready,
|
|
task);
|
|
g_array_unref (supported_capabilities);
|
|
return;
|
|
}
|
|
|
|
/* If no specific way of getting modem capabilities, default to the current ones */
|
|
g_array_unref (supported_capabilities);
|
|
supported_capabilities = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 1);
|
|
current = mm_gdbus_modem_get_current_capabilities (ctx->skeleton);
|
|
g_array_append_val (supported_capabilities, current);
|
|
mm_gdbus_modem_set_supported_capabilities (
|
|
ctx->skeleton,
|
|
mm_common_capability_combinations_garray_to_variant (supported_capabilities));
|
|
}
|
|
g_array_unref (supported_capabilities);
|
|
|
|
ctx->step++;
|
|
} /* fall-through */
|
|
|
|
case INITIALIZATION_STEP_SUPPORTED_CHARSETS:
|
|
if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets (
|
|
self,
|
|
(GAsyncReadyCallback)load_supported_charsets_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_CHARSET:
|
|
/* Only try to set charsets if we were able to load supported ones */
|
|
if (ctx->supported_charsets > 0 &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset_finish) {
|
|
gboolean next_to_try = FALSE;
|
|
|
|
while (!next_to_try) {
|
|
if (!ctx->current_charset)
|
|
/* Switch the device's charset; we prefer UTF-8, but UCS2 will do too */
|
|
ctx->current_charset = &best_charsets[0];
|
|
else
|
|
/* Try with the next one */
|
|
ctx->current_charset++;
|
|
|
|
if (*ctx->current_charset == MM_MODEM_CHARSET_UNKNOWN)
|
|
break;
|
|
|
|
if (ctx->supported_charsets & (*ctx->current_charset))
|
|
next_to_try = TRUE;
|
|
}
|
|
|
|
if (next_to_try) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset (
|
|
self,
|
|
*ctx->current_charset,
|
|
(GAsyncReadyCallback)setup_charset_ready,
|
|
task);
|
|
return;
|
|
}
|
|
|
|
g_task_return_new_error (task,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"Failed to find a usable modem character set");
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_BEARERS: {
|
|
MMBearerList *list = NULL;
|
|
|
|
/* Bearers setup is meant to be loaded only once during the whole
|
|
* lifetime of the modem. The list may have been created by the object
|
|
* implementing the interface; if so use it. */
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_BEARER_LIST, &list,
|
|
NULL);
|
|
|
|
if (!list) {
|
|
guint n;
|
|
|
|
/* The maximum number of available/connected modems is guessed from
|
|
* the size of the data ports list. */
|
|
n = g_list_length (mm_base_modem_peek_data_ports (MM_BASE_MODEM (self)));
|
|
mm_obj_dbg (self, "allowed up to %u bearers", n);
|
|
|
|
/* Create new default list */
|
|
list = mm_bearer_list_new (n, n);
|
|
g_signal_connect (list,
|
|
"notify::" MM_BEARER_LIST_NUM_BEARERS,
|
|
G_CALLBACK (bearer_list_updated),
|
|
self);
|
|
g_object_set (self,
|
|
MM_IFACE_MODEM_BEARER_LIST, list,
|
|
NULL);
|
|
}
|
|
|
|
if (mm_gdbus_modem_get_max_bearers (ctx->skeleton) == 0)
|
|
mm_gdbus_modem_set_max_bearers (
|
|
ctx->skeleton,
|
|
mm_bearer_list_get_max (list));
|
|
if (mm_gdbus_modem_get_max_active_bearers (ctx->skeleton) == 0)
|
|
mm_gdbus_modem_set_max_active_bearers (
|
|
ctx->skeleton,
|
|
mm_bearer_list_get_max_active (list));
|
|
g_object_unref (list);
|
|
|
|
ctx->step++;
|
|
} /* fall-through */
|
|
|
|
case INITIALIZATION_STEP_MANUFACTURER:
|
|
/* Manufacturer is meant to be loaded only once during the whole
|
|
* lifetime of the modem. Therefore, if we already have them loaded,
|
|
* don't try to load them again. */
|
|
if (mm_gdbus_modem_get_manufacturer (ctx->skeleton) == NULL &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_manufacturer &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_manufacturer_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_manufacturer (
|
|
self,
|
|
(GAsyncReadyCallback)load_manufacturer_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_MODEL:
|
|
/* Model is meant to be loaded only once during the whole
|
|
* lifetime of the modem. Therefore, if we already have them loaded,
|
|
* don't try to load them again. */
|
|
if (mm_gdbus_modem_get_model (ctx->skeleton) == NULL &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_model &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_model_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_model (
|
|
self,
|
|
(GAsyncReadyCallback)load_model_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_REVISION:
|
|
/* Revision is meant to be loaded only once during the whole
|
|
* lifetime of the modem. Therefore, if we already have them loaded,
|
|
* don't try to load them again. */
|
|
if (mm_gdbus_modem_get_revision (ctx->skeleton) == NULL &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_revision &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_revision_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_revision (
|
|
self,
|
|
(GAsyncReadyCallback)load_revision_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_CARRIER_CONFIG:
|
|
/* Current carrier config is meant to be loaded only once during the whole
|
|
* lifetime of the modem. Therefore, if we already have them loaded,
|
|
* don't try to load them again. */
|
|
if (mm_gdbus_modem_get_carrier_configuration (ctx->skeleton) == NULL &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_carrier_config &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_carrier_config_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_carrier_config (self,
|
|
(GAsyncReadyCallback)load_carrier_config_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_HARDWARE_REVISION:
|
|
/* HardwareRevision is meant to be loaded only once during the whole
|
|
* lifetime of the modem. Therefore, if we already have them loaded,
|
|
* don't try to load them again. */
|
|
if (mm_gdbus_modem_get_hardware_revision (ctx->skeleton) == NULL &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_hardware_revision &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_hardware_revision_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_hardware_revision (
|
|
self,
|
|
(GAsyncReadyCallback)load_hardware_revision_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_EQUIPMENT_ID:
|
|
/* Equipment ID is meant to be loaded only once during the whole
|
|
* lifetime of the modem. Therefore, if we already have them loaded,
|
|
* don't try to load them again. */
|
|
if (mm_gdbus_modem_get_equipment_identifier (ctx->skeleton) == NULL &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_equipment_identifier &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_equipment_identifier_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_equipment_identifier (
|
|
self,
|
|
(GAsyncReadyCallback)load_equipment_identifier_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_DEVICE_ID:
|
|
/* Device ID is meant to be loaded only once during the whole
|
|
* lifetime of the modem. Therefore, if we already have them loaded,
|
|
* don't try to load them again. */
|
|
if (mm_gdbus_modem_get_device_identifier (ctx->skeleton) == NULL &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_device_identifier &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_device_identifier_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_device_identifier (
|
|
self,
|
|
(GAsyncReadyCallback)load_device_identifier_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_SUPPORTED_MODES:
|
|
if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_modes != NULL &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_modes_finish != NULL) {
|
|
GArray *supported_modes;
|
|
MMModemModeCombination *mode = NULL;
|
|
|
|
supported_modes = (mm_common_mode_combinations_variant_to_garray (
|
|
mm_gdbus_modem_get_supported_modes (ctx->skeleton)));
|
|
|
|
/* Supported modes are meant to be loaded only once during the whole
|
|
* lifetime of the modem. Therefore, if we already have them loaded,
|
|
* don't try to load them again. */
|
|
if (supported_modes->len == 1)
|
|
mode = &g_array_index (supported_modes, MMModemModeCombination, 0);
|
|
if (supported_modes->len == 0 ||
|
|
(mode && mode->allowed == MM_MODEM_MODE_ANY && mode->preferred == MM_MODEM_MODE_NONE)) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_modes (
|
|
self,
|
|
(GAsyncReadyCallback)load_supported_modes_ready,
|
|
task);
|
|
g_array_unref (supported_modes);
|
|
return;
|
|
}
|
|
|
|
g_array_unref (supported_modes);
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_SUPPORTED_BANDS: {
|
|
GArray *supported_bands;
|
|
|
|
supported_bands = (mm_common_bands_variant_to_garray (
|
|
mm_gdbus_modem_get_supported_bands (ctx->skeleton)));
|
|
|
|
/* Supported bands are meant to be loaded only once during the whole
|
|
* lifetime of the modem. Therefore, if we already have them loaded,
|
|
* don't try to load them again. */
|
|
if (supported_bands->len == 0 ||
|
|
g_array_index (supported_bands, MMModemBand, 0) == MM_MODEM_BAND_UNKNOWN) {
|
|
if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_bands &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_bands_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_bands (
|
|
self,
|
|
(GAsyncReadyCallback)load_supported_bands_ready,
|
|
task);
|
|
g_array_unref (supported_bands);
|
|
return;
|
|
}
|
|
|
|
/* Loading supported bands not implemented, default to UNKNOWN */
|
|
mm_gdbus_modem_set_supported_bands (ctx->skeleton, mm_common_build_bands_unknown ());
|
|
mm_gdbus_modem_set_current_bands (ctx->skeleton, mm_common_build_bands_unknown ());
|
|
}
|
|
g_array_unref (supported_bands);
|
|
|
|
ctx->step++;
|
|
} /* fall-through */
|
|
|
|
case INITIALIZATION_STEP_SUPPORTED_IP_FAMILIES:
|
|
/* Supported ip_families are meant to be loaded only once during the whole
|
|
* lifetime of the modem. Therefore, if we already have them loaded,
|
|
* don't try to load them again. */
|
|
if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_ip_families != NULL &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_ip_families_finish != NULL &&
|
|
mm_gdbus_modem_get_supported_ip_families (ctx->skeleton) == MM_BEARER_IP_FAMILY_NONE) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_ip_families (
|
|
self,
|
|
(GAsyncReadyCallback)load_supported_ip_families_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_POWER_STATE:
|
|
/* Initial power state is meant to be loaded only once. Therefore, if we
|
|
* already have it loaded, don't try to load it again. */
|
|
if (mm_gdbus_modem_get_power_state (ctx->skeleton) == MM_MODEM_POWER_STATE_UNKNOWN) {
|
|
if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state (
|
|
self,
|
|
(GAsyncReadyCallback)load_power_state_ready,
|
|
task);
|
|
return;
|
|
}
|
|
|
|
/* We don't know how to load current power state; assume ON */
|
|
mm_gdbus_modem_set_power_state (ctx->skeleton, MM_MODEM_POWER_STATE_ON);
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_SIM_HOT_SWAP: {
|
|
gboolean sim_hot_swap_configured = FALSE;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, &sim_hot_swap_configured,
|
|
NULL);
|
|
if (!sim_hot_swap_configured &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->setup_sim_hot_swap &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->setup_sim_hot_swap_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->setup_sim_hot_swap (
|
|
MM_IFACE_MODEM (self),
|
|
(GAsyncReadyCallback) setup_sim_hot_swap_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
} /* fall-through */
|
|
|
|
case INITIALIZATION_STEP_SIM_SLOTS:
|
|
/* If the modem doesn't need any SIM (not implemented by plugin, or not
|
|
* needed in CDMA-only modems), or if we don't know how to query
|
|
* for SIM slots */
|
|
if (!mm_gdbus_modem_get_sim_slots (ctx->skeleton) &&
|
|
!mm_iface_modem_is_cdma_only (self) &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_sim_slots &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_sim_slots_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_sim_slots (MM_IFACE_MODEM (self),
|
|
(GAsyncReadyCallback)load_sim_slots_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_UNLOCK_REQUIRED:
|
|
/* Only check unlock required if we were previously not unlocked */
|
|
if (mm_gdbus_modem_get_unlock_required (ctx->skeleton) != MM_MODEM_LOCK_NONE) {
|
|
mm_iface_modem_update_lock_info (self,
|
|
MM_MODEM_LOCK_UNKNOWN, /* ask */
|
|
(GAsyncReadyCallback)modem_update_lock_info_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_SIM:
|
|
/* If the modem doesn't need any SIM (not implemented by plugin, or not
|
|
* needed in CDMA-only modems) */
|
|
if (!mm_iface_modem_is_cdma_only (self) &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->create_sim &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->create_sim_finish) {
|
|
MMBaseSim *sim = NULL;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_SIM, &sim,
|
|
NULL);
|
|
if (!sim) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->create_sim (
|
|
MM_IFACE_MODEM (self),
|
|
(GAsyncReadyCallback)sim_new_ready,
|
|
task);
|
|
return;
|
|
}
|
|
|
|
/* If already available the sim object, relaunch initialization.
|
|
* This will try to load any missing property value that couldn't be
|
|
* retrieved before due to having the SIM locked. */
|
|
mm_base_sim_initialize (sim,
|
|
g_task_get_cancellable (task),
|
|
(GAsyncReadyCallback)sim_reinit_ready,
|
|
task);
|
|
g_object_unref (sim);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_SETUP_CARRIER_CONFIG:
|
|
/* Setup and perform automatic carrier config switching as soon as the
|
|
* SIM initialization has been performed, only applicable if there is
|
|
* actually a SIM found with a valid IMSI read */
|
|
if (!mm_iface_modem_is_cdma_only (self) &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->setup_carrier_config &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->setup_carrier_config_finish) {
|
|
MMBaseSim *sim = NULL;
|
|
gchar *carrier_config_mapping = NULL;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_SIM, &sim,
|
|
MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING, &carrier_config_mapping,
|
|
NULL);
|
|
|
|
/* If we have a SIM object, and carrier config switching is supported,
|
|
* validate whether we're already using the best config or not. */
|
|
if (!sim)
|
|
mm_obj_dbg (self, "not setting up carrier config: SIM not found");
|
|
else if (!carrier_config_mapping)
|
|
mm_obj_dbg (self, "not setting up carrier config: mapping file not configured");
|
|
else {
|
|
const gchar *imsi;
|
|
|
|
imsi = mm_gdbus_sim_get_imsi (MM_GDBUS_SIM (sim));
|
|
if (imsi) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->setup_carrier_config (self,
|
|
imsi,
|
|
carrier_config_mapping,
|
|
(GAsyncReadyCallback)setup_carrier_config_ready,
|
|
task);
|
|
g_object_unref (sim);
|
|
g_free (carrier_config_mapping);
|
|
return;
|
|
}
|
|
mm_obj_warn (self, "couldn't setup carrier config: unknown IMSI");
|
|
}
|
|
g_clear_object (&sim);
|
|
g_free (carrier_config_mapping);
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_OWN_NUMBERS:
|
|
/* Own numbers is meant to be loaded only once during the whole
|
|
* lifetime of the modem. Therefore, if we already have them loaded,
|
|
* don't try to load them again. */
|
|
if (mm_gdbus_modem_get_own_numbers (ctx->skeleton) == NULL &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers (
|
|
self,
|
|
(GAsyncReadyCallback)load_own_numbers_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall-through */
|
|
|
|
case INITIALIZATION_STEP_CURRENT_MODES: {
|
|
MMModemMode allowed = MM_MODEM_MODE_ANY;
|
|
MMModemMode preferred = MM_MODEM_MODE_NONE;
|
|
GVariant *aux;
|
|
|
|
aux = mm_gdbus_modem_get_current_modes (ctx->skeleton);
|
|
if (aux)
|
|
g_variant_get (aux, "(uu)", &allowed, &preferred);
|
|
|
|
/* Current modes are only meant to be loaded once, so if we have them
|
|
* loaded already, just skip re-loading */
|
|
if (allowed == MM_MODEM_MODE_ANY && preferred == MM_MODEM_MODE_NONE) {
|
|
GArray *supported;
|
|
|
|
supported = (mm_common_mode_combinations_variant_to_garray (
|
|
mm_gdbus_modem_get_supported_modes (ctx->skeleton)));
|
|
|
|
/* If there is only one item in the list of supported modes, we're done */
|
|
if (supported && supported->len == 1) {
|
|
MMModemModeCombination *supported_mode;
|
|
|
|
supported_mode = &g_array_index (supported, MMModemModeCombination, 0);
|
|
mm_gdbus_modem_set_current_modes (ctx->skeleton, g_variant_new ("(uu)", supported_mode->allowed, supported_mode->preferred));
|
|
} else if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes (
|
|
self,
|
|
(GAsyncReadyCallback)load_current_modes_ready,
|
|
task);
|
|
if (supported)
|
|
g_array_unref (supported);
|
|
return;
|
|
}
|
|
|
|
if (supported)
|
|
g_array_unref (supported);
|
|
}
|
|
|
|
ctx->step++;
|
|
} /* fall-through */
|
|
|
|
case INITIALIZATION_STEP_CURRENT_BANDS: {
|
|
GArray *current;
|
|
|
|
current = (mm_common_bands_variant_to_garray (
|
|
mm_gdbus_modem_get_current_bands (ctx->skeleton)));
|
|
|
|
/* Current bands are only meant to be loaded once, so if we have them
|
|
* loaded already, just skip re-loading */
|
|
if (!current || (current->len == 1 && g_array_index (current, MMModemBand, 0) == MM_MODEM_BAND_UNKNOWN)) {
|
|
if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands &&
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands_finish) {
|
|
MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands (
|
|
self,
|
|
(GAsyncReadyCallback)load_current_bands_ready,
|
|
task);
|
|
if (current)
|
|
g_array_unref (current);
|
|
return;
|
|
}
|
|
|
|
/* If no way to get current bands, default to what supported has */
|
|
mm_gdbus_modem_set_current_bands (ctx->skeleton, mm_gdbus_modem_get_supported_bands (ctx->skeleton));
|
|
}
|
|
|
|
if (current)
|
|
g_array_unref (current);
|
|
|
|
ctx->step++;
|
|
} /* fall-through */
|
|
|
|
case INITIALIZATION_STEP_LAST:
|
|
/* Setup all method handlers */
|
|
g_object_connect (ctx->skeleton,
|
|
"signal::handle-set-current-capabilities", G_CALLBACK (handle_set_current_capabilities), self,
|
|
"signal::handle-set-power-state", G_CALLBACK (handle_set_power_state), self,
|
|
"signal::handle-reset", G_CALLBACK (handle_reset), self,
|
|
"signal::handle-factory-reset", G_CALLBACK (handle_factory_reset), self,
|
|
"signal::handle-create-bearer", G_CALLBACK (handle_create_bearer), self,
|
|
"signal::handle-command", G_CALLBACK (handle_command), self,
|
|
"signal::handle-delete-bearer", G_CALLBACK (handle_delete_bearer), self,
|
|
"signal::handle-list-bearers", G_CALLBACK (handle_list_bearers), self,
|
|
"signal::handle-enable", G_CALLBACK (handle_enable), self,
|
|
"signal::handle-set-current-bands", G_CALLBACK (handle_set_current_bands), self,
|
|
"signal::handle-set-current-modes", G_CALLBACK (handle_set_current_modes), self,
|
|
"signal::handle-set-primary-sim-slot", G_CALLBACK (handle_set_primary_sim_slot), self,
|
|
NULL);
|
|
|
|
/* Finally, export the new interface, even if we got errors, but only if not
|
|
* done already */
|
|
if (!mm_gdbus_object_peek_modem (MM_GDBUS_OBJECT (self)))
|
|
mm_gdbus_object_skeleton_set_modem (MM_GDBUS_OBJECT_SKELETON (self),
|
|
MM_GDBUS_MODEM (ctx->skeleton));
|
|
|
|
if (ctx->fatal_error) {
|
|
g_task_return_error (task, ctx->fatal_error);
|
|
ctx->fatal_error = NULL;
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
g_object_unref (task);
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_initialize_finish (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_initialize (MMIfaceModem *self,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
InitializationContext *ctx;
|
|
MmGdbusModem *skeleton = NULL;
|
|
GTask *task;
|
|
|
|
/* Did we already create it? */
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
if (!skeleton) {
|
|
skeleton = mm_gdbus_modem_skeleton_new ();
|
|
|
|
/* Set all initial property defaults */
|
|
mm_gdbus_modem_set_sim (skeleton, NULL);
|
|
mm_gdbus_modem_set_supported_capabilities (skeleton, mm_common_build_capability_combinations_none ());
|
|
mm_gdbus_modem_set_current_capabilities (skeleton, MM_MODEM_CAPABILITY_NONE);
|
|
mm_gdbus_modem_set_max_bearers (skeleton, 0);
|
|
mm_gdbus_modem_set_max_active_bearers (skeleton, 0);
|
|
mm_gdbus_modem_set_manufacturer (skeleton, NULL);
|
|
mm_gdbus_modem_set_model (skeleton, NULL);
|
|
mm_gdbus_modem_set_revision (skeleton, NULL);
|
|
mm_gdbus_modem_set_own_numbers (skeleton, NULL);
|
|
mm_gdbus_modem_set_device_identifier (skeleton, NULL);
|
|
mm_gdbus_modem_set_device (skeleton, NULL);
|
|
mm_gdbus_modem_set_drivers (skeleton, NULL);
|
|
mm_gdbus_modem_set_plugin (skeleton, NULL);
|
|
mm_gdbus_modem_set_equipment_identifier (skeleton, NULL);
|
|
mm_gdbus_modem_set_unlock_required (skeleton, MM_MODEM_LOCK_UNKNOWN);
|
|
mm_gdbus_modem_set_unlock_retries (skeleton, 0);
|
|
mm_gdbus_modem_set_access_technologies (skeleton, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
|
|
mm_gdbus_modem_set_signal_quality (skeleton, g_variant_new ("(ub)", 0, FALSE));
|
|
mm_gdbus_modem_set_supported_modes (skeleton, mm_common_build_mode_combinations_default ());
|
|
mm_gdbus_modem_set_current_modes (skeleton, g_variant_new ("(uu)", MM_MODEM_MODE_ANY, MM_MODEM_MODE_NONE));
|
|
mm_gdbus_modem_set_supported_bands (skeleton, mm_common_build_bands_unknown ());
|
|
mm_gdbus_modem_set_current_bands (skeleton, mm_common_build_bands_unknown ());
|
|
mm_gdbus_modem_set_supported_ip_families (skeleton, MM_BEARER_IP_FAMILY_NONE);
|
|
mm_gdbus_modem_set_power_state (skeleton, MM_MODEM_POWER_STATE_UNKNOWN);
|
|
mm_gdbus_modem_set_state_failed_reason (skeleton, MM_MODEM_STATE_FAILED_REASON_NONE);
|
|
|
|
/* Bind our State property */
|
|
g_object_bind_property (self, MM_IFACE_MODEM_STATE,
|
|
skeleton, "state",
|
|
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
|
|
|
|
g_object_set (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, skeleton,
|
|
NULL);
|
|
}
|
|
|
|
/* Perform async initialization here */
|
|
ctx = g_new0 (InitializationContext, 1);
|
|
ctx->step = INITIALIZATION_STEP_FIRST;
|
|
ctx->skeleton = skeleton;
|
|
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
|
|
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_shutdown (MMIfaceModem *self)
|
|
{
|
|
/* Make sure signal polling is disabled. No real need to clear values, as
|
|
* we're shutting down the interface anyway. */
|
|
periodic_signal_check_disable (self, FALSE);
|
|
|
|
/* Remove SignalQualityUpdateContext object to make sure any pending
|
|
* invocation of expire_signal_quality is canceled before the DBus skeleton
|
|
* is removed. */
|
|
if (G_LIKELY (signal_quality_update_context_quark))
|
|
g_object_set_qdata (G_OBJECT (self),
|
|
signal_quality_update_context_quark,
|
|
NULL);
|
|
|
|
/* Remove running restart initialization idle, if any */
|
|
if (G_LIKELY (restart_initialize_idle_quark))
|
|
g_object_set_qdata (G_OBJECT (self),
|
|
restart_initialize_idle_quark,
|
|
NULL);
|
|
|
|
/* Remove SIM object */
|
|
g_object_set (self,
|
|
MM_IFACE_MODEM_SIM, NULL,
|
|
NULL);
|
|
/* Unexport DBus interface and remove the skeleton */
|
|
mm_gdbus_object_skeleton_set_modem (MM_GDBUS_OBJECT_SKELETON (self), NULL);
|
|
g_object_set (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, NULL,
|
|
NULL);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
MMModemAccessTechnology
|
|
mm_iface_modem_get_access_technologies (MMIfaceModem *self)
|
|
{
|
|
MMModemAccessTechnology access_tech = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
|
|
MmGdbusModem *skeleton;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
|
|
if (skeleton) {
|
|
access_tech = mm_gdbus_modem_get_access_technologies (skeleton);
|
|
g_object_unref (skeleton);
|
|
}
|
|
|
|
return access_tech;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
find_supported_mode (MMIfaceModem *self,
|
|
MMModemMode mode,
|
|
gboolean *only)
|
|
{
|
|
gboolean matched = FALSE;
|
|
MmGdbusModem *skeleton;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
|
|
if (skeleton) {
|
|
GArray *supported;
|
|
guint i;
|
|
guint n_unmatched = 0;
|
|
|
|
supported = mm_common_mode_combinations_variant_to_garray (
|
|
mm_gdbus_modem_get_supported_modes (skeleton));
|
|
|
|
/* Check if the given mode is supported */
|
|
for (i = 0; i < supported->len; i++) {
|
|
MMModemModeCombination *supported_mode;
|
|
|
|
supported_mode = &g_array_index (supported, MMModemModeCombination, i);
|
|
if (supported_mode->allowed & mode) {
|
|
matched = TRUE;
|
|
if (supported_mode->allowed != mode)
|
|
n_unmatched++;
|
|
} else
|
|
n_unmatched++;
|
|
|
|
if (matched && (only == NULL || n_unmatched > 0))
|
|
break;
|
|
}
|
|
|
|
if (only)
|
|
*only = (n_unmatched == 0);
|
|
|
|
g_array_unref (supported);
|
|
g_object_unref (skeleton);
|
|
}
|
|
|
|
return matched;
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_is_2g (MMIfaceModem *self)
|
|
{
|
|
return find_supported_mode (self, MM_MODEM_MODE_2G, NULL);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_is_2g_only (MMIfaceModem *self)
|
|
{
|
|
gboolean only;
|
|
|
|
return (find_supported_mode (self, MM_MODEM_MODE_2G, &only) ?
|
|
only :
|
|
FALSE);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_is_3g (MMIfaceModem *self)
|
|
{
|
|
return find_supported_mode (self, MM_MODEM_MODE_3G, NULL);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_is_3g_only (MMIfaceModem *self)
|
|
{
|
|
gboolean only;
|
|
|
|
return (find_supported_mode (self, MM_MODEM_MODE_3G, &only) ?
|
|
only :
|
|
FALSE);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_is_4g (MMIfaceModem *self)
|
|
{
|
|
return find_supported_mode (self, MM_MODEM_MODE_4G, NULL);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_is_4g_only (MMIfaceModem *self)
|
|
{
|
|
gboolean only;
|
|
|
|
return (find_supported_mode (self, MM_MODEM_MODE_4G, &only) ?
|
|
only :
|
|
FALSE);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_is_5g (MMIfaceModem *self)
|
|
{
|
|
return find_supported_mode (self, MM_MODEM_MODE_5G, NULL);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_is_5g_only (MMIfaceModem *self)
|
|
{
|
|
gboolean only;
|
|
|
|
return (find_supported_mode (self, MM_MODEM_MODE_5G, &only) ?
|
|
only :
|
|
FALSE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
MMModemCapability
|
|
mm_iface_modem_get_current_capabilities (MMIfaceModem *self)
|
|
{
|
|
MMModemCapability current = MM_MODEM_CAPABILITY_NONE;
|
|
MmGdbusModem *skeleton;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
|
|
if (skeleton) {
|
|
current = mm_gdbus_modem_get_current_capabilities (skeleton);
|
|
g_object_unref (skeleton);
|
|
}
|
|
|
|
return current;
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_is_3gpp (MMIfaceModem *self)
|
|
{
|
|
return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_3GPP);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_is_3gpp_lte (MMIfaceModem *self)
|
|
{
|
|
return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_LTE);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_is_3gpp_5gnr (MMIfaceModem *self)
|
|
{
|
|
return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_5GNR);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_is_cdma (MMIfaceModem *self)
|
|
{
|
|
return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_CDMA_EVDO);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_is_3gpp_only (MMIfaceModem *self)
|
|
{
|
|
MMModemCapability capabilities;
|
|
|
|
capabilities = mm_iface_modem_get_current_capabilities (self);
|
|
return (capabilities & MM_MODEM_CAPABILITY_3GPP) && !((MM_MODEM_CAPABILITY_3GPP ^ capabilities) & capabilities);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_is_cdma_only (MMIfaceModem *self)
|
|
{
|
|
return (mm_iface_modem_get_current_capabilities (self) == MM_MODEM_CAPABILITY_CDMA_EVDO);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const gchar *
|
|
mm_iface_modem_get_model (MMIfaceModem *self)
|
|
{
|
|
const gchar *model = NULL;
|
|
MmGdbusModem *skeleton;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
|
|
if (skeleton) {
|
|
model = mm_gdbus_modem_get_model (skeleton);
|
|
g_object_unref (skeleton);
|
|
}
|
|
|
|
return model;
|
|
}
|
|
|
|
const gchar *
|
|
mm_iface_modem_get_revision (MMIfaceModem *self)
|
|
{
|
|
const gchar *revision = NULL;
|
|
MmGdbusModem *skeleton;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
|
|
if (skeleton) {
|
|
revision = mm_gdbus_modem_get_revision (skeleton);
|
|
g_object_unref (skeleton);
|
|
}
|
|
|
|
return revision;
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_get_carrier_config (MMIfaceModem *self,
|
|
const gchar **name,
|
|
const gchar **revision)
|
|
{
|
|
MmGdbusModem *skeleton;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
if (!skeleton)
|
|
return FALSE;
|
|
|
|
if (name)
|
|
*name = mm_gdbus_modem_get_carrier_configuration (skeleton);
|
|
if (revision)
|
|
*revision = mm_gdbus_modem_get_carrier_configuration_revision (skeleton);
|
|
g_object_unref (skeleton);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
iface_modem_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_DBUS_SKELETON,
|
|
"Modem DBus skeleton",
|
|
"DBus skeleton for the Modem interface",
|
|
MM_GDBUS_TYPE_MODEM_SKELETON,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_interface_install_property
|
|
(g_iface,
|
|
g_param_spec_object (MM_IFACE_MODEM_SIM,
|
|
"SIM",
|
|
"SIM object",
|
|
MM_TYPE_BASE_SIM,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_interface_install_property
|
|
(g_iface,
|
|
g_param_spec_boxed (MM_IFACE_MODEM_SIM_SLOTS,
|
|
"SIM slots",
|
|
"SIM objects in SIM slots",
|
|
MM_TYPE_OBJECT_ARRAY,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_interface_install_property
|
|
(g_iface,
|
|
g_param_spec_enum (MM_IFACE_MODEM_STATE,
|
|
"State",
|
|
"State of the modem",
|
|
MM_TYPE_MODEM_STATE,
|
|
MM_MODEM_STATE_UNKNOWN,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_interface_install_property
|
|
(g_iface,
|
|
g_param_spec_object (MM_IFACE_MODEM_BEARER_LIST,
|
|
"Bearer list",
|
|
"List of bearers handled by the modem",
|
|
MM_TYPE_BEARER_LIST,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_interface_install_property
|
|
(g_iface,
|
|
g_param_spec_boolean (MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED,
|
|
"Sim Hot Swap Supported",
|
|
"Whether the modem supports sim hot swap or not.",
|
|
FALSE,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_interface_install_property
|
|
(g_iface,
|
|
g_param_spec_boolean (MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED,
|
|
"Sim Hot Swap Configured",
|
|
"Whether the sim hot swap support is configured correctly.",
|
|
FALSE,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_interface_install_property
|
|
(g_iface,
|
|
g_param_spec_boolean (MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED,
|
|
"Periodic signal quality check disabled",
|
|
"Whether periodic signal quality check is disabled.",
|
|
FALSE,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_interface_install_property
|
|
(g_iface,
|
|
g_param_spec_boolean (MM_IFACE_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED,
|
|
"Periodic access technology check disabled",
|
|
"Whether periodic access technology check is disabled.",
|
|
FALSE,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_interface_install_property
|
|
(g_iface,
|
|
g_param_spec_string (MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING,
|
|
"Carrier config mapping table",
|
|
"Path to the file including the carrier mapping for the module",
|
|
NULL,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
|
|
|
initialized = TRUE;
|
|
}
|
|
|
|
GType
|
|
mm_iface_modem_get_type (void)
|
|
{
|
|
static GType iface_modem_type = 0;
|
|
|
|
if (!G_UNLIKELY (iface_modem_type)) {
|
|
static const GTypeInfo info = {
|
|
sizeof (MMIfaceModem), /* class_size */
|
|
iface_modem_init, /* base_init */
|
|
NULL, /* base_finalize */
|
|
};
|
|
|
|
iface_modem_type = g_type_register_static (G_TYPE_INTERFACE,
|
|
"MMIfaceModem",
|
|
&info,
|
|
0);
|
|
|
|
g_type_interface_add_prerequisite (iface_modem_type, MM_TYPE_BASE_MODEM);
|
|
}
|
|
|
|
return iface_modem_type;
|
|
}
|