diff options
-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 */ |