Files
ModemManager/src/mm-iface-modem.c
T
Aleksander Morgado 0336b8f89d iface-modem: don't process signal/act updates if interface is shut down
When the interface is shut down the skeleton object no longer exists. Given that
we're allowing this actions from outside the interface, we should be safe and
don't assume that the caller knows exactly the state of the interface.
2012-08-02 09:23:16 +02:00

3963 lines
138 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>
#include <libmm-common.h>
#include "mm-modem-helpers.h"
#include "mm-iface-modem.h"
#include "mm-base-modem.h"
#include "mm-base-modem-at.h"
#include "mm-sim.h"
#include "mm-bearer-list.h"
#include "mm-log.h"
#include "mm-context.h"
#define SIGNAL_QUALITY_RECENT_TIMEOUT_SEC 60
#define SIGNAL_QUALITY_CHECK_TIMEOUT_SEC 30
#define ACCESS_TECHNOLOGIES_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_QUALITY_CHECK_CONTEXT_TAG "signal-quality-check-context-tag"
#define ACCESS_TECHNOLOGIES_CHECK_CONTEXT_TAG "access-technologies-check-context-tag"
static GQuark state_update_context_quark;
static GQuark signal_quality_update_context_quark;
static GQuark signal_quality_check_context_quark;
static GQuark access_technologies_check_context_quark;
/*****************************************************************************/
void
mm_iface_modem_bind_simple_status (MMIfaceModem *self,
MMSimpleStatus *status)
{
MmGdbusModem *skeleton;
g_object_get (self,
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
NULL);
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, "bands",
status, MM_SIMPLE_PROPERTY_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);
}
/*****************************************************************************/
static MMModemState get_current_consolidated_state (MMIfaceModem *self);
typedef struct {
MMBearer *self;
guint others_connected;
} CountOthersConnectedContext;
static void
bearer_list_count_others_connected (MMBearer *bearer,
CountOthersConnectedContext *ctx)
{
/* We can safely compare pointers here */
if (bearer != ctx->self &&
mm_bearer_get_status (bearer) == MM_BEARER_STATUS_CONNECTED) {
ctx->others_connected++;
}
}
static void
bearer_status_changed (MMBearer *bearer,
GParamSpec *pspec,
MMIfaceModem *self)
{
CountOthersConnectedContext ctx;
MMBearerList *list = NULL;
g_object_get (self,
MM_IFACE_MODEM_BEARER_LIST, &list,
NULL);
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_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_current_consolidated_state (self);
break;
}
mm_iface_modem_update_state (self,
new_state,
MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED);
}
g_object_unref (list);
}
MMBearer *
mm_iface_modem_create_bearer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
MMBearer *bearer;
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
return NULL;
bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
return g_object_ref (bearer);
}
static void
create_bearer_ready (MMIfaceModem *self,
GAsyncResult *res,
GSimpleAsyncResult *simple)
{
MMBearer *bearer;
GError *error = NULL;
bearer = MM_IFACE_MODEM_GET_INTERFACE (self)->create_bearer_finish (self,
res,
&error);
if (error)
g_simple_async_result_take_error (simple, error);
else {
MMBearerList *list = NULL;
g_object_get (self,
MM_IFACE_MODEM_BEARER_LIST, &list,
NULL);
if (!mm_bearer_list_add_bearer (list, bearer, &error))
g_simple_async_result_take_error (simple, error);
else {
/* If bearer properly created and added to the list, follow its
* status */
g_signal_connect (bearer,
"notify::" MM_BEARER_STATUS,
(GCallback)bearer_status_changed,
self);
g_simple_async_result_set_op_res_gpointer (simple,
g_object_ref (bearer),
g_object_unref);
}
g_object_unref (bearer);
g_object_unref (list);
}
g_simple_async_result_complete (simple);
g_object_unref (simple);
}
void
mm_iface_modem_create_bearer (MMIfaceModem *self,
MMBearerProperties *properties,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBearerList *list = NULL;
g_object_get (self,
MM_IFACE_MODEM_BEARER_LIST, &list,
NULL);
if (mm_bearer_list_get_count (list) == mm_bearer_list_get_max (list))
g_simple_async_report_error_in_idle (
G_OBJECT (self),
callback,
user_data,
MM_CORE_ERROR,
MM_CORE_ERROR_TOO_MANY,
"Cannot add new bearer: already reached maximum (%u)",
mm_bearer_list_get_count (list));
else
MM_IFACE_MODEM_GET_INTERFACE (self)->create_bearer (
self,
properties,
(GAsyncReadyCallback)create_bearer_ready,
g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
mm_iface_modem_create_bearer));
g_object_unref (list);
}
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)
{
MMBearer *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_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;
}
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 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;
}
/* 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;
gchar *bearer_path;
} HandleDeleteBearerContext;
static void
handle_delete_bearer_context_free (HandleDeleteBearerContext *ctx)
{
g_object_unref (ctx->skeleton);
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_free (ctx->bearer_path);
g_free (ctx);
}
static void
handle_delete_bearer_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleDeleteBearerContext *ctx)
{
MMBearerList *list = NULL;
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;
}
g_object_get (self,
MM_IFACE_MODEM_BEARER_LIST, &list,
NULL);
if (!mm_bearer_list_delete_bearer (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);
g_object_unref (list);
handle_delete_bearer_context_free (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);
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;
g_object_get (self,
MM_IFACE_MODEM_BEARER_LIST, &list,
NULL);
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;
}
/*****************************************************************************/
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_info ("Modem %s: access technology changed (%s -> %s)",
g_dbus_object_get_object_path (G_DBUS_OBJECT (self)),
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 timeout_source;
gboolean running;
} AccessTechnologiesCheckContext;
static void
access_technologies_check_context_free (AccessTechnologiesCheckContext *ctx)
{
if (ctx->timeout_source)
g_source_remove (ctx->timeout_source);
g_free (ctx);
}
static void
access_technologies_check_ready (MMIfaceModem *self,
GAsyncResult *res)
{
GError *error = NULL;
MMModemAccessTechnology access_technologies = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
guint mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY;
AccessTechnologiesCheckContext *ctx;
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies_finish (
self,
res,
&access_technologies,
&mask,
&error)) {
mm_dbg ("Couldn't refresh access technologies: '%s'", error->message);
g_error_free (error);
} else
mm_iface_modem_update_access_technologies (self, access_technologies, mask);
/* Remove the running tag. Note that the context may have been removed by
* mm_iface_modem_shutdown when this function is invoked as a callback of
* load_access_technologies. */
ctx = g_object_get_qdata (G_OBJECT (self), access_technologies_check_context_quark);
if (ctx)
ctx->running = FALSE;
}
static gboolean
periodic_access_technologies_check (MMIfaceModem *self)
{
AccessTechnologiesCheckContext *ctx;
ctx = g_object_get_qdata (G_OBJECT (self), access_technologies_check_context_quark);
/* Only launch a new one if not one running already OR if the last one run
* was more than 15s ago. */
if (!ctx->running) {
ctx->running = TRUE;
MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies (
self,
(GAsyncReadyCallback)access_technologies_check_ready,
NULL);
}
return TRUE;
}
static void
periodic_access_technologies_check_disable (MMIfaceModem *self)
{
if (G_UNLIKELY (!access_technologies_check_context_quark))
access_technologies_check_context_quark = (g_quark_from_static_string (
ACCESS_TECHNOLOGIES_CHECK_CONTEXT_TAG));
/* Clear access technology */
mm_iface_modem_update_access_technologies (self,
MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN,
MM_MODEM_ACCESS_TECHNOLOGY_ANY);
/* Overwriting the data will free the previous context */
g_object_set_qdata (G_OBJECT (self),
access_technologies_check_context_quark,
NULL);
mm_dbg ("Periodic access technology checks disabled");
}
static void
periodic_access_technologies_check_enable (MMIfaceModem *self)
{
AccessTechnologiesCheckContext *ctx;
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies ||
!MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies_finish) {
/* If loading access technology not supported, don't even bother setting up
* a timeout */
return;
}
if (G_UNLIKELY (!access_technologies_check_context_quark))
access_technologies_check_context_quark = (g_quark_from_static_string (
ACCESS_TECHNOLOGIES_CHECK_CONTEXT_TAG));
ctx = g_object_get_qdata (G_OBJECT (self), access_technologies_check_context_quark);
/* If context is already there, we're already enabled */
if (ctx) {
periodic_access_technologies_check (self);
return;
}
/* Create context and keep it as object data */
mm_dbg ("Periodic access technology checks enabled");
ctx = g_new0 (AccessTechnologiesCheckContext, 1);
ctx->timeout_source = g_timeout_add_seconds (ACCESS_TECHNOLOGIES_CHECK_TIMEOUT_SEC,
(GSourceFunc)periodic_access_technologies_check,
self);
g_object_set_qdata_full (G_OBJECT (self),
access_technologies_check_context_quark,
ctx,
(GDestroyNotify)access_technologies_check_context_free);
/* Get first access technology value */
periodic_access_technologies_check (self);
}
/*****************************************************************************/
typedef struct {
time_t last_update;
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 time_t
get_last_signal_quality_update_time (MMIfaceModem *self)
{
SignalQualityUpdateContext *ctx;
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);
return (ctx ? ctx->last_update : 0);
}
static gboolean
expire_signal_quality (MMIfaceModem *self)
{
GVariant *old;
guint signal_quality = 0;
gboolean recent = FALSE;
MmGdbusModem *skeleton = NULL;
SignalQualityUpdateContext *ctx;
g_object_get (self,
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
NULL);
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_dbg ("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 FALSE;
}
static void
update_signal_quality (MMIfaceModem *self,
guint signal_quality,
gboolean expire)
{
SignalQualityUpdateContext *ctx;
MmGdbusModem *skeleton = NULL;
const gchar *dbus_path;
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);
}
/* Keep current timestamp */
ctx->last_update = time (NULL);
/* 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));
dbus_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (self));
mm_info ("Modem %s: signal quality updated (%u)",
dbus_path,
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);
}
/*****************************************************************************/
typedef struct {
guint timeout_source;
gboolean running;
} SignalQualityCheckContext;
static void
signal_quality_check_context_free (SignalQualityCheckContext *ctx)
{
if (ctx->timeout_source)
g_source_remove (ctx->timeout_source);
g_free (ctx);
}
static void
signal_quality_check_ready (MMIfaceModem *self,
GAsyncResult *res)
{
GError *error = NULL;
guint signal_quality;
SignalQualityCheckContext *ctx;
signal_quality = MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality_finish (self,
res,
&error);
if (error) {
mm_dbg ("Couldn't refresh signal quality: '%s'", error->message);
g_error_free (error);
} else
update_signal_quality (self, signal_quality, TRUE);
/* Remove the running tag. Note that the context may have been removed by
* mm_iface_modem_shutdown when this function is invoked as a callback of
* load_signal_quality. */
ctx = g_object_get_qdata (G_OBJECT (self), signal_quality_check_context_quark);
if (ctx)
ctx->running = FALSE;
}
static gboolean
periodic_signal_quality_check (MMIfaceModem *self)
{
SignalQualityCheckContext *ctx;
ctx = g_object_get_qdata (G_OBJECT (self), signal_quality_check_context_quark);
/* Only launch a new one if not one running already OR if the last one run
* was more than 15s ago. */
if (!ctx->running ||
(time (NULL) - get_last_signal_quality_update_time (self) > 15)) {
ctx->running = TRUE;
MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality (
self,
(GAsyncReadyCallback)signal_quality_check_ready,
NULL);
}
return TRUE;
}
static void
periodic_signal_quality_check_disable (MMIfaceModem *self)
{
if (G_UNLIKELY (!signal_quality_check_context_quark))
signal_quality_check_context_quark = (g_quark_from_static_string (
SIGNAL_QUALITY_CHECK_CONTEXT_TAG));
/* Clear signal quality */
update_signal_quality (self, 0, FALSE);
/* Overwriting the data will free the previous context */
g_object_set_qdata (G_OBJECT (self),
signal_quality_check_context_quark,
NULL);
mm_dbg ("Periodic signal quality checks disabled");
}
static void
periodic_signal_quality_check_enable (MMIfaceModem *self)
{
SignalQualityCheckContext *ctx;
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality ||
!MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality_finish) {
/* If loading signal quality not supported, don't even bother setting up
* a timeout */
return;
}
if (G_UNLIKELY (!signal_quality_check_context_quark))
signal_quality_check_context_quark = (g_quark_from_static_string (
SIGNAL_QUALITY_CHECK_CONTEXT_TAG));
ctx = g_object_get_qdata (G_OBJECT (self), signal_quality_check_context_quark);
/* If context is already there, we're already enabled */
if (ctx) {
periodic_signal_quality_check (self);
return;
}
/* Create context and keep it as object data */
mm_dbg ("Periodic signal quality checks enabled");
ctx = g_new0 (SignalQualityCheckContext, 1);
ctx->timeout_source = g_timeout_add_seconds (SIGNAL_QUALITY_CHECK_TIMEOUT_SEC,
(GSourceFunc)periodic_signal_quality_check,
self);
g_object_set_qdata_full (G_OBJECT (self),
signal_quality_check_context_quark,
ctx,
(GDestroyNotify)signal_quality_check_context_free);
/* Get first signal quality value */
periodic_signal_quality_check (self);
}
/*****************************************************************************/
static void
bearer_list_count_connected (MMBearer *bearer,
guint *count)
{
if (mm_bearer_get_status (bearer) == MM_BEARER_STATUS_CONNECTED)
(*count)++;
}
void
mm_iface_modem_update_state (MMIfaceModem *self,
MMModemState new_state,
MMModemStateChangeReason 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);
/* While connected we don't want registration status changes to change
* the modem's state away from CONNECTED. */
if ((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;
}
/* Update state only if different */
if (new_state != old_state) {
const gchar *dbus_path;
dbus_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (self));
mm_info ("Modem%s%s: state changed (%s -> %s)",
dbus_path ? " " : "",
dbus_path ? dbus_path : "",
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)
mm_gdbus_modem_emit_state_changed (skeleton,
old_state,
new_state,
reason);
/* If we go to registered 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_quality_check_enable (self);
periodic_access_technologies_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_quality_check_disable (self);
periodic_access_technologies_check_disable (self);
}
}
if (skeleton)
g_object_unref (skeleton);
if (bearer_list)
g_object_unref (bearer_list);
}
/*****************************************************************************/
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_current_consolidated_state (MMIfaceModem *self)
{
MMModemState consolidated = MM_MODEM_STATE_DISABLED;
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,
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 subystem. 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_dbg ("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_current_consolidated_state (self);
}
void
mm_iface_modem_update_subsystem_state (MMIfaceModem *self,
const gchar *subsystem,
MMModemState new_state,
MMModemStateChangeReason reason)
{
MMModemState consolidated;
/* 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, subsystem, new_state);
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 (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;
} 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)
{
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_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;
}
modem_state = MM_MODEM_STATE_UNKNOWN;
g_object_get (self,
MM_IFACE_MODEM_STATE, &modem_state,
NULL);
if (modem_state < MM_MODEM_STATE_DISABLED) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot reset modem: not initialized/unlocked yet");
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)
{
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_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;
}
modem_state = MM_MODEM_STATE_UNKNOWN;
g_object_get (self,
MM_IFACE_MODEM_STATE, &modem_state,
NULL);
if (modem_state < MM_MODEM_STATE_DISABLED) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot reset the modem to factory defaults: "
"not initialized/unlocked yet");
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;
}
/*****************************************************************************/
/* BANDS */
typedef struct {
MMIfaceModem *self;
MmGdbusModem *skeleton;
GSimpleAsyncResult *result;
GArray *bands_array;
} SetBandsContext;
static void
set_bands_context_complete_and_free (SetBandsContext *ctx)
{
g_simple_async_result_complete_in_idle (ctx->result);
g_object_unref (ctx->result);
g_object_unref (ctx->self);
g_object_unref (ctx->skeleton);
g_array_unref (ctx->bands_array);
g_free (ctx);
}
gboolean
mm_iface_modem_set_bands_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
static void
set_bands_ready (MMIfaceModem *self,
GAsyncResult *res,
SetBandsContext *ctx)
{
GError *error = NULL;
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_bands_finish (self, res, &error))
g_simple_async_result_take_error (ctx->result, error);
else {
mm_gdbus_modem_set_bands (ctx->skeleton,
mm_common_bands_garray_to_variant (ctx->bands_array));
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
}
set_bands_context_complete_and_free (ctx);
}
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) {
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 *)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_bands (MMIfaceModem *self,
GArray *bands_array,
GAsyncReadyCallback callback,
gpointer user_data)
{
SetBandsContext *ctx;
GArray *supported_bands_array;
GError *error = NULL;
/* If setting allowed bands is not implemented, report an error */
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_bands ||
!MM_IFACE_MODEM_GET_INTERFACE (self)->set_bands_finish) {
g_simple_async_report_error_in_idle (G_OBJECT (self),
callback,
user_data,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Setting allowed bands not supported");
return;
}
/* Setup context */
ctx = g_new0 (SetBandsContext, 1);
ctx->self = g_object_ref (self);
ctx->result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
mm_iface_modem_set_bands);
g_object_get (self,
MM_IFACE_MODEM_DBUS_SKELETON, &ctx->skeleton,
NULL);
ctx->bands_array = g_array_ref (bands_array);
/* Get list of supported bands */
supported_bands_array = (mm_common_bands_variant_to_garray (
mm_gdbus_modem_get_supported_bands (ctx->skeleton)));
/* Validate input list of bands */
if (!validate_bands (supported_bands_array,
ctx->bands_array,
&error)) {
g_array_unref (supported_bands_array);
g_simple_async_result_take_error (ctx->result, error);
set_bands_context_complete_and_free (ctx);
return;
}
MM_IFACE_MODEM_GET_INTERFACE (self)->set_bands (
self,
bands_array,
(GAsyncReadyCallback)set_bands_ready,
ctx);
g_array_unref (supported_bands_array);
}
typedef struct {
MmGdbusModem *skeleton;
GDBusMethodInvocation *invocation;
MMIfaceModem *self;
GVariant *bands;
} HandleSetBandsContext;
static void
handle_set_bands_context_free (HandleSetBandsContext *ctx)
{
g_variant_unref (ctx->bands);
g_object_unref (ctx->skeleton);
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_free (ctx);
}
static void
handle_set_bands_ready (MMIfaceModem *self,
GAsyncResult *res,
HandleSetBandsContext *ctx)
{
GError *error = NULL;
if (!mm_iface_modem_set_bands_finish (self, res, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
else
mm_gdbus_modem_complete_set_bands (ctx->skeleton, ctx->invocation);
handle_set_bands_context_free (ctx);
}
static void
handle_set_bands_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleSetBandsContext *ctx)
{
GArray *bands_array;
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_bands_context_free (ctx);
return;
}
modem_state = MM_MODEM_STATE_UNKNOWN;
g_object_get (self,
MM_IFACE_MODEM_STATE, &modem_state,
NULL);
if (modem_state < MM_MODEM_STATE_DISABLED) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot set allowed bands: "
"not initialized/unlocked yet");
handle_set_bands_context_free (ctx);
return;
}
bands_array = mm_common_bands_variant_to_garray (ctx->bands);
mm_iface_modem_set_bands (MM_IFACE_MODEM (self),
bands_array,
(GAsyncReadyCallback)handle_set_bands_ready,
ctx);
g_array_unref (bands_array);
}
static gboolean
handle_set_bands (MmGdbusModem *skeleton,
GDBusMethodInvocation *invocation,
GVariant *bands_variant,
MMIfaceModem *self)
{
HandleSetBandsContext *ctx;
ctx = g_new (HandleSetBandsContext, 1);
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_bands_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
/* ALLOWED MODES */
typedef struct {
MMIfaceModem *self;
MmGdbusModem *skeleton;
GSimpleAsyncResult *result;
MMModemMode allowed;
MMModemMode preferred;
} SetAllowedModesContext;
static void
set_allowed_modes_context_complete_and_free (SetAllowedModesContext *ctx)
{
g_simple_async_result_complete_in_idle (ctx->result);
g_object_unref (ctx->result);
g_object_unref (ctx->self);
g_object_unref (ctx->skeleton);
g_free (ctx);
}
gboolean
mm_iface_modem_set_allowed_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
static void
set_allowed_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
SetAllowedModesContext *ctx)
{
GError *error = NULL;
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_allowed_modes_finish (self, res, &error))
g_simple_async_result_take_error (ctx->result, error);
else {
mm_gdbus_modem_set_allowed_modes (ctx->skeleton, ctx->allowed);
mm_gdbus_modem_set_preferred_mode (ctx->skeleton, ctx->preferred);
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
}
set_allowed_modes_context_complete_and_free (ctx);
}
void
mm_iface_modem_set_allowed_modes (MMIfaceModem *self,
MMModemMode allowed,
MMModemMode preferred,
GAsyncReadyCallback callback,
gpointer user_data)
{
SetAllowedModesContext *ctx;
MMModemMode supported;
MMModemMode not_supported;
/* If setting allowed modes is not implemented, report an error */
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_allowed_modes ||
!MM_IFACE_MODEM_GET_INTERFACE (self)->set_allowed_modes_finish) {
g_simple_async_report_error_in_idle (G_OBJECT (self),
callback,
user_data,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Setting allowed modes not supported");
return;
}
/* Setup context */
ctx = g_new0 (SetAllowedModesContext, 1);
ctx->self = g_object_ref (self);
ctx->result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
mm_iface_modem_set_allowed_modes);
g_object_get (self,
MM_IFACE_MODEM_DBUS_SKELETON, &ctx->skeleton,
NULL);
/* Get list of supported modes */
supported = mm_gdbus_modem_get_supported_modes (ctx->skeleton);
/* Whenever we get 'any', just reset to be equal to the list of supported modes */
if (allowed == MM_MODEM_MODE_ANY)
allowed = supported;
ctx->allowed = allowed;
ctx->preferred = preferred;
/* Check if we already are in the requested setup */
if (mm_gdbus_modem_get_allowed_modes (ctx->skeleton) == allowed &&
mm_gdbus_modem_get_preferred_mode (ctx->skeleton) == preferred) {
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
set_allowed_modes_context_complete_and_free (ctx);
return;
}
/* Check if any of the modes being allowed is not supported */
not_supported = ((supported ^ allowed) & allowed);
/* Ensure allowed is a subset of supported */
if (not_supported) {
gchar *not_supported_str;
gchar *supported_str;
not_supported_str = mm_modem_mode_build_string_from_mask (not_supported);
supported_str = mm_modem_mode_build_string_from_mask (supported);
g_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Some of the allowed modes (%s) are not "
"supported (%s)",
not_supported_str,
supported_str);
g_free (supported_str);
g_free (not_supported_str);
set_allowed_modes_context_complete_and_free (ctx);
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_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Preferred mode (%s) is not allowed (%s)",
preferred_str,
allowed_str);
g_free (preferred_str);
g_free (allowed_str);
set_allowed_modes_context_complete_and_free (ctx);
return;
}
MM_IFACE_MODEM_GET_INTERFACE (self)->set_allowed_modes (self,
allowed,
preferred,
(GAsyncReadyCallback)set_allowed_modes_ready,
ctx);
}
typedef struct {
MmGdbusModem *skeleton;
GDBusMethodInvocation *invocation;
MMIfaceModem *self;
MMModemMode allowed;
MMModemMode preferred;
} HandleSetAllowedModesContext;
static void
handle_set_allowed_modes_context_free (HandleSetAllowedModesContext *ctx)
{
g_object_unref (ctx->skeleton);
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_free (ctx);
}
static void
handle_set_allowed_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
HandleSetAllowedModesContext *ctx)
{
GError *error = NULL;
if (!mm_iface_modem_set_allowed_modes_finish (self, res, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
else
mm_gdbus_modem_complete_set_allowed_modes (ctx->skeleton, ctx->invocation);
handle_set_allowed_modes_context_free (ctx);
}
static void
handle_set_allowed_modes_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleSetAllowedModesContext *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_allowed_modes_context_free (ctx);
return;
}
modem_state = MM_MODEM_STATE_UNKNOWN;
g_object_get (self,
MM_IFACE_MODEM_STATE, &modem_state,
NULL);
if (modem_state < MM_MODEM_STATE_DISABLED) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot set allowed modes: "
"not initialized/unlocked yet");
handle_set_allowed_modes_context_free (ctx);
return;
}
mm_iface_modem_set_allowed_modes (MM_IFACE_MODEM (self),
ctx->allowed,
ctx->preferred,
(GAsyncReadyCallback)handle_set_allowed_modes_ready,
ctx);
}
static gboolean
handle_set_allowed_modes (MmGdbusModem *skeleton,
GDBusMethodInvocation *invocation,
guint allowed,
guint preferred,
MMIfaceModem *self)
{
HandleSetAllowedModesContext *ctx;
ctx = g_new (HandleSetAllowedModesContext, 1);
ctx->skeleton = g_object_ref (skeleton);
ctx->invocation = g_object_ref (invocation);
ctx->self = g_object_ref (self);
ctx->allowed = allowed;
ctx->preferred = preferred;
mm_base_modem_authorize (MM_BASE_MODEM (self),
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
(GAsyncReadyCallback)handle_set_allowed_modes_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
typedef struct _UnlockCheckContext UnlockCheckContext;
struct _UnlockCheckContext {
MMIfaceModem *self;
guint pin_check_tries;
guint pin_check_timeout_id;
GSimpleAsyncResult *result;
MmGdbusModem *skeleton;
};
static UnlockCheckContext *
unlock_check_context_new (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
UnlockCheckContext *ctx;
ctx = g_new0 (UnlockCheckContext, 1);
ctx->self = g_object_ref (self);
ctx->result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
unlock_check_context_new);
g_object_get (ctx->self,
MM_IFACE_MODEM_DBUS_SKELETON, &ctx->skeleton,
NULL);
g_assert (ctx->skeleton != NULL);
return ctx;
}
static void
unlock_check_context_free (UnlockCheckContext *ctx)
{
g_object_unref (ctx->self);
g_object_unref (ctx->result);
g_object_unref (ctx->skeleton);
g_free (ctx);
}
static void
reinitialize_ready (MMBaseModem *self,
GAsyncResult *res)
{
GError *error = NULL;
mm_base_modem_initialize_finish (self, res, &error);
if (error) {
mm_warn ("Modem reinitialization failed: '%s'", error->message);
g_error_free (error);
}
}
static void
modem_after_sim_unlock_ready (MMIfaceModem *self,
GAsyncResult *res)
{
GError *error = NULL;
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_sim_unlock_finish (self, res, &error)) {
mm_warn ("After SIM unlock failed setup: '%s'", error->message);
g_error_free (error);
}
/* Go on */
mm_base_modem_initialize (MM_BASE_MODEM (self),
(GAsyncReadyCallback) reinitialize_ready,
NULL);
}
static gboolean
restart_initialize_idle (MMIfaceModem *self)
{
/* If we were asked to run something after having sent the PIN unlock,
* do it now. This may be just a timeout or some other command that gives us
* the real SIM state */
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_IFACE_MODEM_GET_INTERFACE (self)->modem_after_sim_unlock(
self,
(GAsyncReadyCallback)modem_after_sim_unlock_ready,
NULL);
} else {
/* If no wait needed, just go on */
mm_base_modem_initialize (MM_BASE_MODEM (self),
(GAsyncReadyCallback) reinitialize_ready,
NULL);
}
return FALSE;
}
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)
g_idle_add ((GSourceFunc)restart_initialize_idle, self);
}
} 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_unlock_check_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
return MM_MODEM_LOCK_UNKNOWN;
return (MMModemLock) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
}
static void unlock_check_ready (MMIfaceModem *self,
GAsyncResult *res,
UnlockCheckContext *ctx);
static gboolean
unlock_check_again (UnlockCheckContext *ctx)
{
ctx->pin_check_timeout_id = 0;
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required (
ctx->self,
(GAsyncReadyCallback)unlock_check_ready,
ctx);
return FALSE;
}
static void
unlock_check_ready (MMIfaceModem *self,
GAsyncResult *res,
UnlockCheckContext *ctx)
{
GError *error = NULL;
MMModemLock lock;
lock = MM_IFACE_MODEM_GET_INTERFACE (self)->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,
MM_CORE_ERROR,
MM_CORE_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_simple_async_result_take_error (ctx->result, error);
g_simple_async_result_complete (ctx->result);
unlock_check_context_free (ctx);
return;
}
mm_dbg ("Couldn't check if unlock required: '%s'",
error->message);
g_error_free (error);
/* Retry up to 3 times */
if (mm_gdbus_modem_get_unlock_required (ctx->skeleton) != MM_MODEM_LOCK_NONE &&
++ctx->pin_check_tries < 3) {
mm_dbg ("Retrying (%u) unlock required check", ctx->pin_check_tries);
if (ctx->pin_check_timeout_id)
g_source_remove (ctx->pin_check_timeout_id);
ctx->pin_check_timeout_id = g_timeout_add_seconds (
2,
(GSourceFunc)unlock_check_again,
ctx);
return;
}
/* If reached max retries and still reporting error, set UNKNOWN */
lock = MM_MODEM_LOCK_UNKNOWN;
}
/* Update lock status and modem status if needed */
set_lock_status (self, ctx->skeleton, lock);
g_simple_async_result_set_op_res_gpointer (ctx->result,
GUINT_TO_POINTER (lock),
NULL);
g_simple_async_result_complete (ctx->result);
unlock_check_context_free (ctx);
}
void
mm_iface_modem_unlock_check (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
UnlockCheckContext *ctx;
ctx = unlock_check_context_new (self, callback, user_data);
/* If we're already unlocked, we're done */
if (mm_gdbus_modem_get_unlock_required (ctx->skeleton) != MM_MODEM_LOCK_NONE &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required (
self,
(GAsyncReadyCallback)unlock_check_ready,
ctx);
return;
}
/* Just assume that no lock is required */
g_simple_async_result_set_op_res_gpointer (ctx->result,
GUINT_TO_POINTER (MM_MODEM_LOCK_NONE),
NULL);
g_simple_async_result_complete_in_idle (ctx->result);
unlock_check_context_free (ctx);
}
/*****************************************************************************/
/* Unlock retry count */
gboolean
mm_iface_modem_update_unlock_retries_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
return FALSE;
return g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res));
}
static void
update_unlock_retries (MMIfaceModem *self,
MMUnlockRetries *unlock_retries)
{
MmGdbusModem *skeleton = NULL;
GError *error = NULL;
GVariant *previous_dictionary;
MMUnlockRetries *previous_unlock_retries;
g_object_get (self,
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
NULL);
previous_dictionary = mm_gdbus_modem_get_unlock_retries (skeleton);
previous_unlock_retries = mm_unlock_retries_new_from_dictionary (previous_dictionary);
if (error) {
mm_warn ("Couldn't build previous unlock retries: '%s'", error->message);
g_error_free (error);
} else {
/* 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);
}
static void
unlock_retries_ready (MMIfaceModem *self,
GAsyncResult *res,
GSimpleAsyncResult *simple)
{
GError *error = NULL;
MMUnlockRetries *unlock_retries;
unlock_retries = MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_retries_finish (self, res, &error);
if (!unlock_retries) {
g_simple_async_result_take_error (simple, error);
g_simple_async_result_complete (simple);
g_object_unref (simple);
return;
}
/* Update the dictionary in the DBus interface */
update_unlock_retries (self, unlock_retries);
g_object_unref (unlock_retries);
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
g_simple_async_result_complete (simple);
g_object_unref (simple);
}
void
mm_iface_modem_update_unlock_retries (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *result;
result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
mm_iface_modem_update_unlock_retries);
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)unlock_retries_ready,
result);
return;
}
/* Return FALSE when we cannot load unlock retries */
g_simple_async_result_set_op_res_gboolean (result, FALSE);
g_simple_async_result_complete_in_idle (result);
g_object_unref (result);
}
/*****************************************************************************/
/* MODEM DISABLING */
typedef struct _DisablingContext DisablingContext;
static void interface_disabling_step (DisablingContext *ctx);
typedef enum {
DISABLING_STEP_FIRST,
DISABLING_STEP_CURRENT_BANDS,
DISABLING_STEP_ALLOWED_MODES,
DISABLING_STEP_MODEM_POWER_DOWN,
DISABLING_STEP_CLOSE_PORTS,
DISABLING_STEP_LAST
} DisablingStep;
struct _DisablingContext {
MMIfaceModem *self;
MMAtSerialPort *primary;
MMAtSerialPort *secondary;
MMQcdmSerialPort *qcdm;
DisablingStep step;
MMModemState previous_state;
gboolean disabled;
GSimpleAsyncResult *result;
MmGdbusModem *skeleton;
};
static DisablingContext *
disabling_context_new (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
DisablingContext *ctx;
ctx = g_new0 (DisablingContext, 1);
ctx->self = g_object_ref (self);
ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
ctx->qcdm = mm_base_modem_get_port_qcdm (MM_BASE_MODEM (self));
ctx->result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
disabling_context_new);
ctx->step = DISABLING_STEP_FIRST;
g_object_get (ctx->self,
MM_IFACE_MODEM_DBUS_SKELETON, &ctx->skeleton,
MM_IFACE_MODEM_STATE, &ctx->previous_state,
NULL);
g_assert (ctx->skeleton != NULL);
mm_iface_modem_update_state (ctx->self,
MM_MODEM_STATE_DISABLING,
MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED);
return ctx;
}
static void
disabling_context_complete_and_free (DisablingContext *ctx)
{
g_simple_async_result_complete_in_idle (ctx->result);
if (ctx->disabled)
mm_iface_modem_update_state (ctx->self,
MM_MODEM_STATE_DISABLED,
MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED);
else
/* Fallback to previous state */
mm_iface_modem_update_state (ctx->self,
ctx->previous_state,
MM_MODEM_STATE_CHANGE_REASON_UNKNOWN);
g_object_unref (ctx->self);
if (ctx->primary)
g_object_unref (ctx->primary);
if (ctx->secondary)
g_object_unref (ctx->secondary);
if (ctx->qcdm)
g_object_unref (ctx->qcdm);
g_object_unref (ctx->result);
g_object_unref (ctx->skeleton);
g_free (ctx);
}
gboolean
mm_iface_modem_disable_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
static void
modem_power_down_ready (MMIfaceModem *self,
GAsyncResult *res,
DisablingContext *ctx)
{
GError *error = NULL;
MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down_finish (self, res, &error);
if (error) {
g_simple_async_result_take_error (ctx->result, error);
disabling_context_complete_and_free (ctx);
return;
}
mm_dbg ("Modem properly powered down...");
/* Go on to next step */
ctx->step++;
interface_disabling_step (ctx);
}
static void
interface_disabling_step (DisablingContext *ctx)
{
switch (ctx->step) {
case DISABLING_STEP_FIRST:
/* Fall down to next step */
ctx->step++;
case DISABLING_STEP_CURRENT_BANDS:
/* Clear current bands */
mm_gdbus_modem_set_bands (ctx->skeleton, mm_common_build_bands_unknown ());
/* Fall down to next step */
ctx->step++;
case DISABLING_STEP_ALLOWED_MODES:
/* Clear allowed/preferred modes */
mm_gdbus_modem_set_allowed_modes (ctx->skeleton, MM_MODEM_MODE_NONE);
mm_gdbus_modem_set_preferred_mode (ctx->skeleton, MM_MODEM_MODE_NONE);
/* Fall down to next step */
ctx->step++;
case DISABLING_STEP_MODEM_POWER_DOWN:
/* CFUN=0 is dangerous and often will shoot devices in the head (that's
* what it's supposed to do). So don't use CFUN=0 by default, but let
* specific plugins use it when they know it's safe to do so. For
* example, CFUN=0 will often make phones turn themselves off, but some
* dedicated devices (ex Sierra WWAN cards) will just turn off their
* radio but otherwise still work.
*/
if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_down &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_down_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_down (
ctx->self,
(GAsyncReadyCallback)modem_power_down_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
case DISABLING_STEP_CLOSE_PORTS:
/* While the modem is enabled ports are kept open, so we need to close
* them when the modem gets disabled. As this (should) be the last
* closing in order to get it really closed (open count = 1), it should
* be safe to check whether they are really open before trying to close.
*/
mm_dbg ("Closing all ports...");
if (ctx->primary && mm_serial_port_is_open (MM_SERIAL_PORT (ctx->primary)))
mm_serial_port_close (MM_SERIAL_PORT (ctx->primary));
if (ctx->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (ctx->secondary)))
mm_serial_port_close (MM_SERIAL_PORT (ctx->secondary));
if (ctx->qcdm && mm_serial_port_is_open (MM_SERIAL_PORT (ctx->qcdm)))
mm_serial_port_close (MM_SERIAL_PORT (ctx->qcdm));
/* Fall down to next step */
ctx->step++;
case DISABLING_STEP_LAST:
/* We are done without errors! */
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
ctx->disabled = TRUE;
disabling_context_complete_and_free (ctx);
return;
}
g_assert_not_reached ();
}
void
mm_iface_modem_disable (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
interface_disabling_step (disabling_context_new (self,
callback,
user_data));
}
/*****************************************************************************/
/* MODEM ENABLING */
typedef struct _EnablingContext EnablingContext;
static void interface_enabling_step (EnablingContext *ctx);
typedef enum {
ENABLING_STEP_FIRST,
ENABLING_STEP_OPEN_PORTS,
ENABLING_STEP_FLASH_PORT,
ENABLING_STEP_MODEM_INIT,
ENABLING_STEP_MODEM_POWER_UP,
ENABLING_STEP_MODEM_AFTER_POWER_UP,
ENABLING_STEP_FLOW_CONTROL,
ENABLING_STEP_SUPPORTED_CHARSETS,
ENABLING_STEP_CHARSET,
ENABLING_STEP_ALLOWED_MODES,
ENABLING_STEP_CURRENT_BANDS,
ENABLING_STEP_LAST
} EnablingStep;
struct _EnablingContext {
MMIfaceModem *self;
MMAtSerialPort *primary;
gboolean primary_open;
MMAtSerialPort *secondary;
gboolean secondary_open;
MMQcdmSerialPort *qcdm;
gboolean qcdm_open;
EnablingStep step;
MMModemCharset supported_charsets;
const MMModemCharset *current_charset;
gboolean enabled;
GSimpleAsyncResult *result;
GCancellable *cancellable;
MmGdbusModem *skeleton;
};
static EnablingContext *
enabling_context_new (MMIfaceModem *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
EnablingContext *ctx;
ctx = g_new0 (EnablingContext, 1);
ctx->self = g_object_ref (self);
ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
ctx->qcdm = mm_base_modem_get_port_qcdm (MM_BASE_MODEM (self));
ctx->cancellable = g_object_ref (cancellable);
ctx->result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
enabling_context_new);
ctx->step = ENABLING_STEP_FIRST;
g_object_get (ctx->self,
MM_IFACE_MODEM_DBUS_SKELETON, &ctx->skeleton,
NULL);
g_assert (ctx->skeleton != NULL);
mm_iface_modem_update_state (ctx->self,
MM_MODEM_STATE_ENABLING,
MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED);
return ctx;
}
static void
enabling_context_complete_and_free (EnablingContext *ctx)
{
g_simple_async_result_complete_in_idle (ctx->result);
if (ctx->enabled)
mm_iface_modem_update_state (ctx->self,
MM_MODEM_STATE_ENABLED,
MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED);
else {
MMModemLock lock;
/* Fallback to DISABLED/LOCKED */
lock = mm_gdbus_modem_get_unlock_required (ctx->skeleton);
mm_iface_modem_update_state (
ctx->self,
((lock == MM_MODEM_LOCK_NONE ||
lock == MM_MODEM_LOCK_SIM_PIN2 ||
lock == MM_MODEM_LOCK_SIM_PUK2) ?
MM_MODEM_STATE_DISABLED :
MM_MODEM_STATE_LOCKED),
MM_MODEM_STATE_CHANGE_REASON_UNKNOWN);
/* Close the ports if enabling failed */
if (ctx->primary_open)
mm_serial_port_close (MM_SERIAL_PORT (ctx->primary));
if (ctx->secondary_open)
mm_serial_port_close (MM_SERIAL_PORT (ctx->secondary));
if (ctx->qcdm_open)
mm_serial_port_close (MM_SERIAL_PORT (ctx->qcdm));
}
g_object_unref (ctx->self);
if (ctx->primary)
g_object_unref (ctx->primary);
if (ctx->secondary)
g_object_unref (ctx->secondary);
if (ctx->qcdm)
g_object_unref (ctx->qcdm);
g_object_unref (ctx->result);
g_object_unref (ctx->cancellable);
g_object_unref (ctx->skeleton);
g_free (ctx);
}
static gboolean
enabling_context_complete_and_free_if_cancelled (EnablingContext *ctx)
{
if (!g_cancellable_is_cancelled (ctx->cancellable))
return FALSE;
g_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
MM_CORE_ERROR_CANCELLED,
"Interface enabling cancelled");
enabling_context_complete_and_free (ctx);
return TRUE;
}
gboolean
mm_iface_modem_enable_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
#undef VOID_REPLY_READY_FN
#define VOID_REPLY_READY_FN(NAME) \
static void \
NAME##_ready (MMIfaceModem *self, \
GAsyncResult *res, \
EnablingContext *ctx) \
{ \
GError *error = NULL; \
\
MM_IFACE_MODEM_GET_INTERFACE (self)->NAME##_finish (self, res, &error); \
if (error) { \
g_simple_async_result_take_error (ctx->result, error); \
enabling_context_complete_and_free (ctx); \
return; \
} \
\
/* Go on to next step */ \
ctx->step++; \
interface_enabling_step (ctx); \
}
VOID_REPLY_READY_FN (modem_init);
VOID_REPLY_READY_FN (modem_power_up);
VOID_REPLY_READY_FN (modem_after_power_up);
VOID_REPLY_READY_FN (setup_flow_control);
static void
load_supported_charsets_ready (MMIfaceModem *self,
GAsyncResult *res,
EnablingContext *ctx)
{
GError *error = NULL;
ctx->supported_charsets =
MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets_finish (self, res, &error);
if (error) {
mm_warn ("couldn't load Supported Charsets: '%s'", error->message);
g_error_free (error);
}
/* Go on to next step */
ctx->step++;
interface_enabling_step (ctx);
}
static void
setup_charset_ready (MMIfaceModem *self,
GAsyncResult *res,
EnablingContext *ctx)
{
GError *error = NULL;
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset_finish (self, res, &error)) {
mm_dbg ("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_enabling_step (ctx);
}
static void
load_allowed_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
EnablingContext *ctx)
{
MMModemMode allowed = MM_MODEM_MODE_NONE;
MMModemMode preferred = MM_MODEM_MODE_NONE;
GError *error = NULL;
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_allowed_modes_finish (self,
res,
&allowed,
&preferred,
&error)) {
/* Errors when getting allowed/preferred won't be critical */
mm_warn ("couldn't load current allowed/preferred modes: '%s'", error->message);
g_error_free (error);
/* If errors getting allowed modes, assume allowed=supported,
* and none preferred */
allowed = mm_gdbus_modem_get_supported_modes (ctx->skeleton);
preferred = MM_MODEM_MODE_NONE;
}
mm_gdbus_modem_set_allowed_modes (ctx->skeleton, allowed);
mm_gdbus_modem_set_preferred_mode (ctx->skeleton, preferred);
/* Done, Go on to next step */
ctx->step++;
interface_enabling_step (ctx);
}
static void
load_current_bands_ready (MMIfaceModem *self,
GAsyncResult *res,
EnablingContext *ctx)
{
GArray *bands_array;
GError *error = NULL;
bands_array = MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands_finish (self, res, &error);
if (bands_array) {
mm_gdbus_modem_set_bands (ctx->skeleton,
mm_common_bands_garray_to_variant (bands_array));
g_array_unref (bands_array);
} else
mm_gdbus_modem_set_bands (ctx->skeleton, mm_common_build_bands_unknown ());
/* Errors when getting current bands won't be critical */
if (error) {
mm_warn ("couldn't load current Bands: '%s'", error->message);
g_error_free (error);
}
/* Done, Go on to next step */
ctx->step++;
interface_enabling_step (ctx);
}
static void
interface_enabling_flash_done (MMSerialPort *port,
GError *error,
gpointer user_data)
{
EnablingContext *ctx = user_data;
if (error) {
g_simple_async_result_set_from_error (ctx->result, error);
enabling_context_complete_and_free (ctx);
return;
}
/* Go on to next step */
ctx->step++;
interface_enabling_step (ctx);
}
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 (EnablingContext *ctx)
{
/* Don't run new steps if we're cancelled */
if (enabling_context_complete_and_free_if_cancelled (ctx))
return;
switch (ctx->step) {
case ENABLING_STEP_FIRST:
/* Fall down to next step */
ctx->step++;
case ENABLING_STEP_OPEN_PORTS: {
GError *error = NULL;
/* Open primary port */
if (!ctx->primary) {
g_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Cannot enable: no primary port");
enabling_context_complete_and_free (ctx);
return;
}
if (!mm_serial_port_open (MM_SERIAL_PORT (ctx->primary), &error)) {
g_simple_async_result_take_error (ctx->result, error);
enabling_context_complete_and_free (ctx);
return;
}
ctx->primary_open = TRUE;
/* If there is a secondary AT port, open it */
if (ctx->secondary) {
if (!mm_serial_port_open (MM_SERIAL_PORT (ctx->secondary), &error)) {
g_simple_async_result_take_error (ctx->result, error);
enabling_context_complete_and_free (ctx);
return;
}
ctx->secondary_open = TRUE;
}
/* If there is a qcdm AT port, open it */
if (ctx->qcdm) {
if (!mm_serial_port_open (MM_SERIAL_PORT (ctx->qcdm), &error)) {
g_simple_async_result_take_error (ctx->result, error);
enabling_context_complete_and_free (ctx);
return;
}
ctx->qcdm_open = TRUE;
}
/* Fall down to next step */
ctx->step++;
}
case ENABLING_STEP_FLASH_PORT:
/* Flash port */
mm_serial_port_flash (MM_SERIAL_PORT (ctx->primary),
100,
FALSE,
interface_enabling_flash_done,
ctx);
return;
case ENABLING_STEP_MODEM_INIT:
if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_init &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_init_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_init (
ctx->self,
(GAsyncReadyCallback)modem_init_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
case ENABLING_STEP_MODEM_POWER_UP:
if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_up &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_up_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_up (
ctx->self,
(GAsyncReadyCallback)modem_power_up_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
case ENABLING_STEP_MODEM_AFTER_POWER_UP:
if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_after_power_up &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_after_power_up_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_after_power_up (
ctx->self,
(GAsyncReadyCallback)modem_after_power_up_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
case ENABLING_STEP_FLOW_CONTROL:
if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->setup_flow_control &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->setup_flow_control_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->setup_flow_control (
ctx->self,
(GAsyncReadyCallback)setup_flow_control_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
case ENABLING_STEP_SUPPORTED_CHARSETS:
if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_charsets &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_charsets_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_charsets (
ctx->self,
(GAsyncReadyCallback)load_supported_charsets_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
case ENABLING_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 (ctx->self)->setup_charset &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->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 (ctx->self)->setup_charset (
ctx->self,
*ctx->current_charset,
(GAsyncReadyCallback)setup_charset_ready,
ctx);
return;
}
g_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Failed to find a usable modem character set");
enabling_context_complete_and_free (ctx);
return;
}
/* Fall down to next step */
ctx->step++;
case ENABLING_STEP_ALLOWED_MODES:
if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_allowed_modes &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_allowed_modes_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_allowed_modes (
ctx->self,
(GAsyncReadyCallback)load_allowed_modes_ready,
ctx);
return;
}
/* If no way to get allowed modes, assume allowed=supported,
* and none preferred */
mm_gdbus_modem_set_allowed_modes (ctx->skeleton,
mm_gdbus_modem_get_supported_modes (ctx->skeleton));
mm_gdbus_modem_set_preferred_mode (ctx->skeleton, MM_MODEM_MODE_NONE);
/* Fall down to next step */
ctx->step++;
case ENABLING_STEP_CURRENT_BANDS:
if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_bands &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_bands_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_bands (
ctx->self,
(GAsyncReadyCallback)load_current_bands_ready,
ctx);
return;
} else
mm_gdbus_modem_set_bands (ctx->skeleton, mm_common_build_bands_unknown ());
/* Fall down to next step */
ctx->step++;
case ENABLING_STEP_LAST:
/* We are done without errors! */
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
ctx->enabled = TRUE;
enabling_context_complete_and_free (ctx);
return;
}
g_assert_not_reached ();
}
void
mm_iface_modem_enable (MMIfaceModem *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
interface_enabling_step (enabling_context_new (self,
cancellable,
callback,
user_data));
}
/*****************************************************************************/
/* MODEM INITIALIZATION */
typedef struct _InitializationContext InitializationContext;
static void interface_initialization_step (InitializationContext *ctx);
typedef enum {
INITIALIZATION_STEP_FIRST,
INITIALIZATION_STEP_CURRENT_CAPABILITIES,
INITIALIZATION_STEP_MODEM_CAPABILITIES,
INITIALIZATION_STEP_BEARERS,
INITIALIZATION_STEP_MANUFACTURER,
INITIALIZATION_STEP_MODEL,
INITIALIZATION_STEP_REVISION,
INITIALIZATION_STEP_EQUIPMENT_ID,
INITIALIZATION_STEP_DEVICE_ID,
INITIALIZATION_STEP_OWN_NUMBERS,
INITIALIZATION_STEP_UNLOCK_REQUIRED,
INITIALIZATION_STEP_UNLOCK_RETRIES,
INITIALIZATION_STEP_SIM,
INITIALIZATION_STEP_SUPPORTED_MODES,
INITIALIZATION_STEP_SUPPORTED_BANDS,
INITIALIZATION_STEP_LAST
} InitializationStep;
struct _InitializationContext {
MMIfaceModem *self;
InitializationStep step;
GSimpleAsyncResult *result;
GCancellable *cancellable;
MmGdbusModem *skeleton;
GError *fatal_error;
};
static InitializationContext *
initialization_context_new (MMIfaceModem *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
InitializationContext *ctx;
ctx = g_new0 (InitializationContext, 1);
ctx->self = g_object_ref (self);
ctx->cancellable = g_object_ref (cancellable);
ctx->result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
initialization_context_new);
ctx->step = INITIALIZATION_STEP_FIRST;
g_object_get (ctx->self,
MM_IFACE_MODEM_DBUS_SKELETON, &ctx->skeleton,
NULL);
g_assert (ctx->skeleton != NULL);
return ctx;
}
static void
initialization_context_complete_and_free (InitializationContext *ctx)
{
g_assert (ctx->fatal_error == NULL);
g_simple_async_result_complete_in_idle (ctx->result);
g_object_unref (ctx->cancellable);
g_object_unref (ctx->self);
g_object_unref (ctx->result);
g_object_unref (ctx->skeleton);
g_free (ctx);
}
static gboolean
initialization_context_complete_and_free_if_cancelled (InitializationContext *ctx)
{
if (!g_cancellable_is_cancelled (ctx->cancellable))
return FALSE;
g_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
MM_CORE_ERROR_CANCELLED,
"Interface initialization cancelled");
initialization_context_complete_and_free (ctx);
return TRUE;
}
#undef STR_REPLY_READY_FN
#define STR_REPLY_READY_FN(NAME,DISPLAY) \
static void \
load_##NAME##_ready (MMIfaceModem *self, \
GAsyncResult *res, \
InitializationContext *ctx) \
{ \
GError *error = NULL; \
gchar *val; \
\
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_warn ("couldn't load %s: '%s'", DISPLAY, error->message); \
g_error_free (error); \
} \
\
/* Go on to next step */ \
ctx->step++; \
interface_initialization_step (ctx); \
}
#undef UINT_REPLY_READY_FN
#define UINT_REPLY_READY_FN(NAME,DISPLAY,FATAL) \
static void \
load_##NAME##_ready (MMIfaceModem *self, \
GAsyncResult *res, \
InitializationContext *ctx) \
{ \
GError *error = NULL; \
\
mm_gdbus_modem_set_##NAME ( \
ctx->skeleton, \
MM_IFACE_MODEM_GET_INTERFACE (self)->load_##NAME##_finish (self, res, &error)); \
\
if (error) { \
if (FATAL) { \
g_propagate_error (&ctx->fatal_error, error); \
g_prefix_error (&ctx->fatal_error, "couldn't load %s: ", DISPLAY); \
/* Jump to the last step */ \
ctx->step = INITIALIZATION_STEP_LAST; \
} else { \
mm_warn ("couldn't load %s: '%s'", DISPLAY, error->message); \
g_error_free (error); \
/* Go on to next step */ \
ctx->step++; \
} \
} else { \
/* Go on to next step */ \
ctx->step++; \
} \
\
interface_initialization_step (ctx); \
}
UINT_REPLY_READY_FN (current_capabilities, "Current Capabilities", TRUE)
UINT_REPLY_READY_FN (modem_capabilities, "Modem Capabilities", FALSE)
STR_REPLY_READY_FN (manufacturer, "Manufacturer")
STR_REPLY_READY_FN (model, "Model")
STR_REPLY_READY_FN (revision, "Revision")
STR_REPLY_READY_FN (equipment_identifier, "Equipment Identifier")
STR_REPLY_READY_FN (device_identifier, "Device Identifier")
static void
load_own_numbers_ready (MMIfaceModem *self,
GAsyncResult *res,
InitializationContext *ctx)
{
GError *error = NULL;
GStrv str_list;
str_list = MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers_finish (self, res, &error);
if (error) {
mm_warn ("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 (ctx);
}
static void
load_supported_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
InitializationContext *ctx)
{
GError *error = NULL;
MMModemMode modes;
modes = MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_modes_finish (self, res, &error);
if (modes != MM_MODEM_MODE_NONE) {
mm_gdbus_modem_set_supported_modes (ctx->skeleton, modes);
mm_gdbus_modem_set_allowed_modes (ctx->skeleton, modes);
}
if (error) {
mm_warn ("couldn't load Supported Modes: '%s'", error->message);
g_error_free (error);
}
/* Go on to next step */
ctx->step++;
interface_initialization_step (ctx);
}
static void
load_supported_bands_ready (MMIfaceModem *self,
GAsyncResult *res,
InitializationContext *ctx)
{
GError *error = NULL;
GArray *bands_array;
bands_array = MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_bands_finish (self, res, &error);
if (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_warn ("couldn't load Supported Bands: '%s'", error->message);
g_error_free (error);
}
/* Go on to next step */
ctx->step++;
interface_initialization_step (ctx);
}
static void
load_unlock_required_ready (MMIfaceModem *self,
GAsyncResult *res,
InitializationContext *ctx)
{
/* NOTE: we already propagated the lock state, no need to do it again */
mm_iface_modem_unlock_check_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 (ctx);
}
static void
update_unlock_retries_ready (MMIfaceModem *self,
GAsyncResult *res,
InitializationContext *ctx)
{
GError *error = NULL;
mm_iface_modem_update_unlock_retries_finish (self, res, &error);
if (error) {
mm_warn ("couldn't update unlock retries: '%s'", error->message);
g_error_free (error);
}
/* Go on to next step */
ctx->step++;
interface_initialization_step (ctx);
}
static void
sim_new_ready (GAsyncInitable *initable,
GAsyncResult *res,
InitializationContext *ctx)
{
MMSim *sim;
GError *error = NULL;
sim = MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->create_sim_finish (ctx->self, res, &error);
if (error) {
mm_warn ("couldn't create SIM: '%s'", error->message);
g_simple_async_result_take_error (ctx->result, error);
initialization_context_complete_and_free (ctx);
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_SIM_PATH,
ctx->skeleton, "sim",
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
g_object_set (ctx->self,
MM_IFACE_MODEM_SIM, sim,
NULL);
g_object_unref (sim);
}
/* Go on to next step */
ctx->step++;
interface_initialization_step (ctx);
}
static void
sim_reinit_ready (MMSim *sim,
GAsyncResult *res,
InitializationContext *ctx)
{
GError *error = NULL;
if (!mm_sim_initialize_finish (sim, res, &error)) {
mm_warn ("SIM re-initialization failed: '%s'",
error ? error->message : "Unknown error");
g_clear_error (&error);
}
/* Go on to next step */
ctx->step++;
interface_initialization_step (ctx);
}
static void
interface_initialization_step (InitializationContext *ctx)
{
/* Don't run new steps if we're cancelled */
if (initialization_context_complete_and_free_if_cancelled (ctx))
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 (ctx->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_driver (ctx->skeleton)) {
gchar *driver;
g_object_get (ctx->self,
MM_BASE_MODEM_DRIVER, &driver,
NULL);
mm_gdbus_modem_set_driver (ctx->skeleton, driver);
g_free (driver);
}
/* Load plugin if not done before */
if (!mm_gdbus_modem_get_plugin (ctx->skeleton)) {
gchar *plugin;
g_object_get (ctx->self,
MM_BASE_MODEM_PLUGIN, &plugin,
NULL);
mm_gdbus_modem_set_plugin (ctx->skeleton, plugin);
g_free (plugin);
}
/* Fall down to next step */
ctx->step++;
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 (ctx->self)->load_current_capabilities &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_capabilities_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_capabilities (
ctx->self,
(GAsyncReadyCallback)load_current_capabilities_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
case INITIALIZATION_STEP_MODEM_CAPABILITIES:
/* Modem 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 (mm_gdbus_modem_get_modem_capabilities (ctx->skeleton) == MM_MODEM_CAPABILITY_NONE &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_modem_capabilities &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_modem_capabilities_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_modem_capabilities (
ctx->self,
(GAsyncReadyCallback)load_modem_capabilities_ready,
ctx);
return;
}
/* If no specific way of getting modem capabilities, assume they are
* equal to the current capabilities */
mm_gdbus_modem_set_modem_capabilities (
ctx->skeleton,
mm_gdbus_modem_get_current_capabilities (ctx->skeleton));
/* Fall down to next step */
ctx->step++;
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 (ctx->self,
MM_IFACE_MODEM_BEARER_LIST, &list,
NULL);
if (!list) {
list = mm_bearer_list_new (1, 1);
/* Create new default list */
g_object_set (ctx->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);
/* Fall down to next step */
ctx->step++;
}
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 (ctx->self)->load_manufacturer &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_manufacturer_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_manufacturer (
ctx->self,
(GAsyncReadyCallback)load_manufacturer_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
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 (ctx->self)->load_model &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_model_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_model (
ctx->self,
(GAsyncReadyCallback)load_model_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
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 (ctx->self)->load_revision &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_revision_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_revision (
ctx->self,
(GAsyncReadyCallback)load_revision_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
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 (ctx->self)->load_equipment_identifier &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_equipment_identifier_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_equipment_identifier (
ctx->self,
(GAsyncReadyCallback)load_equipment_identifier_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
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 (ctx->self)->load_device_identifier &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_device_identifier_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_device_identifier (
ctx->self,
(GAsyncReadyCallback)load_device_identifier_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
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 (ctx->self)->load_own_numbers &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_own_numbers_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_own_numbers (
ctx->self,
(GAsyncReadyCallback)load_own_numbers_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
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_unlock_check (ctx->self,
(GAsyncReadyCallback)load_unlock_required_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
case INITIALIZATION_STEP_UNLOCK_RETRIES:
mm_iface_modem_update_unlock_retries (ctx->self,
(GAsyncReadyCallback)update_unlock_retries_ready,
ctx);
return;
case INITIALIZATION_STEP_SIM:
/* If the modem doesn't need any SIM, skip */
if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->create_sim &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->create_sim_finish) {
MMSim *sim = NULL;
g_object_get (ctx->self,
MM_IFACE_MODEM_SIM, &sim,
NULL);
if (!sim) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->create_sim (
MM_IFACE_MODEM (ctx->self),
(GAsyncReadyCallback)sim_new_ready,
ctx);
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_sim_initialize (sim,
NULL, /* TODO: cancellable */
(GAsyncReadyCallback)sim_reinit_ready,
ctx);
g_object_unref (sim);
return;
}
case INITIALIZATION_STEP_SUPPORTED_MODES:
g_assert (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_modes != NULL);
g_assert (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_modes_finish != NULL);
/* 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 (mm_gdbus_modem_get_supported_modes (ctx->skeleton) == MM_MODEM_MODE_NONE) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_modes (
ctx->self,
(GAsyncReadyCallback)load_supported_modes_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
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 (ctx->self)->load_supported_bands &&
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_bands_finish) {
MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_bands (
ctx->self,
(GAsyncReadyCallback)load_supported_bands_ready,
ctx);
g_array_unref (supported_bands);
return;
}
/* Loading supported bands not implemented, default to ANY */
mm_gdbus_modem_set_supported_bands (ctx->skeleton, mm_common_build_bands_any ());
mm_gdbus_modem_set_bands (ctx->skeleton, mm_common_build_bands_any ());
}
g_array_unref (supported_bands);
/* Fall down to next step */
ctx->step++;
}
case INITIALIZATION_STEP_LAST:
if (ctx->fatal_error) {
g_simple_async_result_take_error (ctx->result, ctx->fatal_error);
ctx->fatal_error = NULL;
} else {
/* We are done without errors!
* Handle method invocations */
g_signal_connect (ctx->skeleton,
"handle-create-bearer",
G_CALLBACK (handle_create_bearer),
ctx->self);
g_signal_connect (ctx->skeleton,
"handle-command",
G_CALLBACK (handle_command),
ctx->self);
g_signal_connect (ctx->skeleton,
"handle-delete-bearer",
G_CALLBACK (handle_delete_bearer),
ctx->self);
g_signal_connect (ctx->skeleton,
"handle-list-bearers",
G_CALLBACK (handle_list_bearers),
ctx->self);
g_signal_connect (ctx->skeleton,
"handle-enable",
G_CALLBACK (handle_enable),
ctx->self);
g_signal_connect (ctx->skeleton,
"handle-reset",
G_CALLBACK (handle_reset),
ctx->self);
g_signal_connect (ctx->skeleton,
"handle-factory-reset",
G_CALLBACK (handle_factory_reset),
ctx->self);
g_signal_connect (ctx->skeleton,
"handle-set-bands",
G_CALLBACK (handle_set_bands),
ctx->self);
g_signal_connect (ctx->skeleton,
"handle-set-allowed-modes",
G_CALLBACK (handle_set_allowed_modes),
ctx->self);
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
}
/* Finally, export the new interface, even if we got errors */
mm_gdbus_object_skeleton_set_modem (MM_GDBUS_OBJECT_SKELETON (ctx->self),
MM_GDBUS_MODEM (ctx->skeleton));
initialization_context_complete_and_free (ctx);
return;
}
g_assert_not_reached ();
}
gboolean
mm_iface_modem_initialize_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
g_return_val_if_fail (MM_IS_IFACE_MODEM (self), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
void
mm_iface_modem_initialize (MMIfaceModem *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
MmGdbusModem *skeleton = NULL;
g_return_if_fail (MM_IS_IFACE_MODEM (self));
/* 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_current_capabilities (skeleton, MM_MODEM_CAPABILITY_NONE);
mm_gdbus_modem_set_modem_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_driver (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_MODEM_MODE_NONE);
mm_gdbus_modem_set_allowed_modes (skeleton, MM_MODEM_MODE_NONE);
mm_gdbus_modem_set_preferred_mode (skeleton, MM_MODEM_MODE_NONE);
mm_gdbus_modem_set_supported_bands (skeleton, mm_common_build_bands_unknown ());
mm_gdbus_modem_set_bands (skeleton, mm_common_build_bands_unknown ());
/* 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 */
interface_initialization_step (initialization_context_new (self,
cancellable,
callback,
user_data));
g_object_unref (skeleton);
return;
}
void
mm_iface_modem_shutdown (MMIfaceModem *self)
{
g_return_if_fail (MM_IS_IFACE_MODEM (self));
/* Remove SignalQualityCheckContext object to make sure any pending
* invocation of periodic_signal_quality_check is cancelled before
* SignalQualityUpdateContext is removed (as signal_quality_check_ready may
* call update_signal_quality). */
if (G_LIKELY (signal_quality_check_context_quark))
g_object_set_qdata (G_OBJECT (self),
signal_quality_check_context_quark,
NULL);
/* 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 AccessTechnologiesCheckContext object to make sure any pending
* invocation of periodic_access_technologies_check is canceled before the
* DBus skeleton is removed. */
if (G_LIKELY (access_technologies_check_context_quark))
g_object_set_qdata (G_OBJECT (self),
access_technologies_check_context_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);
}
/*****************************************************************************/
MMModemMode
mm_iface_modem_get_supported_modes (MMIfaceModem *self)
{
MMModemMode supported = MM_MODEM_MODE_NONE;
MmGdbusModem *skeleton;
g_object_get (self,
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
NULL);
if (skeleton) {
supported = mm_gdbus_modem_get_supported_modes (skeleton);
g_object_unref (skeleton);
}
return supported;
}
gboolean
mm_iface_modem_is_2g (MMIfaceModem *self)
{
return (mm_iface_modem_get_supported_modes (self) & MM_MODEM_MODE_2G);
}
gboolean
mm_iface_modem_is_2g_only (MMIfaceModem *self)
{
MMModemMode supported;
supported = mm_iface_modem_get_supported_modes (self);
return !((MM_MODEM_MODE_2G ^ supported) & supported);
}
gboolean
mm_iface_modem_is_3g (MMIfaceModem *self)
{
return (mm_iface_modem_get_supported_modes (self) & MM_MODEM_MODE_3G);
}
gboolean
mm_iface_modem_is_3g_only (MMIfaceModem *self)
{
MMModemMode supported;
supported = mm_iface_modem_get_supported_modes (self);
return !((MM_MODEM_MODE_3G ^ supported) & supported);
}
gboolean
mm_iface_modem_is_4g (MMIfaceModem *self)
{
return (mm_iface_modem_get_supported_modes (self) & MM_MODEM_MODE_4G);
}
gboolean
mm_iface_modem_is_4g_only (MMIfaceModem *self)
{
MMModemMode supported;
supported = mm_iface_modem_get_supported_modes (self);
return !((MM_MODEM_MODE_4G ^ supported) & supported);
}
/*****************************************************************************/
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_3GPP_LTE);
}
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 !((MM_MODEM_CAPABILITY_3GPP ^ capabilities) & capabilities);
}
gboolean
mm_iface_modem_is_3gpp_lte_only (MMIfaceModem *self)
{
MMModemCapability capabilities;
capabilities = mm_iface_modem_get_current_capabilities (self);
return !((MM_MODEM_CAPABILITY_3GPP_LTE ^ capabilities) & capabilities);
}
gboolean
mm_iface_modem_is_cdma_only (MMIfaceModem *self)
{
MMModemCapability capabilities;
capabilities = mm_iface_modem_get_current_capabilities (self);
return !((MM_MODEM_CAPABILITY_CDMA_EVDO ^ capabilities) & capabilities);
}
/*****************************************************************************/
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_SIM,
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));
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;
}