diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2021-02-18 15:52:11 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2021-03-10 10:59:22 +0100 |
commit | 7b20550ac0fb114c871720c56e40150fba7079f7 (patch) | |
tree | 1d319a5e164d0a8477085d3faff140bc3a88cc8a /src | |
parent | 032a86915a6d28cf39bc249a45dfb4d02e3a7eb6 (diff) |
port-qmi: implement port reset logic
The port reset operation will attempt to setup the QMI and associated
NET ports to the same state as they has when the modem was originally
detected: no mux links, net interface down and (for the qmi_wwan based
devices) expected kernel data format set to 802.3.
By default, the external calls to the port reset logic will require
the port to be closed (i.e. no internal QmiDevice), so a new temporary
QmiDevice will be created just for this operation. The new QmiDevice
doesn't even need to be open, as the reset operations just uses the
device to attempt to remove net links.
The internal call to the port reset logic uses an input QmiDevice
which may be the newly allocated one in the external call, or the
internal QmiDevice, if there is one.
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-port-qmi.c | 207 | ||||
-rw-r--r-- | src/mm-port-qmi.h | 8 |
2 files changed, 215 insertions, 0 deletions
diff --git a/src/mm-port-qmi.c b/src/mm-port-qmi.c index a5f065aa..7c79e69b 100644 --- a/src/mm-port-qmi.c +++ b/src/mm-port-qmi.c @@ -22,6 +22,7 @@ #include <mm-errors-types.h> #include "mm-port-qmi.h" +#include "mm-port-net.h" #include "mm-modem-helpers-qmi.h" #include "mm-log-object.h" @@ -274,6 +275,212 @@ mm_port_qmi_allocate_client (MMPortQmi *self, /*****************************************************************************/ +typedef struct { + QmiDevice *device; + MMPort *data; +} InternalResetContext; + +static void +internal_reset_context_free (InternalResetContext *ctx) +{ + g_clear_object (&ctx->device); + g_clear_object (&ctx->data); + g_slice_free (InternalResetContext, ctx); +} + +static gboolean +internal_reset_finish (MMPortQmi *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +delete_all_links_ready (QmiDevice *device, + GAsyncResult *res, + GTask *task) +{ + MMPortQmi *self; + InternalResetContext *ctx; + GError *error = NULL; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + /* link deletion not fatal, it may happen if in 802.3 already */ + if (!qmi_device_delete_all_links_finish (device, res, &error)) { + mm_obj_dbg (self, "couldn't delete all links: %s", error->message); + g_clear_error (&error); + } + + /* expected data format only applicable to qmi_wwan */ + if (mm_port_get_subsys (MM_PORT (self)) == MM_PORT_SUBSYS_USBMISC) { + mm_obj_dbg (self, "reseting expected kernel data format to 802.3 in data interface '%s'", + mm_port_get_device (MM_PORT (ctx->data))); + if (!qmi_device_set_expected_data_format (ctx->device, QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + } + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void +net_link_down_ready (MMPortNet *data, + GAsyncResult *res, + GTask *task) +{ + MMPortQmi *self; + InternalResetContext *ctx; + GError *error = NULL; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + if (!mm_port_net_link_setup_finish (data, res, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* first, delete all links found, if any */ + mm_obj_dbg (self, "deleting all links in data interface '%s'", + mm_port_get_device (ctx->data)); + qmi_device_delete_all_links (ctx->device, + mm_port_get_device (ctx->data), + NULL, + (GAsyncReadyCallback)delete_all_links_ready, + task); +} + +static void +internal_reset (MMPortQmi *self, + MMPort *data, + QmiDevice *device, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + InternalResetContext *ctx; + + task = g_task_new (self, NULL, callback, user_data); + + ctx = g_slice_new0 (InternalResetContext); + ctx->data = g_object_ref (data); + ctx->device = g_object_ref (device); + g_task_set_task_data (task, ctx, (GDestroyNotify) internal_reset_context_free); + + /* first, bring down master interface */ + mm_obj_dbg (self, "bringing down data interface '%s'", + mm_port_get_device (ctx->data)); + mm_port_net_link_setup (MM_PORT_NET (ctx->data), + FALSE, + MM_PORT_NET_MTU_DEFAULT, + NULL, + (GAsyncReadyCallback) net_link_down_ready, + task); +} + +/*****************************************************************************/ + +typedef struct { + QmiDevice *device; + MMPort *data; +} ResetContext; + +static void +reset_context_free (ResetContext *ctx) +{ + g_clear_object (&ctx->device); + g_clear_object (&ctx->data); + g_slice_free (ResetContext, ctx); +} + +gboolean +mm_port_qmi_reset_finish (MMPortQmi *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +internal_reset_ready (MMPortQmi *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + + if (!internal_reset_finish (self, res, &error)) + g_task_return_error (task, error); + else + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void +reset_device_new_ready (GObject *source, + GAsyncResult *res, + GTask *task) +{ + MMPortQmi *self; + ResetContext *ctx; + GError *error = NULL; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + ctx->device = qmi_device_new_finish (res, &error); + if (!ctx->device) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + internal_reset (self, + ctx->data, + ctx->device, + (GAsyncReadyCallback) internal_reset_ready, + task); +} + +void +mm_port_qmi_reset (MMPortQmi *self, + MMPort *data, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + ResetContext *ctx; + g_autoptr(GFile) file = NULL; + g_autofree gchar *fullpath = NULL; + + task = g_task_new (self, NULL, callback, user_data); + + if (self->priv->qmi_device) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port is already open"); + g_object_unref (task); + return; + } + + ctx = g_slice_new0 (ResetContext); + ctx->data = g_object_ref (data); + g_task_set_task_data (task, ctx, (GDestroyNotify) reset_context_free); + + fullpath = g_strdup_printf ("/dev/%s", mm_port_get_device (MM_PORT (self))); + file = g_file_new_for_path (fullpath); + + qmi_device_new (file, NULL, + (GAsyncReadyCallback) reset_device_new_ready, + task); +} + +/*****************************************************************************/ + QmiWdaLinkLayerProtocol mm_port_qmi_get_link_layer_protocol (MMPortQmi *self) { diff --git a/src/mm-port-qmi.h b/src/mm-port-qmi.h index f776951d..835f35c3 100644 --- a/src/mm-port-qmi.h +++ b/src/mm-port-qmi.h @@ -112,4 +112,12 @@ gboolean mm_port_qmi_setup_data_format_finish (MMPortQmi *s GAsyncResult *res, GError **error); +void mm_port_qmi_reset (MMPortQmi *self, + MMPort *data, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_port_qmi_reset_finish (MMPortQmi *self, + GAsyncResult *res, + GError **error); + #endif /* MM_PORT_QMI_H */ |