Rather than make all of them rely on MMBaseModem for it. This lets us disentangle dependencies for easier unit testing. For interfaces, rather than casting directly to MMBaseModem use intermediate interfaces (MMIfaceAuth and MMIfaceOpLock) that tests can fake out. Signed-off-by: Dan Williams <dan@ioncontrol.co>
554 lines
18 KiB
C
554 lines
18 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) 2021 Fibocom Wireless Inc.
|
|
*/
|
|
|
|
#include <ModemManager.h>
|
|
#define _LIBMM_INSIDE_MM
|
|
#include <libmm-glib.h>
|
|
|
|
#include "mm-iface-modem.h"
|
|
#include "mm-iface-modem-sar.h"
|
|
#include "mm-error-helpers.h"
|
|
#include "mm-log-object.h"
|
|
|
|
#define SUPPORT_CHECKED_TAG "sar-support-checked-tag"
|
|
#define SUPPORTED_TAG "sar-supported-tag"
|
|
|
|
static GQuark support_checked_quark;
|
|
static GQuark supported_quark;
|
|
|
|
G_DEFINE_INTERFACE (MMIfaceModemSar, mm_iface_modem_sar, MM_TYPE_IFACE_MODEM)
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
mm_iface_modem_sar_bind_simple_status (MMIfaceModemSar *self,
|
|
MMSimpleStatus *status)
|
|
{
|
|
}
|
|
|
|
guint
|
|
mm_iface_modem_sar_get_power_level (MMIfaceModemSar *self)
|
|
{
|
|
MmGdbusModemSar *skeleton = NULL;
|
|
guint level;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_SAR_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
|
|
if (!skeleton)
|
|
return 0;
|
|
|
|
level = mm_gdbus_modem_sar_get_power_level (skeleton);
|
|
g_object_unref (skeleton);
|
|
return level;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Handle Enable() */
|
|
|
|
typedef struct {
|
|
MmGdbusModemSar *skeleton;
|
|
GDBusMethodInvocation *invocation;
|
|
MMIfaceModemSar *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_slice_free (HandleEnableContext, ctx);
|
|
}
|
|
|
|
static void
|
|
enable_ready (MMIfaceModemSar *self,
|
|
GAsyncResult *res,
|
|
HandleEnableContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
guint power_level = 0;
|
|
|
|
if (!MM_IFACE_MODEM_SAR_GET_IFACE (ctx->self)->enable_finish (self, res, &power_level, &error)) {
|
|
mm_obj_warn (self, "failed %s SAR: %s", ctx->enable ? "enabling" : "disabling", error->message);
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
} else {
|
|
mm_obj_info (self, "%s SAR", ctx->enable ? "enabled" : "disabled");
|
|
mm_gdbus_modem_sar_set_state (ctx->skeleton, ctx->enable);
|
|
if (ctx->enable)
|
|
mm_gdbus_modem_sar_set_power_level (ctx->skeleton, power_level);
|
|
mm_gdbus_modem_sar_complete_enable (ctx->skeleton, ctx->invocation);
|
|
}
|
|
|
|
handle_enable_context_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_enable_auth_ready (MMIfaceAuth *_self,
|
|
GAsyncResult *res,
|
|
HandleEnableContext *ctx)
|
|
{
|
|
MMIfaceModemSar *self = MM_IFACE_MODEM_SAR (_self);
|
|
GError *error = NULL;
|
|
|
|
if (!mm_iface_auth_authorize_finish (_self, res, &error)) {
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
handle_enable_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
if (!MM_IFACE_MODEM_SAR_GET_IFACE (ctx->self)->enable ||
|
|
!MM_IFACE_MODEM_SAR_GET_IFACE (ctx->self)->enable_finish) {
|
|
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
|
"Cannot setup SAR: operation not supported");
|
|
handle_enable_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
if (mm_gdbus_modem_sar_get_state (ctx->skeleton) == ctx->enable) {
|
|
mm_gdbus_modem_sar_complete_enable (ctx->skeleton, ctx->invocation);
|
|
handle_enable_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
mm_obj_info (self, "processing user request to %s SAR...", ctx->enable ? "enable" : "disable");
|
|
MM_IFACE_MODEM_SAR_GET_IFACE (ctx->self)->enable (
|
|
ctx->self,
|
|
ctx->enable,
|
|
(GAsyncReadyCallback)enable_ready,
|
|
ctx);
|
|
}
|
|
|
|
static gboolean
|
|
handle_enable (MmGdbusModemSar *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
gboolean enable,
|
|
MMIfaceModemSar *self)
|
|
{
|
|
HandleEnableContext *ctx;
|
|
|
|
ctx = g_slice_new0 (HandleEnableContext);
|
|
ctx->skeleton = g_object_ref (skeleton);
|
|
ctx->invocation = g_object_ref (invocation);
|
|
ctx->self = g_object_ref (self);
|
|
ctx->enable = enable;
|
|
|
|
mm_iface_auth_authorize (MM_IFACE_AUTH (self),
|
|
invocation,
|
|
MM_AUTHORIZATION_DEVICE_CONTROL,
|
|
(GAsyncReadyCallback)handle_enable_auth_ready,
|
|
ctx);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Handle Set Power Level() */
|
|
|
|
typedef struct {
|
|
MmGdbusModemSar *skeleton;
|
|
GDBusMethodInvocation *invocation;
|
|
MMIfaceModemSar *self;
|
|
guint power_level;
|
|
} HandleSetPowerLevelContext;
|
|
|
|
static void
|
|
handle_set_power_level_context_free (HandleSetPowerLevelContext *ctx)
|
|
{
|
|
g_object_unref (ctx->skeleton);
|
|
g_object_unref (ctx->invocation);
|
|
g_object_unref (ctx->self);
|
|
g_slice_free (HandleSetPowerLevelContext, ctx);
|
|
}
|
|
|
|
static void
|
|
set_power_level_ready (MMIfaceModemSar *self,
|
|
GAsyncResult *res,
|
|
HandleSetPowerLevelContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!MM_IFACE_MODEM_SAR_GET_IFACE (ctx->self)->set_power_level_finish (self, res, &error)) {
|
|
mm_obj_warn (self, "failed setting SAR power level to %u: %s", ctx->power_level, error->message);
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
} else {
|
|
mm_obj_info (self, "SAR power level set to %u", ctx->power_level);
|
|
mm_gdbus_modem_sar_set_power_level (ctx->skeleton, ctx->power_level);
|
|
mm_gdbus_modem_sar_complete_set_power_level (ctx->skeleton, ctx->invocation);
|
|
}
|
|
|
|
handle_set_power_level_context_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_set_power_level_auth_ready (MMIfaceAuth *_self,
|
|
GAsyncResult *res,
|
|
HandleSetPowerLevelContext *ctx)
|
|
{
|
|
MMIfaceModemSar *self = MM_IFACE_MODEM_SAR (_self);
|
|
GError *error = NULL;
|
|
|
|
if (!mm_iface_auth_authorize_finish (_self, res, &error)) {
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
handle_set_power_level_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
if (!MM_IFACE_MODEM_SAR_GET_IFACE (ctx->self)->set_power_level ||
|
|
!MM_IFACE_MODEM_SAR_GET_IFACE (ctx->self)->set_power_level_finish) {
|
|
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
|
"Cannot set SAR power level: operation not supported");
|
|
handle_set_power_level_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
if (!mm_gdbus_modem_sar_get_state (ctx->skeleton)) {
|
|
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
|
|
"Cannot set SAR power level: SAR is disabled");
|
|
handle_set_power_level_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
if (mm_gdbus_modem_sar_get_power_level (ctx->skeleton) == ctx->power_level) {
|
|
mm_gdbus_modem_sar_complete_set_power_level (ctx->skeleton, ctx->invocation);
|
|
handle_set_power_level_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
mm_obj_info (self, "processing user request to set SAR power level to %u...", ctx->power_level);
|
|
MM_IFACE_MODEM_SAR_GET_IFACE (ctx->self)->set_power_level (
|
|
ctx->self,
|
|
ctx->power_level,
|
|
(GAsyncReadyCallback)set_power_level_ready,
|
|
ctx);
|
|
}
|
|
|
|
static gboolean
|
|
handle_set_power_level (MmGdbusModemSar *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
guint level,
|
|
MMIfaceModemSar *self)
|
|
{
|
|
HandleSetPowerLevelContext *ctx;
|
|
|
|
ctx = g_slice_new0 (HandleSetPowerLevelContext);
|
|
ctx->skeleton = g_object_ref (skeleton);
|
|
ctx->invocation = g_object_ref (invocation);
|
|
ctx->self = g_object_ref (self);
|
|
ctx->power_level = level;
|
|
|
|
mm_iface_auth_authorize (MM_IFACE_AUTH (self),
|
|
invocation,
|
|
MM_AUTHORIZATION_DEVICE_CONTROL,
|
|
(GAsyncReadyCallback)handle_set_power_level_auth_ready,
|
|
ctx);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct _InitializationContext InitializationContext;
|
|
static void interface_initialization_step (GTask *task);
|
|
|
|
typedef enum {
|
|
INITIALIZATION_STEP_FIRST,
|
|
INITIALIZATION_STEP_CHECK_SUPPORT,
|
|
INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED,
|
|
INITIALIZATION_STEP_LOAD_STATE,
|
|
INITIALIZATION_STEP_LOAD_POWER_LEVEL,
|
|
INITIALIZATION_STEP_LAST
|
|
} InitializationStep;
|
|
|
|
struct _InitializationContext {
|
|
MmGdbusModemSar *skeleton;
|
|
InitializationStep step;
|
|
};
|
|
|
|
static void
|
|
initialization_context_free (InitializationContext *ctx)
|
|
{
|
|
g_object_unref (ctx->skeleton);
|
|
g_free (ctx);
|
|
}
|
|
|
|
gboolean
|
|
mm_iface_modem_sar_initialize_finish (MMIfaceModemSar *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
load_power_level_ready (MMIfaceModemSar *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
GError *error = NULL;
|
|
guint level;
|
|
|
|
if (!MM_IFACE_MODEM_SAR_GET_IFACE (self)->load_power_level_finish (self, res, &level, &error)) {
|
|
mm_obj_warn (self, "loading SAR power level failed: %s", error->message);
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
mm_gdbus_modem_sar_set_power_level (ctx->skeleton, level);
|
|
|
|
/* Go on to next step */
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
load_state_ready (MMIfaceModemSar *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
gboolean state;
|
|
GError *error = NULL;
|
|
|
|
if (!MM_IFACE_MODEM_SAR_GET_IFACE (self)->load_state_finish (self, res, &state, &error)) {
|
|
mm_obj_warn (self, "loading SAR state failed: %s", error->message);
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
mm_gdbus_modem_sar_set_state (ctx->skeleton, state);
|
|
|
|
/* Go on to next step */
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
check_support_ready (MMIfaceModemSar *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
InitializationContext *ctx;
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
if (!MM_IFACE_MODEM_SAR_GET_IFACE (self)->check_support_finish (self, res, &error)) {
|
|
if (error) {
|
|
/* This error shouldn't be treated as critical */
|
|
mm_obj_dbg (self, "SAR support check failed: %s", error->message);
|
|
}
|
|
} else {
|
|
/* SAR is supported! */
|
|
g_object_set_qdata (G_OBJECT (self),
|
|
supported_quark,
|
|
GUINT_TO_POINTER (TRUE));
|
|
}
|
|
|
|
/* Go on to next step */
|
|
ctx = g_task_get_task_data (task);
|
|
ctx->step++;
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
static void
|
|
interface_initialization_step (GTask *task)
|
|
{
|
|
MMIfaceModemSar *self;
|
|
InitializationContext *ctx;
|
|
|
|
/* Don't run new steps if we're cancelled */
|
|
if (g_task_return_error_if_cancelled (task)) {
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
self = g_task_get_source_object (task);
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
switch (ctx->step) {
|
|
case INITIALIZATION_STEP_FIRST:
|
|
/* Setup quarks if we didn't do it before */
|
|
if (G_UNLIKELY (!support_checked_quark))
|
|
support_checked_quark = (g_quark_from_static_string (
|
|
SUPPORT_CHECKED_TAG));
|
|
if (G_UNLIKELY (!supported_quark))
|
|
supported_quark = (g_quark_from_static_string (
|
|
SUPPORTED_TAG));
|
|
ctx->step++;
|
|
/* fall through */
|
|
|
|
case INITIALIZATION_STEP_CHECK_SUPPORT:
|
|
if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self),
|
|
support_checked_quark))) {
|
|
/* Set the checked flag so that we don't run it again */
|
|
g_object_set_qdata (G_OBJECT (self),
|
|
support_checked_quark,
|
|
GUINT_TO_POINTER (TRUE));
|
|
/* Initially, assume we don't support it */
|
|
g_object_set_qdata (G_OBJECT (self),
|
|
supported_quark,
|
|
GUINT_TO_POINTER (FALSE));
|
|
|
|
if (MM_IFACE_MODEM_SAR_GET_IFACE (self)->check_support &&
|
|
MM_IFACE_MODEM_SAR_GET_IFACE (self)->check_support_finish) {
|
|
MM_IFACE_MODEM_SAR_GET_IFACE (self)->check_support (
|
|
self,
|
|
(GAsyncReadyCallback)check_support_ready,
|
|
task);
|
|
return;
|
|
}
|
|
|
|
/* If there is no implementation to check support, assume we DON'T
|
|
* support it. */
|
|
}
|
|
ctx->step++;
|
|
/* fall through */
|
|
|
|
case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED:
|
|
if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self),
|
|
supported_quark))) {
|
|
g_task_return_new_error (task,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_UNSUPPORTED,
|
|
"SAR not supported");
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall through */
|
|
|
|
case INITIALIZATION_STEP_LOAD_STATE:
|
|
if (MM_IFACE_MODEM_SAR_GET_IFACE (self)->load_state &&
|
|
MM_IFACE_MODEM_SAR_GET_IFACE (self)->load_state_finish) {
|
|
MM_IFACE_MODEM_SAR_GET_IFACE (self)->load_state (
|
|
self,
|
|
(GAsyncReadyCallback)load_state_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall through */
|
|
|
|
case INITIALIZATION_STEP_LOAD_POWER_LEVEL:
|
|
if (MM_IFACE_MODEM_SAR_GET_IFACE (self)->load_power_level &&
|
|
MM_IFACE_MODEM_SAR_GET_IFACE (self)->load_power_level_finish) {
|
|
MM_IFACE_MODEM_SAR_GET_IFACE (self)->load_power_level (
|
|
self,
|
|
(GAsyncReadyCallback)load_power_level_ready,
|
|
task);
|
|
return;
|
|
}
|
|
ctx->step++;
|
|
/* fall through */
|
|
|
|
case INITIALIZATION_STEP_LAST:
|
|
/* We are done without errors! */
|
|
|
|
/* Handle method invocations */
|
|
g_signal_connect (ctx->skeleton,
|
|
"handle-enable",
|
|
G_CALLBACK (handle_enable),
|
|
self);
|
|
g_signal_connect (ctx->skeleton,
|
|
"handle-set-power-level",
|
|
G_CALLBACK (handle_set_power_level),
|
|
self);
|
|
|
|
/* Finally, export the new interface */
|
|
mm_gdbus_object_skeleton_set_modem_sar (MM_GDBUS_OBJECT_SKELETON (self),
|
|
MM_GDBUS_MODEM_SAR (ctx->skeleton));
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_sar_initialize (MMIfaceModemSar *self,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
InitializationContext *ctx;
|
|
MmGdbusModemSar *skeleton = NULL;
|
|
GTask *task;
|
|
|
|
/* Did we already create it? */
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_SAR_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
|
|
if (!skeleton) {
|
|
skeleton = mm_gdbus_modem_sar_skeleton_new ();
|
|
g_object_set (self,
|
|
MM_IFACE_MODEM_SAR_DBUS_SKELETON, skeleton,
|
|
NULL);
|
|
}
|
|
|
|
/* Perform async initialization here */
|
|
ctx = g_new0 (InitializationContext, 1);
|
|
ctx->step = INITIALIZATION_STEP_FIRST;
|
|
ctx->skeleton = skeleton;
|
|
|
|
task = g_task_new (self, cancellable, callback, user_data);
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
|
|
|
|
interface_initialization_step (task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
mm_iface_modem_sar_shutdown (MMIfaceModemSar *self)
|
|
{
|
|
/* Unexport DBus interface and remove the skeleton */
|
|
mm_gdbus_object_skeleton_set_modem_sar (MM_GDBUS_OBJECT_SKELETON (self), NULL);
|
|
g_object_set (self,
|
|
MM_IFACE_MODEM_SAR_DBUS_SKELETON, NULL,
|
|
NULL);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
mm_iface_modem_sar_default_init (MMIfaceModemSarInterface *iface)
|
|
{
|
|
static gsize initialized = 0;
|
|
|
|
if (!g_once_init_enter (&initialized))
|
|
return;
|
|
|
|
/* Properties */
|
|
g_object_interface_install_property (
|
|
iface,
|
|
g_param_spec_object (MM_IFACE_MODEM_SAR_DBUS_SKELETON,
|
|
"SAR DBus skeleton",
|
|
"DBus skeleton for the SAR interface",
|
|
MM_GDBUS_TYPE_MODEM_SAR_SKELETON,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_once_init_leave (&initialized, 1);
|
|
}
|