diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2021-02-24 10:18:39 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2021-03-10 12:58:12 +0100 |
commit | b3fb87cdf6ceb1593a27807b7f00f17ca489e23d (patch) | |
tree | deb7591b9e31548ce8de46421b6db5d2fcf60d88 /src/mm-base-modem.c | |
parent | 897e48709d1f5238f5165a5e5ed42937b0784c80 (diff) |
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.
Diffstat (limited to 'src/mm-base-modem.c')
-rw-r--r-- | src/mm-base-modem.c | 128 |
1 files changed, 128 insertions, 0 deletions
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, |