diff options
author | Nero <zrxu.zhang@fibocom.com> | 2024-03-14 21:32:54 +0800 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2024-04-16 12:14:45 +0200 |
commit | 04d826f563a497f2af0557982cdfae7586ca8efb (patch) | |
tree | 71991768ce0084e75e668a6e0924eada43813c19 | |
parent | 941461a2c5c08b76319a91b1662d2b7ea63d7273 (diff) |
dispatcher-modem-setup: new modem setup dispatcher
There are cases where the user wants to configure some actions
to be run on a modem before ModemManager takes over full control.
The new "modem setup" dispatcher scripts allow running operations
in the modem after the port probing has happened and we know which
ports are usable in the device.
The scripts may e.g. change device configuration, enable modem
logging infrastructure, or any other process that would otherwise
conflict with MM's own modem management operations.
-rw-r--r-- | data/dispatcher-modem-setup/0000:0000 | 25 | ||||
-rw-r--r-- | data/dispatcher-modem-setup/meson.build | 26 | ||||
-rw-r--r-- | meson.build | 1 | ||||
-rw-r--r-- | src/meson.build | 3 | ||||
-rw-r--r-- | src/mm-base-manager.c | 105 | ||||
-rw-r--r-- | src/mm-base-modem.c | 26 | ||||
-rw-r--r-- | src/mm-dispatcher-modem-setup.c | 150 | ||||
-rw-r--r-- | src/mm-dispatcher-modem-setup.h | 50 |
8 files changed, 359 insertions, 27 deletions
diff --git a/data/dispatcher-modem-setup/0000:0000 b/data/dispatcher-modem-setup/0000:0000 new file mode 100644 index 00000000..6489e9b3 --- /dev/null +++ b/data/dispatcher-modem-setup/0000:0000 @@ -0,0 +1,25 @@ +#!/bin/sh + +# SPDX-License-Identifier: CC0-1.0 +# 2024 Nero Sinaro <xu.zhang@fibocom.com> +# +# modem setup test script +# +# This is the test script: +# Parameter list: vid pid device_path port_name +# device_path: Path of the device on the sys file system, it's a string +# port_name: Port name of the device enumeration, It's an array of strings +# + +logger "[Test] ARGV:$@, ARG count:$#" + +count=1 +while [ $count -lt 10 ]; do + # will print logger to syslog + logger "[Test] (attempt #${i})" + + sleep 0.5 + count=$((count + 1)) +done + +exit $? diff --git a/data/dispatcher-modem-setup/meson.build b/data/dispatcher-modem-setup/meson.build new file mode 100644 index 00000000..812152a3 --- /dev/null +++ b/data/dispatcher-modem-setup/meson.build @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2024 Nero Sinaro <xu.zhang@fibocom.com> + +# Shipped but disabled Modem setup tools +mm_modemsetupdiravailable = mm_pkgdatadir / 'modem-setup.available.d' + +# Directory for user-enabled tools +mm_modemsetupdiruser = mm_pkgsysconfdir / 'modem-setup.d' + +# Directory for package-enabled tools +mm_modemsetupdirpackage = mm_pkglibdir / 'modem-setup.d' + +# This is a test file +examples = files( + '0000:0000' +) + +install_data( + examples, + install_mode: 'rwx------', + install_dir: mm_modemsetupdiravailable, +) + +mkdir_cmd = 'mkdir -p ${DESTDIR}@0@' +meson.add_install_script('sh', '-c', mkdir_cmd.format(mm_prefix / mm_modemsetupdiruser)) +meson.add_install_script('sh', '-c', mkdir_cmd.format(mm_prefix / mm_modemsetupdirpackage)) diff --git a/meson.build b/meson.build index 1adc5e5d..61efb149 100644 --- a/meson.build +++ b/meson.build @@ -404,6 +404,7 @@ subdir('data') if get_option('examples') subdir('data/dispatcher-connection') subdir('data/dispatcher-fcc-unlock') + subdir('data/dispatcher-modem-setup') endif subdir('introspection') subdir('include') diff --git a/src/meson.build b/src/meson.build index 3052e219..f3a6218f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -269,6 +269,7 @@ sources = files( 'mm-dispatcher.c', 'mm-dispatcher-connection.c', 'mm-dispatcher-fcc-unlock.c', + 'mm-dispatcher-modem-setup.c', 'mm-filter.c', 'mm-iface-modem-3gpp.c', 'mm-iface-modem-3gpp-profile-manager.c', @@ -308,6 +309,8 @@ endif c_args = [ '-DMM_COMPILATION', '-DPLUGINDIR="@0@"'.format(mm_prefix / mm_pkglibdir), + '-DMODEMSETUPDIRPACKAGE="@0@"'.format(mm_prefix / mm_pkglibdir / 'modem-setup.d'), + '-DMODEMSETUPDIRUSER="@0@"'.format(mm_prefix / mm_pkgsysconfdir / 'modem-setup.d'), '-DFCCUNLOCKDIRPACKAGE="@0@"'.format(mm_prefix / mm_pkglibdir / 'fcc-unlock.d'), '-DFCCUNLOCKDIRUSER="@0@"'.format(mm_prefix / mm_pkgsysconfdir / 'fcc-unlock.d'), '-DCONNECTIONDIRPACKAGE="@0@"'.format(mm_prefix / mm_pkglibdir / 'connection.d'), diff --git a/src/mm-base-manager.c b/src/mm-base-manager.c index 74e4020d..b2a14b04 100644 --- a/src/mm-base-manager.c +++ b/src/mm-base-manager.c @@ -59,6 +59,8 @@ #include "mm-base-modem.h" #include "mm-iface-modem.h" +#include "mm-dispatcher-modem-setup.h" + static void initable_iface_init (GInitableIface *iface); static void log_object_iface_init (MMLogObjectInterface *iface); @@ -201,6 +203,106 @@ find_device_support_context_free (FindDeviceSupportContext *ctx) g_slice_free (FindDeviceSupportContext, ctx); } +/*****************************************************************************/ +static void +initialize_ready (MMBaseModem *self, + GAsyncResult *res) +{ + g_autoptr(GError) error = NULL; + + if (!mm_base_modem_initialize_finish (self, res, &error)) { + if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED)) { + /* FATAL error, won't even be exported in DBus */ + mm_obj_err (self, "fatal error initializing: %s", error->message); + } else { + /* non-fatal error */ + mm_obj_warn (self, "error initializing: %s", error->message); + mm_base_modem_set_valid (self, TRUE); + } + } else { + mm_obj_dbg (self, "modem initialized"); + mm_base_modem_set_valid (self, TRUE); + } +} + +static void +dispatcher_modem_setup_ready (MMDispatcherModemSetup *dispatcher, + GAsyncResult *res, + FindDeviceSupportContext *ctx) +{ + g_autoptr(GError) error = NULL; + MMBaseModem *modem; + + if (!mm_dispatcher_modem_setup_run_finish (dispatcher, res, &error)) { + if (!g_error_matches(error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND)) { + mm_obj_err (ctx->self, "couldn't run modem setup: %s", error->message); + } else { + mm_obj_dbg (ctx->self, "no need to run modem setup"); + } + } + + modem = mm_device_peek_modem (ctx->device); + if (modem) { + /* As soon as we get the ports organized, we initialize the modem */ + mm_base_modem_initialize (modem, + (GAsyncReadyCallback)initialize_ready, + NULL); + } else { + mm_obj_warn(modem, "no modem found"); + } + + find_device_support_context_free (ctx); +} + +static void +modem_setup (FindDeviceSupportContext *ctx) +{ + guint n_port_infos = 0; + g_auto(GStrv) modem_ports = NULL; + MMDispatcherModemSetup *dispatcher; + MMModemPortInfo *port_infos; + MMBaseModem *modem = NULL; + GPtrArray *aux; + guint i; + + dispatcher = mm_dispatcher_modem_setup_get (); + + modem = mm_device_peek_modem (ctx->device); + + aux = g_ptr_array_new (); + port_infos = mm_base_modem_get_port_infos (modem, &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_dispatcher_modem_setup_run (dispatcher, + mm_base_modem_get_vendor_id (modem), + mm_base_modem_get_product_id (modem), + mm_base_modem_get_device (modem), + modem_ports, + NULL, + (GAsyncReadyCallback)dispatcher_modem_setup_ready, + ctx); +} + static void device_support_check_ready (MMPluginManager *plugin_manager, GAsyncResult *res, @@ -238,7 +340,8 @@ device_support_check_ready (MMPluginManager *plugin_manager, /* Modem now created */ mm_obj_msg (ctx->self, "modem for device '%s' successfully created", mm_device_get_uid (ctx->device)); - find_device_support_context_free (ctx); + + modem_setup (ctx); } static gboolean is_device_inhibited (MMBaseManager *self, diff --git a/src/mm-base-modem.c b/src/mm-base-modem.c index 2cb9383d..3ef4905f 100644 --- a/src/mm-base-modem.c +++ b/src/mm-base-modem.c @@ -1284,27 +1284,6 @@ mm_base_modem_get_port (MMBaseModem *self, return (port ? g_object_ref (port) : NULL); } -static void -initialize_ready (MMBaseModem *self, - GAsyncResult *res) -{ - g_autoptr(GError) error = NULL; - - if (!mm_base_modem_initialize_finish (self, res, &error)) { - if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED)) { - /* FATAL error, won't even be exported in DBus */ - mm_obj_err (self, "fatal error initializing: %s", error->message); - } else { - /* non-fatal error */ - mm_obj_warn (self, "error initializing: %s", error->message); - mm_base_modem_set_valid (self, TRUE); - } - } else { - mm_obj_dbg (self, "modem initialized"); - mm_base_modem_set_valid (self, TRUE); - } -} - static inline void log_port (MMBaseModem *self, MMPort *port, @@ -1627,11 +1606,6 @@ mm_base_modem_organize_ports (MMBaseModem *self, } #endif - /* As soon as we get the ports organized, we initialize the modem */ - mm_base_modem_initialize (self, - (GAsyncReadyCallback)initialize_ready, - NULL); - return TRUE; } diff --git a/src/mm-dispatcher-modem-setup.c b/src/mm-dispatcher-modem-setup.c new file mode 100644 index 00000000..5867ecf1 --- /dev/null +++ b/src/mm-dispatcher-modem-setup.c @@ -0,0 +1,150 @@ +/* -*- 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) 2023-2024 Nero Sinaro <xu.zhang@fibocom.com> + */ + +#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-dispatcher-modem-setup.h" + +#if !defined MODEMSETUPDIRPACKAGE +# error MODEMSETUPDIRPACKAGE must be defined at build time +#endif + +#if !defined MODEMSETUPDIRUSER +# error MODEMSETUPDIRUSER must be defined at build time +#endif + +#define OPERATION_DESCRIPTION "modem setup" + +/* Maximum time a Modem Setup command is allowed to run before + * us killing it */ +#define MAX_MODEM_SETUP_EXEC_TIME_SECS 5 + +struct _MMDispatcherModemSetup { + MMDispatcher parent; +}; + +struct _MMDispatcherModemSetupClass { + MMDispatcherClass parent; +}; + +G_DEFINE_TYPE (MMDispatcherModemSetup, mm_dispatcher_modem_setup, MM_TYPE_DISPATCHER) + +/*****************************************************************************/ +gboolean +mm_dispatcher_modem_setup_run_finish (MMDispatcherModemSetup *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +dispatcher_run_ready (MMDispatcher *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + + if (!mm_dispatcher_run_finish (self, res, &error)) + g_task_return_error (task, error); + else + g_task_return_boolean (task, TRUE); + + g_object_unref (task); +} + +void +mm_dispatcher_modem_setup_run (MMDispatcherModemSetup *self, + guint vid, + guint pid, + const gchar *device_path, + const GStrv modem_ports, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autofree gchar *filename = NULL; + GTask *task; + g_auto(GStrv) argv = NULL; + GPtrArray *aux = NULL; + guint i; + guint j; + + const gchar *enabled_dirs[] = { + MODEMSETUPDIRUSER, /* sysconfdir */ + MODEMSETUPDIRPACKAGE, /* libdir */ + }; + + task = g_task_new (self, cancellable, callback, user_data); + filename = g_strdup_printf ("%04x:%04x", vid, pid); + + /* Iterate over all enabled dirs and collect all dispatcher script paths */ + for (i = 0; i < G_N_ELEMENTS (enabled_dirs); i++) { + g_autofree gchar *path = NULL; + g_autoptr(GFile) file = NULL; + + path = g_build_path (G_DIR_SEPARATOR_S, enabled_dirs[i], filename, NULL); + file = g_file_new_for_path (path); + + /* If file exists, we attempt to use it */ + if (!g_file_query_exists (file, cancellable)) { + continue; + } + + aux = g_ptr_array_new (); + g_ptr_array_add (aux, g_steal_pointer (&path)); + g_ptr_array_add (aux, g_strdup (device_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); + + /* run */ + mm_dispatcher_run (MM_DISPATCHER (self), + argv, + MAX_MODEM_SETUP_EXEC_TIME_SECS, + g_task_get_cancellable (task), + (GAsyncReadyCallback) dispatcher_run_ready, + task); + return; + } + + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, + OPERATION_DESCRIPTION " operation launch aborted: no valid program found"); + g_object_unref (task); +} +/*****************************************************************************/ + +static void +mm_dispatcher_modem_setup_init (MMDispatcherModemSetup *self) +{ +} + +static void +mm_dispatcher_modem_setup_class_init (MMDispatcherModemSetupClass *class) +{ +} + +MM_DEFINE_SINGLETON_GETTER (MMDispatcherModemSetup, mm_dispatcher_modem_setup_get, MM_TYPE_DISPATCHER_MODEM_SETUP, + MM_DISPATCHER_OPERATION_DESCRIPTION, OPERATION_DESCRIPTION) + diff --git a/src/mm-dispatcher-modem-setup.h b/src/mm-dispatcher-modem-setup.h new file mode 100644 index 00000000..8738af86 --- /dev/null +++ b/src/mm-dispatcher-modem-setup.h @@ -0,0 +1,50 @@ +/* -*- 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) 2023-2024 Nero Sinaro <xu.zhang@fibocom.com> + */ + +#ifndef MM_DISPATCHER_MODEM_SETUP_H +#define MM_DISPATCHER_MODEM_SETUP_H + +#include <config.h> +#include <gio/gio.h> + +#include "mm-dispatcher.h" + +#define MM_TYPE_DISPATCHER_MODEM_SETUP (mm_dispatcher_modem_setup_get_type ()) +#define MM_DISPATCHER_MODEM_SETUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_DISPATCHER_MODEM_SETUP, MMDispatcherModemSetup)) +#define MM_DISPATCHER_MODEM_SETUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_DISPATCHER_MODEM_SETUP, MMDispatcherModemSetupClass)) +#define MM_IS_DISPATCHER_MODEM_SETUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_DISPATCHER_MODEM_SETUP)) +#define MM_IS_DISPATCHER_MODEM_SETUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_DISPATCHER_MODEM_SETUP)) +#define MM_DISPATCHER_MODEM_SETUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_DISPATCHER_MODEM_SETUP, MMDispatcherModemSetupClass)) + +typedef struct _MMDispatcherModemSetup MMDispatcherModemSetup; +typedef struct _MMDispatcherModemSetupClass MMDispatcherModemSetupClass; +typedef struct _MMDispatcherModemSetupPrivate MMDispatcherModemSetupPrivate; + +GType mm_dispatcher_modem_setup_get_type (void); +MMDispatcherModemSetup *mm_dispatcher_modem_setup_get (void); +void mm_dispatcher_modem_setup_run (MMDispatcherModemSetup *self, + guint vid, + guint pid, + const gchar *modem_dbus_path, + const GStrv ports, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean mm_dispatcher_modem_setup_run_finish (MMDispatcherModemSetup *self, + GAsyncResult *res, + GError **error); + +#endif /* MM_DISPATCHER_MODEM_SETUP_H */ |