aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@aleksander.es>2021-02-18 15:52:11 +0100
committerAleksander Morgado <aleksander@aleksander.es>2021-03-10 10:59:22 +0100
commit7b20550ac0fb114c871720c56e40150fba7079f7 (patch)
tree1d319a5e164d0a8477085d3faff140bc3a88cc8a
parent032a86915a6d28cf39bc249a45dfb4d02e3a7eb6 (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.
-rw-r--r--src/mm-port-qmi.c207
-rw-r--r--src/mm-port-qmi.h8
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 */