iface-modem: use external dispatcher to attempt FCC unlock
We remove the built-in FCC unlock procedures from the ModemManager, we will no longer run them automatically, and instead rely on external scripts/programs to do that. Packages providing the external FCC unlock tools can install them in ${pkglibdir}/fcc-unlock.d. Users manually enabling external FCC unlock tools can install them in ${pkgsysconfdir}/fcc-unlock.d. The user-enabled path takes precedence over the package-enabled one.
This commit is contained in:
@@ -190,6 +190,10 @@ dnl-----------------------------------------------------------------------------
|
||||
dnl System paths
|
||||
dnl
|
||||
|
||||
dnl ModemManager configuration directory
|
||||
pkgsysconfdir="${sysconfdir}/ModemManager"
|
||||
AC_SUBST(pkgsysconfdir)
|
||||
|
||||
dnl DBus system directory
|
||||
AC_ARG_WITH(dbus-sys-dir, AS_HELP_STRING([--with-dbus-sys-dir=DIR], [where D-BUS system.d directory is]))
|
||||
if test -n "$with_dbus_sys_dir" ; then
|
||||
@@ -618,6 +622,7 @@ echo "
|
||||
|
||||
System paths:
|
||||
prefix: ${prefix}
|
||||
configuration directory: ${pkgsysconfdir}
|
||||
D-Bus system directory: ${DBUS_SYS_DIR}
|
||||
udev base directory: ${UDEV_BASE_DIR}
|
||||
systemd unit directory: ${with_systemdsystemunitdir}
|
||||
|
@@ -30,6 +30,7 @@ mm_sysconfdir = get_option('sysconfdir')
|
||||
mm_pkgdatadir = mm_datadir / mm_name
|
||||
mm_pkgincludedir = mm_includedir / mm_name
|
||||
mm_pkglibdir = mm_libdir / mm_name
|
||||
mm_pkgsysconfdir = mm_sysconfdir / mm_name
|
||||
|
||||
mm_glib_name = 'libmm-glib'
|
||||
mm_glib_pkgincludedir = mm_includedir / mm_glib_name
|
||||
@@ -400,6 +401,7 @@ summary({
|
||||
|
||||
summary({
|
||||
'prefix': mm_prefix,
|
||||
'configuration directory': mm_pkgsysconfdir,
|
||||
'D-Bus policy directory': dbus_policy_dir,
|
||||
'udev base directory': udev_udevdir,
|
||||
'systemd user unit directory': systemd_systemdsystemunitdir,
|
||||
|
@@ -41,7 +41,6 @@
|
||||
static void iface_modem_location_init (MMIfaceModemLocation *iface);
|
||||
|
||||
#if defined WITH_QMI
|
||||
static void iface_modem_init (MMIfaceModem *iface);
|
||||
static void iface_modem_firmware_init (MMIfaceModemFirmware *iface);
|
||||
#endif
|
||||
|
||||
@@ -49,7 +48,6 @@ static MMIfaceModemLocation *iface_modem_location_parent;
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimFoxconn, mm_broadband_modem_mbim_foxconn, MM_TYPE_BROADBAND_MODEM_MBIM, 0,
|
||||
#if defined WITH_QMI
|
||||
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
|
||||
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init)
|
||||
#endif
|
||||
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init))
|
||||
@@ -67,59 +65,6 @@ struct _MMBroadbandModemMbimFoxconnPrivate {
|
||||
|
||||
#if defined WITH_QMI
|
||||
|
||||
/*****************************************************************************/
|
||||
/* FCC unlock (Modem interface) */
|
||||
|
||||
static gboolean
|
||||
fcc_unlock_finish (MMIfaceModem *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
dms_foxconn_set_fcc_authentication_ready (QmiClientDms *client,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
GError *error = NULL;
|
||||
g_autoptr(QmiMessageDmsFoxconnSetFccAuthenticationOutput) output = NULL;
|
||||
|
||||
output = qmi_client_dms_foxconn_set_fcc_authentication_finish (client, res, &error);
|
||||
if (!output || !qmi_message_dms_foxconn_set_fcc_authentication_output_get_result (output, &error))
|
||||
g_task_return_error (task, error);
|
||||
else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static void
|
||||
fcc_unlock (MMIfaceModem *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
QmiClient *client = NULL;
|
||||
g_autoptr(QmiMessageDmsFoxconnSetFccAuthenticationInput) input = NULL;
|
||||
|
||||
if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
|
||||
QMI_SERVICE_DMS, &client,
|
||||
callback, user_data))
|
||||
return;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
|
||||
input = qmi_message_dms_foxconn_set_fcc_authentication_input_new ();
|
||||
qmi_message_dms_foxconn_set_fcc_authentication_input_set_value (input, 0x00, NULL);
|
||||
qmi_client_dms_foxconn_set_fcc_authentication (QMI_CLIENT_DMS (client),
|
||||
input,
|
||||
5,
|
||||
NULL,
|
||||
(GAsyncReadyCallback)dms_foxconn_set_fcc_authentication_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Firmware update settings
|
||||
*
|
||||
@@ -554,13 +499,6 @@ iface_modem_location_init (MMIfaceModemLocation *iface)
|
||||
|
||||
#if defined WITH_QMI
|
||||
|
||||
static void
|
||||
iface_modem_init (MMIfaceModem *iface)
|
||||
{
|
||||
iface->fcc_unlock = fcc_unlock;
|
||||
iface->fcc_unlock_finish = fcc_unlock_finish;
|
||||
}
|
||||
|
||||
static void
|
||||
iface_modem_firmware_init (MMIfaceModemFirmware *iface)
|
||||
{
|
||||
|
@@ -292,6 +292,8 @@ CLEANFILES += $(DAEMON_ENUMS_GENERATED)
|
||||
|
||||
ModemManager_CPPFLAGS = \
|
||||
-DPLUGINDIR=\"$(pkglibdir)\" \
|
||||
-DFCCUNLOCKDIRPACKAGE=\"${pkglibdir}/fcc-unlock.d\" \
|
||||
-DFCCUNLOCKDIRUSER=\"${pkgsysconfdir}/fcc-unlock.d\" \
|
||||
-DMM_COMPILATION \
|
||||
$(NULL)
|
||||
|
||||
@@ -311,6 +313,8 @@ ModemManager_SOURCES = \
|
||||
mm-private-boxed-types.c \
|
||||
mm-auth-provider.h \
|
||||
mm-auth-provider.c \
|
||||
mm-fcc-unlock-dispatcher.h \
|
||||
mm-fcc-unlock-dispatcher.c \
|
||||
mm-filter.h \
|
||||
mm-filter.c \
|
||||
mm-base-manager.c \
|
||||
|
@@ -200,6 +200,7 @@ sources = files(
|
||||
'mm-call-list.c',
|
||||
'mm-context.c',
|
||||
'mm-device.c',
|
||||
'mm-fcc-unlock-dispatcher.c',
|
||||
'mm-filter.c',
|
||||
'mm-iface-modem-3gpp.c',
|
||||
'mm-iface-modem-3gpp-profile-manager.c',
|
||||
@@ -250,6 +251,8 @@ deps = [
|
||||
c_args = [
|
||||
'-DMM_COMPILATION',
|
||||
'-DPLUGINDIR="@0@"'.format(mm_prefix / mm_pkglibdir),
|
||||
'-DFCCUNLOCKDIRPACKAGE="@0@"'.format(mm_prefix / mm_pkglibdir / 'fcc-unlock.d'),
|
||||
'-DFCCUNLOCKDIRUSER="@0@"'.format(mm_prefix / mm_pkgsysconfdir / 'fcc-unlock.d'),
|
||||
]
|
||||
|
||||
if enable_qrtr
|
||||
|
@@ -7975,8 +7975,6 @@ iface_modem_init (MMIfaceModem *iface)
|
||||
iface->load_current_bands_finish = mm_shared_qmi_load_current_bands_finish;
|
||||
iface->set_current_bands = mm_shared_qmi_set_current_bands;
|
||||
iface->set_current_bands_finish = mm_shared_qmi_set_current_bands_finish;
|
||||
iface->fcc_unlock = mm_shared_qmi_fcc_unlock;
|
||||
iface->fcc_unlock_finish = mm_shared_qmi_fcc_unlock_finish;
|
||||
#endif
|
||||
|
||||
/* Additional actions */
|
||||
|
@@ -12381,8 +12381,6 @@ iface_modem_init (MMIfaceModem *iface)
|
||||
/* Enabling/disabling */
|
||||
iface->modem_power_up = modem_power_up;
|
||||
iface->modem_power_up_finish = modem_power_up_down_off_finish;
|
||||
iface->fcc_unlock = mm_shared_qmi_fcc_unlock;
|
||||
iface->fcc_unlock_finish = mm_shared_qmi_fcc_unlock_finish;
|
||||
iface->modem_after_power_up = NULL;
|
||||
iface->modem_after_power_up_finish = NULL;
|
||||
iface->modem_power_down = modem_power_down;
|
||||
|
319
src/mm-fcc-unlock-dispatcher.c
Normal file
319
src/mm-fcc-unlock-dispatcher.c
Normal file
@@ -0,0 +1,319 @@
|
||||
/* -*- 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 Aleksander Morgado <aleksander@aleksander.es>
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <ModemManager.h>
|
||||
#include "mm-errors-types.h"
|
||||
#include "mm-utils.h"
|
||||
#include "mm-log-object.h"
|
||||
#include "mm-fcc-unlock-dispatcher.h"
|
||||
|
||||
#if !defined FCCUNLOCKDIRPACKAGE
|
||||
# error FCCUNLOCKDIRPACKAGE must be defined at build time
|
||||
#endif
|
||||
|
||||
#if !defined FCCUNLOCKDIRUSER
|
||||
# error FCCUNLOCKDIRUSER must be defined at build time
|
||||
#endif
|
||||
|
||||
/* Maximum time a FCC unlock command is allowed to run before
|
||||
* us killing it */
|
||||
#define MAX_FCC_UNLOCK_EXEC_TIME_SECS 5
|
||||
|
||||
struct _MMFccUnlockDispatcher {
|
||||
GObject parent;
|
||||
GSubprocessLauncher *launcher;
|
||||
};
|
||||
|
||||
struct _MMFccUnlockDispatcherClass {
|
||||
GObjectClass parent;
|
||||
};
|
||||
|
||||
static void log_object_iface_init (MMLogObjectInterface *iface);
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (MMFccUnlockDispatcher, mm_fcc_unlock_dispatcher, G_TYPE_OBJECT, 0,
|
||||
G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static gchar *
|
||||
log_object_build_id (MMLogObject *_self)
|
||||
{
|
||||
return g_strdup ("fcc-unlock-dispatcher");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
gchar *filename;
|
||||
GSubprocess *subprocess;
|
||||
guint timeout_id;
|
||||
} RunContext;
|
||||
|
||||
static void
|
||||
run_context_free (RunContext *ctx)
|
||||
{
|
||||
g_assert (!ctx->timeout_id);
|
||||
g_clear_object (&ctx->subprocess);
|
||||
g_free (ctx->filename);
|
||||
g_slice_free (RunContext, ctx);
|
||||
}
|
||||
|
||||
gboolean
|
||||
mm_fcc_unlock_dispatcher_run_finish (MMFccUnlockDispatcher *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
subprocess_wait_timed_out (GTask *task)
|
||||
{
|
||||
MMFccUnlockDispatcher *self;
|
||||
RunContext *ctx;
|
||||
|
||||
self = g_task_get_source_object (task);
|
||||
ctx = g_task_get_task_data (task);
|
||||
|
||||
mm_obj_warn (self, "forcing exit on %s FCC unlock operation", ctx->filename);
|
||||
g_subprocess_force_exit (ctx->subprocess);
|
||||
|
||||
ctx->timeout_id = 0;
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
subprocess_wait_ready (GSubprocess *subprocess,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
GError *error = NULL;
|
||||
RunContext *ctx;
|
||||
|
||||
/* cleanup timeout before any return */
|
||||
ctx = g_task_get_task_data (task);
|
||||
if (ctx->timeout_id) {
|
||||
g_source_remove (ctx->timeout_id);
|
||||
ctx->timeout_id = 0;
|
||||
}
|
||||
|
||||
if (!g_subprocess_wait_finish (subprocess, res, &error)) {
|
||||
g_prefix_error (&error, "FCC unlock operation wait failed: ");
|
||||
g_task_return_error (task, error);
|
||||
} else if (!g_subprocess_get_successful (subprocess)) {
|
||||
if (g_subprocess_get_if_signaled (subprocess))
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"FCC unlock operation aborted with signal %d",
|
||||
g_subprocess_get_term_sig (subprocess));
|
||||
else if (g_subprocess_get_if_exited (subprocess))
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"FCC unlock operation finished with status %d",
|
||||
g_subprocess_get_exit_status (subprocess));
|
||||
else
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"FCC unlock operation failed");
|
||||
} else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
validate_file (const gchar *path,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GFile) file = NULL;
|
||||
g_autoptr(GFileInfo) file_info = NULL;
|
||||
guint32 file_mode;
|
||||
guint32 file_uid;
|
||||
|
||||
file = g_file_new_for_path (path);
|
||||
file_info = g_file_query_info (file,
|
||||
(G_FILE_ATTRIBUTE_STANDARD_SIZE ","
|
||||
G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
|
||||
G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
|
||||
G_FILE_ATTRIBUTE_UNIX_MODE ","
|
||||
G_FILE_ATTRIBUTE_UNIX_UID),
|
||||
G_FILE_QUERY_INFO_NONE,
|
||||
NULL,
|
||||
error);
|
||||
if (!file_info)
|
||||
return FALSE;
|
||||
|
||||
if (g_file_info_get_is_symlink (file_info)) {
|
||||
const gchar *link_target;
|
||||
|
||||
link_target = g_file_info_get_symlink_target (file_info);
|
||||
if (g_strcmp0 (link_target, "/dev/null") == 0) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
|
||||
"Link '%s' to /dev/null is not executable", path);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_file_info_get_size (file_info) == 0) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
|
||||
"File '%s' is empty", path);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
file_uid = g_file_info_get_attribute_uint32 (file_info, G_FILE_ATTRIBUTE_UNIX_UID);
|
||||
if (file_uid != 0) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
|
||||
"File '%s' not owned by root", path);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
file_mode = g_file_info_get_attribute_uint32 (file_info, G_FILE_ATTRIBUTE_UNIX_MODE);
|
||||
if (!S_ISREG (file_mode)) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
|
||||
"File '%s' is not regular", path);
|
||||
return FALSE;
|
||||
}
|
||||
if (file_mode & (S_IWGRP | S_IWOTH)) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
|
||||
"File '%s' is writable by group or other", path);
|
||||
return FALSE;
|
||||
}
|
||||
if (file_mode & S_ISUID) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
|
||||
"File '%s' is set-UID", path);
|
||||
return FALSE;
|
||||
}
|
||||
if (!(file_mode & S_IXUSR)) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
|
||||
"File '%s' is not executable by the owner", path);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
mm_fcc_unlock_dispatcher_run (MMFccUnlockDispatcher *self,
|
||||
guint vid,
|
||||
guint pid,
|
||||
const gchar *modem_dbus_path,
|
||||
const GStrv modem_ports,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
RunContext *ctx;
|
||||
guint i;
|
||||
const gchar *enabled_dirs[] = {
|
||||
FCCUNLOCKDIRUSER, /* sysconfdir */
|
||||
FCCUNLOCKDIRPACKAGE, /* libdir */
|
||||
};
|
||||
|
||||
task = g_task_new (self, cancellable, callback, user_data);
|
||||
ctx = g_slice_new0 (RunContext);
|
||||
g_task_set_task_data (task, ctx, (GDestroyNotify) run_context_free);
|
||||
|
||||
ctx->filename = g_strdup_printf ("%04x:%04x", vid, pid);
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (enabled_dirs); i++) {
|
||||
GPtrArray *aux;
|
||||
g_auto(GStrv) argv = NULL;
|
||||
g_autofree gchar *path = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
guint j;
|
||||
|
||||
path = g_build_path (G_DIR_SEPARATOR_S, enabled_dirs[i], ctx->filename, NULL);
|
||||
|
||||
/* Validation checks to see if we should run it or not */
|
||||
if (!validate_file (path, &error)) {
|
||||
mm_obj_dbg (self, "Cannot run FCC unlock operation from %s: %s",
|
||||
path, error->message);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* build argv */
|
||||
aux = g_ptr_array_new ();
|
||||
g_ptr_array_add (aux, g_steal_pointer (&path));
|
||||
g_ptr_array_add (aux, g_strdup (modem_dbus_path));
|
||||
for (j = 0; modem_ports && modem_ports[j]; j++)
|
||||
g_ptr_array_add (aux, g_strdup (modem_ports[j]));
|
||||
g_ptr_array_add (aux, NULL);
|
||||
argv = (GStrv) g_ptr_array_free (aux, FALSE);
|
||||
|
||||
/* create and launch subprocess */
|
||||
ctx->subprocess = g_subprocess_launcher_spawnv (self->launcher,
|
||||
(const gchar * const *)argv,
|
||||
&error);
|
||||
if (!ctx->subprocess) {
|
||||
g_prefix_error (&error, "FCC unlock operation launch from %s failed: ", path);
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
/* setup timeout */
|
||||
ctx->timeout_id = g_timeout_add_seconds (MAX_FCC_UNLOCK_EXEC_TIME_SECS,
|
||||
(GSourceFunc)subprocess_wait_timed_out,
|
||||
task);
|
||||
|
||||
/* wait for subprocess exit */
|
||||
g_subprocess_wait_async (ctx->subprocess,
|
||||
cancellable,
|
||||
(GAsyncReadyCallback)subprocess_wait_ready,
|
||||
task);
|
||||
return;
|
||||
}
|
||||
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"FCC unlock operation launch aborted: no valid program found");
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
mm_fcc_unlock_dispatcher_init (MMFccUnlockDispatcher *self)
|
||||
{
|
||||
self->launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE | G_SUBPROCESS_FLAGS_STDERR_SILENCE);
|
||||
|
||||
/* inherit parent's environment */
|
||||
g_subprocess_launcher_set_environ (self->launcher, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
dispose (GObject *object)
|
||||
{
|
||||
MMFccUnlockDispatcher *self = MM_FCC_UNLOCK_DISPATCHER (object);
|
||||
|
||||
g_clear_object (&self->launcher);
|
||||
|
||||
G_OBJECT_CLASS (mm_fcc_unlock_dispatcher_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
log_object_iface_init (MMLogObjectInterface *iface)
|
||||
{
|
||||
iface->build_id = log_object_build_id;
|
||||
}
|
||||
|
||||
static void
|
||||
mm_fcc_unlock_dispatcher_class_init (MMFccUnlockDispatcherClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->dispose = dispose;
|
||||
}
|
||||
|
||||
MM_DEFINE_SINGLETON_GETTER (MMFccUnlockDispatcher, mm_fcc_unlock_dispatcher_get, MM_TYPE_FCC_UNLOCK_DISPATCHER)
|
47
src/mm-fcc-unlock-dispatcher.h
Normal file
47
src/mm-fcc-unlock-dispatcher.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* -*- 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 Aleksander Morgado <aleksander@aleksander.es>
|
||||
*/
|
||||
|
||||
#ifndef MM_FCC_UNLOCK_DISPATCHER_H
|
||||
#define MM_FCC_UNLOCK_DISPATCHER_H
|
||||
|
||||
#include <config.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#define MM_TYPE_FCC_UNLOCK_DISPATCHER (mm_fcc_unlock_dispatcher_get_type ())
|
||||
#define MM_FCC_UNLOCK_DISPATCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_FCC_UNLOCK_DISPATCHER, MMFccUnlockDispatcher))
|
||||
#define MM_FCC_UNLOCK_DISPATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_FCC_UNLOCK_DISPATCHER, MMFccUnlockDispatcherClass))
|
||||
#define MM_IS_FCC_UNLOCK_DISPATCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_FCC_UNLOCK_DISPATCHER))
|
||||
#define MM_IS_FCC_UNLOCK_DISPATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_FCC_UNLOCK_DISPATCHER))
|
||||
#define MM_FCC_UNLOCK_DISPATCHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_FCC_UNLOCK_DISPATCHER, MMFccUnlockDispatcherClass))
|
||||
|
||||
typedef struct _MMFccUnlockDispatcher MMFccUnlockDispatcher;
|
||||
typedef struct _MMFccUnlockDispatcherClass MMFccUnlockDispatcherClass;
|
||||
typedef struct _MMFccUnlockDispatcherPrivate MMFccUnlockDispatcherPrivate;
|
||||
|
||||
GType mm_fcc_unlock_dispatcher_get_type (void);
|
||||
MMFccUnlockDispatcher *mm_fcc_unlock_dispatcher_get (void);
|
||||
void mm_fcc_unlock_dispatcher_run (MMFccUnlockDispatcher *self,
|
||||
guint vid,
|
||||
guint pid,
|
||||
const gchar *modem_dbus_path,
|
||||
const GStrv ports,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean mm_fcc_unlock_dispatcher_run_finish (MMFccUnlockDispatcher *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
#endif /* MM_FCC_UNLOCK_DISPATCHER_H */
|
@@ -28,6 +28,7 @@
|
||||
#include "mm-private-boxed-types.h"
|
||||
#include "mm-log-object.h"
|
||||
#include "mm-context.h"
|
||||
#include "mm-fcc-unlock-dispatcher.h"
|
||||
#if defined WITH_QMI
|
||||
# include "mm-broadband-modem-qmi.h"
|
||||
#endif
|
||||
@@ -3844,15 +3845,18 @@ modem_after_power_up_ready (MMIfaceModem *self,
|
||||
}
|
||||
|
||||
static void
|
||||
fcc_unlock_ready (MMIfaceModem *self,
|
||||
fcc_unlock_dispatcher_ready (MMFccUnlockDispatcher *dispatcher,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
MMIfaceModem *self;
|
||||
SetPowerStateContext *ctx;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
self = g_task_get_source_object (task);
|
||||
ctx = g_task_get_task_data (task);
|
||||
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->fcc_unlock_finish (self, res, &error))
|
||||
|
||||
if (!mm_fcc_unlock_dispatcher_run_finish (dispatcher, res, &error))
|
||||
mm_obj_dbg (self, "couldn't run FCC unlock: %s", error->message);
|
||||
|
||||
/* always retry, even on reported error */
|
||||
@@ -3860,6 +3864,54 @@ fcc_unlock_ready (MMIfaceModem *self,
|
||||
set_power_state_step (task);
|
||||
}
|
||||
|
||||
static void
|
||||
fcc_unlock (GTask *task)
|
||||
{
|
||||
MMIfaceModem *self;
|
||||
MMFccUnlockDispatcher *dispatcher;
|
||||
MMModemPortInfo *port_infos;
|
||||
guint n_port_infos = 0;
|
||||
guint i;
|
||||
GPtrArray *aux;
|
||||
g_auto(GStrv) modem_ports = NULL;
|
||||
|
||||
self = g_task_get_source_object (task);
|
||||
|
||||
dispatcher = mm_fcc_unlock_dispatcher_get ();
|
||||
|
||||
aux = g_ptr_array_new ();
|
||||
port_infos = mm_base_modem_get_port_infos (MM_BASE_MODEM (self), &n_port_infos);
|
||||
for (i = 0; i < n_port_infos; i++) {
|
||||
switch (port_infos[i].type) {
|
||||
case MM_MODEM_PORT_TYPE_AT:
|
||||
case MM_MODEM_PORT_TYPE_QMI:
|
||||
case MM_MODEM_PORT_TYPE_MBIM:
|
||||
g_ptr_array_add (aux, g_strdup (port_infos[i].name));
|
||||
break;
|
||||
case MM_MODEM_PORT_TYPE_UNKNOWN:
|
||||
case MM_MODEM_PORT_TYPE_NET:
|
||||
case MM_MODEM_PORT_TYPE_QCDM:
|
||||
case MM_MODEM_PORT_TYPE_GPS:
|
||||
case MM_MODEM_PORT_TYPE_AUDIO:
|
||||
case MM_MODEM_PORT_TYPE_IGNORED:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
mm_modem_port_info_array_free (port_infos, n_port_infos);
|
||||
g_ptr_array_add (aux, NULL);
|
||||
modem_ports = (GStrv) g_ptr_array_free (aux, FALSE);
|
||||
|
||||
mm_fcc_unlock_dispatcher_run (dispatcher,
|
||||
mm_base_modem_get_vendor_id (MM_BASE_MODEM (self)),
|
||||
mm_base_modem_get_product_id (MM_BASE_MODEM (self)),
|
||||
g_dbus_object_get_object_path (G_DBUS_OBJECT (self)),
|
||||
modem_ports,
|
||||
g_task_get_cancellable (task),
|
||||
(GAsyncReadyCallback)fcc_unlock_dispatcher_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
static void
|
||||
requested_power_setup_ready (MMIfaceModem *self,
|
||||
GAsyncResult *res,
|
||||
@@ -3959,13 +4011,11 @@ set_power_state_step (GTask *task)
|
||||
if ((ctx->requested_power_state == MM_MODEM_POWER_STATE_ON) &&
|
||||
ctx->saved_error &&
|
||||
g_error_matches (ctx->saved_error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY) &&
|
||||
!ctx->fcc_unlock_attempted &&
|
||||
MM_IFACE_MODEM_GET_INTERFACE (self)->fcc_unlock &&
|
||||
MM_IFACE_MODEM_GET_INTERFACE (self)->fcc_unlock_finish) {
|
||||
!ctx->fcc_unlock_attempted) {
|
||||
mm_obj_dbg (self, "attempting fcc unlock...");
|
||||
ctx->fcc_unlock_attempted = TRUE;
|
||||
g_clear_error (&ctx->saved_error);
|
||||
MM_IFACE_MODEM_GET_INTERFACE (self)->fcc_unlock (self, (GAsyncReadyCallback)fcc_unlock_ready, task);
|
||||
ctx->fcc_unlock_attempted = TRUE;
|
||||
fcc_unlock (task);
|
||||
return;
|
||||
}
|
||||
ctx->step++;
|
||||
|
@@ -270,14 +270,6 @@ struct _MMIfaceModem {
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
/* Asynchronous FCC unlock operation */
|
||||
void (* fcc_unlock) (MMIfaceModem *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean (* fcc_unlock_finish) (MMIfaceModem *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
/* Asynchronous modem power-up operation */
|
||||
void (*modem_power_up) (MMIfaceModem *self,
|
||||
GAsyncReadyCallback callback,
|
||||
|
@@ -4220,56 +4220,6 @@ mm_shared_qmi_setup_sim_hot_swap (MMIfaceModem *self,
|
||||
task);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* FCC unlock (Modem interface) */
|
||||
|
||||
gboolean
|
||||
mm_shared_qmi_fcc_unlock_finish (MMIfaceModem *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
dms_set_fcc_authentication_ready (QmiClientDms *client,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
GError *error = NULL;
|
||||
g_autoptr(QmiMessageDmsSetFccAuthenticationOutput) output = NULL;
|
||||
|
||||
output = qmi_client_dms_set_fcc_authentication_finish (client, res, &error);
|
||||
if (!output || !qmi_message_dms_set_fcc_authentication_output_get_result (output, &error))
|
||||
g_task_return_error (task, error);
|
||||
else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
void
|
||||
mm_shared_qmi_fcc_unlock (MMIfaceModem *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
QmiClient *client = NULL;
|
||||
|
||||
if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
|
||||
QMI_SERVICE_DMS, &client,
|
||||
callback, user_data))
|
||||
return;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
|
||||
qmi_client_dms_set_fcc_authentication (QMI_CLIENT_DMS (client),
|
||||
NULL,
|
||||
5,
|
||||
NULL,
|
||||
(GAsyncReadyCallback)dms_set_fcc_authentication_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Location: Set SUPL server */
|
||||
|
||||
|
@@ -191,12 +191,6 @@ void mm_shared_qmi_setup_sim_hot_swap (MMIfaceMode
|
||||
gboolean mm_shared_qmi_setup_sim_hot_swap_finish (MMIfaceModem *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
void mm_shared_qmi_fcc_unlock (MMIfaceModem *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean mm_shared_qmi_fcc_unlock_finish (MMIfaceModem *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
/* Shared QMI location support */
|
||||
|
||||
|
Reference in New Issue
Block a user