aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPrakash Pabba <quic_ppabba@quicinc.com>2021-12-03 12:52:40 +0530
committerAleksander Morgado <aleksander@aleksander.es>2022-01-11 11:30:19 +0100
commit2d96df774a3de0b18cc35d1077d325eba58a7a91 (patch)
treebfcef58d8e5e397c38b14e5f6f59b62f3d83f5c5 /src
parent300e1023ef2daed40685bb90ea38a029c443396a (diff)
broadband-modem-qmi: Handle DMS operating mode indications
If Mode online is triggered just after Mode LPM and Mode LPM processing is not complete in modem, modem will not process the next request and the device will be stuck in an unwanted modem power state. Thus, wait for a mode change indication from the modem before declaring that a power on/off has been successful.
Diffstat (limited to 'src')
-rw-r--r--src/mm-broadband-modem-qmi.c272
1 files changed, 245 insertions, 27 deletions
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c
index 990ffce4..71e0cf75 100644
--- a/src/mm-broadband-modem-qmi.c
+++ b/src/mm-broadband-modem-qmi.c
@@ -12,7 +12,7 @@
*
* Copyright (C) 2012 Google Inc.
* Copyright (C) 2014 Aleksander Morgado <aleksander@aleksander.es>
- * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <config.h>
@@ -161,6 +161,13 @@ struct _MMBroadbandModemQmiPrivate {
/* For notifying when the qmi-proxy connection is dead */
guint qmi_device_removed_id;
+
+ /* Power Indication Helper */
+ guint power_event_report_indication_id;
+
+ /* Power Set Operating Mode Helper */
+ GTask *set_operating_mode_task;
+ guint set_operating_timeout_id;
};
/*****************************************************************************/
@@ -1699,6 +1706,181 @@ load_signal_quality (MMIfaceModem *self,
}
/*****************************************************************************/
+/* Power Indication Registration */
+
+static void
+dms_set_operating_mode (MMBroadbandModemQmi *self);
+
+static void
+modem_power_indication_deregister (MMBroadbandModemQmi *self)
+{
+ QmiMessageDmsSetEventReportInput *input = NULL;
+ QmiClient *client = NULL;
+
+ g_assert (self->priv->power_event_report_indication_id != 0);
+ g_signal_handler_disconnect (QMI_CLIENT_DMS (client),
+ self->priv->power_event_report_indication_id);
+ self->priv->power_event_report_indication_id = 0;
+
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ if (!client) {
+ return;
+ }
+
+ input = qmi_message_dms_set_event_report_input_new ();
+ qmi_message_dms_set_event_report_input_set_operating_mode_reporting (input, FALSE, NULL);
+ mm_obj_dbg (self, "Power indication deregistration request is sent");
+ qmi_client_dms_set_event_report (
+ QMI_CLIENT_DMS (client),
+ input,
+ 5,
+ NULL,
+ NULL,
+ NULL);
+ qmi_message_dms_set_event_report_input_unref (input);
+}
+
+static void
+dms_set_operating_mode_complete (MMBroadbandModemQmi *self,
+ GError *error)
+{
+ GTask *task;
+
+ task = g_steal_pointer (&(self->priv->set_operating_mode_task));
+
+ if (self->priv->set_operating_timeout_id) {
+ g_source_remove (self->priv->set_operating_timeout_id);
+ self->priv->set_operating_timeout_id = 0;
+ }
+ if (self->priv->power_event_report_indication_id) {
+ modem_power_indication_deregister (self);
+ }
+ g_assert (task != NULL);
+ if (error) {
+ g_task_return_error (task, error);
+ } else {
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+}
+
+static void
+dms_set_operating_mode_timeout_cb (MMBroadbandModemQmi *self)
+{
+ GError *error = NULL;
+
+ error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Operation timed out. Did not get any indication");
+ dms_set_operating_mode_complete (self, error);
+ g_object_unref (self);
+}
+
+static void
+power_event_report_indication_cb (QmiClientDms *client,
+ QmiIndicationDmsEventReportOutput *output,
+ MMBroadbandModemQmi *self)
+{
+ QmiDmsOperatingMode state;
+ GTask *task;
+ GError *error = NULL;
+ QmiDmsOperatingMode mode;
+
+ mm_obj_dbg (self, "Power indication received");
+
+ if (!qmi_indication_dms_event_report_output_get_operating_mode (output, &state, NULL)) {
+ error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Invalid power indication received");
+ dms_set_operating_mode_complete (self, error);
+ g_object_unref (self);
+ return;
+ }
+ mm_obj_dbg (self, "Power state update: '%s'",
+ qmi_dms_operating_mode_get_string (state));
+ task = self->priv->set_operating_mode_task;
+ mode = GPOINTER_TO_UINT (g_task_get_task_data (task));
+ if (mode == state) {
+ mm_obj_dbg (self, "Requested mode and mode received by indication matched");
+ dms_set_operating_mode_complete (self, NULL);
+ g_object_unref (self);
+ return;
+ }
+ error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Requested mode and mode received by indication did not match");
+ dms_set_operating_mode_complete (self, error);
+ g_object_unref (self);
+}
+
+static void
+power_indication_activation_callback_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ MMBroadbandModemQmi *self)
+{
+ QmiMessageDmsSetEventReportOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_dms_set_event_report_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "Power indication activation qmi operation failed");
+ dms_set_operating_mode_complete (self, error);
+ g_object_unref (self);
+ return;
+ }
+ if (!qmi_message_dms_set_event_report_output_get_result (output, &error)) {
+ qmi_message_dms_set_event_report_output_unref (output);
+ g_prefix_error (&error, "Power indication failed: couldn't set event report");
+ dms_set_operating_mode_complete (self, error);
+ g_object_unref (self);
+ return;
+ }
+ qmi_message_dms_set_event_report_output_unref (output);
+ g_assert (self->priv->power_event_report_indication_id == 0);
+ self->priv->power_event_report_indication_id =
+ g_signal_connect (client,
+ "event-report",
+ G_CALLBACK (power_event_report_indication_cb),
+ g_object_ref (self));
+ g_assert (self->priv->set_operating_mode_task != NULL);
+ mm_obj_dbg (self, "Power operation is pending");
+ dms_set_operating_mode (self);
+ g_object_unref (self);
+}
+
+static void
+modem_power_indication_register (MMBroadbandModemQmi *self)
+{
+ QmiMessageDmsSetEventReportInput *input = NULL;
+ QmiClient *client = NULL;
+ GError *error = NULL;
+
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ &error);
+
+ if (!client) {
+ dms_set_operating_mode_complete (self, error);
+ return;
+ }
+ input = qmi_message_dms_set_event_report_input_new ();
+ qmi_message_dms_set_event_report_input_set_operating_mode_reporting (input, TRUE, NULL);
+ mm_obj_dbg (self, "Power indication registration request is sent");
+ qmi_client_dms_set_event_report (
+ QMI_CLIENT_DMS (client),
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)power_indication_activation_callback_ready,
+ g_object_ref (self));
+ qmi_message_dms_set_event_report_input_unref (input);
+}
+
+/*****************************************************************************/
/* Powering up the modem (Modem interface) */
static gboolean
@@ -1712,14 +1894,19 @@ modem_power_up_down_off_finish (MMIfaceModem *self,
static void
dms_set_operating_mode_ready (QmiClientDms *client,
GAsyncResult *res,
- GTask *task)
+ MMBroadbandModemQmi *self)
{
- MMBroadbandModemQmi *self;
- QmiDmsOperatingMode mode;
- GError *error = NULL;
- g_autoptr(QmiMessageDmsSetOperatingModeOutput) output = NULL;
+ GTask *task;
+ QmiDmsOperatingMode mode;
+ GError *error = NULL;
+ g_autoptr (QmiMessageDmsSetOperatingModeOutput) output = NULL;
- self = g_task_get_source_object (task);
+ task = self->priv->set_operating_mode_task;
+ if (task == NULL) {
+ mm_obj_dbg (self, "Indication received before response. Task returned.");
+ g_object_unref (self);
+ return;
+ }
mode = GPOINTER_TO_UINT (g_task_get_task_data (task));
output = qmi_client_dms_set_operating_mode_finish (client, res, &error);
@@ -1751,31 +1938,33 @@ dms_set_operating_mode_ready (QmiClientDms *client,
}
}
- if (error)
- g_task_return_error (task, error);
- else
- g_task_return_boolean (task, TRUE);
- g_object_unref (task);
+ if (error) {
+ dms_set_operating_mode_complete (self, error);
+ } else {
+ mm_obj_dbg (self, "Waiting for power indication to send success for the task");
+ }
+ g_object_unref (self);
}
static void
-common_power_up_down_off (MMIfaceModem *self,
- QmiDmsOperatingMode mode,
- GAsyncReadyCallback callback,
- gpointer user_data)
+dms_set_operating_mode (MMBroadbandModemQmi *self)
{
- GTask *task;
- QmiClient *client = NULL;
- g_autoptr(QmiMessageDmsSetOperatingModeInput) input = NULL;
+ QmiClient *client = NULL;
+ GError *error = NULL;
+ GTask *task = NULL;
+ QmiDmsOperatingMode mode;
+ g_autoptr (QmiMessageDmsSetOperatingModeInput) input = NULL;
- if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ task = self->priv->set_operating_mode_task;
+ mode = GPOINTER_TO_UINT (g_task_get_task_data(task));
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ &error);
+ if (!client) {
+ dms_set_operating_mode_complete (self, error);
return;
-
- task = g_task_new (self, NULL, callback, user_data);
- g_task_set_task_data (task, GUINT_TO_POINTER (mode), NULL);
-
+ }
input = qmi_message_dms_set_operating_mode_input_new ();
qmi_message_dms_set_operating_mode_input_set_mode (input, mode, NULL);
qmi_client_dms_set_operating_mode (QMI_CLIENT_DMS (client),
@@ -1783,7 +1972,36 @@ common_power_up_down_off (MMIfaceModem *self,
20,
NULL,
(GAsyncReadyCallback)dms_set_operating_mode_ready,
- task);
+ g_object_ref (self));
+ mm_obj_dbg (self, "Starting timeout for indication receiving for 10 seconds");
+ self->priv->set_operating_timeout_id = g_timeout_add_seconds (10,
+ (GSourceFunc) dms_set_operating_mode_timeout_cb,
+ g_object_ref (self));
+}
+
+static void
+common_power_up_down_off (MMIfaceModem *_self,
+ QmiDmsOperatingMode mode,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ GError *error = NULL;
+ MMBroadbandModemQmi *self = NULL;
+
+ self = MM_BROADBAND_MODEM_QMI (_self);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (mode), NULL);
+
+ if (self->priv->set_operating_mode_task) {
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "Another Operation in Progress");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ self->priv->set_operating_mode_task = task;
+ g_assert (self->priv->set_operating_mode_task != NULL);
+ modem_power_indication_register (self);
}
static void