From b3fb87cdf6ceb1593a27807b7f00f17ca489e23d Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Wed, 24 Feb 2021 10:18:39 +0100 Subject: base-modem: new method to wait for an explicit link port When new link ports are created, the QmiDevice link management methods try to make sure that the port exists in the system by the time the link addition method returns, but that doesn't guarantee that the port has also been notified by the kernel to ModemManager (e.g. via udev or via kernel events). This new method allows to do a explicit wait for a given port link; if we already have it it will return right away, and otherwise we'll wait for it to be notified via the "link port grabbed" signal. --- src/mm-base-modem.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) (limited to 'src/mm-base-modem.c') diff --git a/src/mm-base-modem.c b/src/mm-base-modem.c index 8122a0ee..54e28b0d 100644 --- a/src/mm-base-modem.c +++ b/src/mm-base-modem.c @@ -441,6 +441,134 @@ mm_base_modem_release_link_port (MMBaseModem *self, /******************************************************************************/ +typedef struct { + gchar *name; + gulong link_port_grabbed_id; + guint timeout_id; +} WaitLinkPortContext; + +static void +wait_link_port_context_free (WaitLinkPortContext *ctx) +{ + g_assert (!ctx->link_port_grabbed_id); + g_assert (!ctx->timeout_id); + g_free (ctx->name); + g_slice_free (WaitLinkPortContext, ctx); +} + +MMPort * +mm_base_modem_wait_link_port_finish (MMBaseModem *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static gboolean +wait_link_port_timeout_cb (GTask *task) +{ + WaitLinkPortContext *ctx; + MMBaseModem *self; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + ctx->timeout_id = 0; + g_signal_handler_disconnect (self, ctx->link_port_grabbed_id); + ctx->link_port_grabbed_id = 0; + + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, + "Timed out waiting for link port 'net/%s'", + ctx->name); + g_object_unref (task); + + return G_SOURCE_REMOVE; +} + +static void +wait_link_port_grabbed_cb (MMBaseModem *self, + MMPort *link_port, + GTask *task) +{ + WaitLinkPortContext *ctx; + MMPortSubsys link_port_subsystem; + const gchar *link_port_name; + + ctx = g_task_get_task_data (task); + + link_port_subsystem = mm_port_get_subsys (link_port); + link_port_name = mm_port_get_device (link_port); + + if (link_port_subsystem != MM_PORT_SUBSYS_NET) { + mm_obj_warn (self, "unexpected link port subsystem grabbed: %s/%s", + mm_port_subsys_get_string (link_port_subsystem), + link_port_name); + return; + } + + if (g_strcmp0 (link_port_name, ctx->name) != 0) + return; + + /* we got it! */ + + g_source_remove (ctx->timeout_id); + ctx->timeout_id = 0; + g_signal_handler_disconnect (self, ctx->link_port_grabbed_id); + ctx->link_port_grabbed_id = 0; + + g_task_return_pointer (task, g_object_ref (link_port), g_object_unref); + g_object_unref (task); +} + +void +mm_base_modem_wait_link_port (MMBaseModem *self, + const gchar *subsystem, + const gchar *name, + guint timeout_ms, + GAsyncReadyCallback callback, + gpointer user_data) +{ + WaitLinkPortContext *ctx; + GTask *task; + g_autofree gchar *key = NULL; + MMPort *port; + + task = g_task_new (self, NULL, callback, user_data); + + if (!g_str_equal (subsystem, "net")) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, + "Cannot wait for port '%s/%s', unexpected link port subsystem", subsystem, name); + g_object_unref (task); + return; + } + + key = g_strdup_printf ("%s%s", subsystem, name); + port = g_hash_table_lookup (self->priv->link_ports, key); + if (port) { + mm_obj_dbg (self, "no need to wait for port '%s/%s': already grabbed", subsystem, name); + g_task_return_pointer (task, g_object_ref (port), g_object_unref); + g_object_unref (task); + return; + } + + ctx = g_slice_new0 (WaitLinkPortContext); + ctx->name = g_strdup (name); + g_task_set_task_data (task, ctx, (GDestroyNotify)wait_link_port_context_free); + + /* task ownership shared between timeout and signal handler */ + ctx->timeout_id = g_timeout_add (timeout_ms, + (GSourceFunc) wait_link_port_timeout_cb, + task); + ctx->link_port_grabbed_id = g_signal_connect (self, + MM_BASE_MODEM_SIGNAL_LINK_PORT_GRABBED, + G_CALLBACK (wait_link_port_grabbed_cb), + task); + + mm_obj_dbg (self, "waiting for port '%s/%s'...", subsystem, name); +} + +/******************************************************************************/ + gboolean mm_base_modem_disable_finish (MMBaseModem *self, GAsyncResult *res, -- cgit v1.2.3-70-g09d2