broadband-modem-qmi: setup operation context for power up/down/off

We must keep a valid QmiClient reference for as long as the operation
runs, so that we can use it in all the request send operations we do,
and also so that we can remove the indication id upon completing the
operation.

The timeout id and indication id, which are also part of a single
power operation are also included in the operation context, because
they don't have any other meaning out of it.

The operation context is stored as task data.
This commit is contained in:
Aleksander Morgado
2022-01-11 12:26:26 +01:00
parent 1291754e60
commit 60f61e9740

View File

@@ -162,12 +162,8 @@ 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;
};
/*****************************************************************************/
@@ -1708,6 +1704,22 @@ load_signal_quality (MMIfaceModem *self,
/*****************************************************************************/
/* Powering up/down/off the modem (Modem interface) */
typedef struct {
QmiDmsOperatingMode mode;
QmiClientDms *client;
guint indication_id;
guint timeout_id;
} SetOperatingModeContext;
static void
set_operating_mode_context_free (SetOperatingModeContext *ctx)
{
g_assert (ctx->indication_id == 0);
g_assert (ctx->timeout_id == 0);
g_clear_object (&ctx->client);
g_slice_free (SetOperatingModeContext, ctx);
}
static gboolean
modem_power_up_down_off_finish (MMIfaceModem *self,
GAsyncResult *res,
@@ -1717,51 +1729,36 @@ modem_power_up_down_off_finish (MMIfaceModem *self,
}
static void
modem_power_indication_deregister (MMBroadbandModemQmi *self)
{
g_autoptr(QmiMessageDmsSetEventReportInput) input = NULL;
QmiClient *client;
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);
}
static void
dms_set_operating_mode_complete (MMBroadbandModemQmi *self,
set_operating_mode_complete (MMBroadbandModemQmi *self,
GError *error)
{
GTask *task;
SetOperatingModeContext *ctx;
g_assert (self->priv->set_operating_mode_task);
task = g_steal_pointer (&self->priv->set_operating_mode_task);
g_assert (task != NULL);
ctx = g_task_get_task_data (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);
if (ctx->timeout_id) {
g_source_remove (ctx->timeout_id);
ctx->timeout_id = 0;
}
if (error) {
if (ctx->indication_id) {
g_autoptr(QmiMessageDmsSetEventReportInput) input = NULL;
g_signal_handler_disconnect (ctx->client, ctx->indication_id);
ctx->indication_id = 0;
input = qmi_message_dms_set_event_report_input_new ();
qmi_message_dms_set_event_report_input_set_operating_mode_reporting (input, FALSE, NULL);
qmi_client_dms_set_event_report (ctx->client, input, 5, NULL, NULL, NULL);
}
if (error)
g_task_return_error (task, error);
} else {
else
g_task_return_boolean (task, TRUE);
}
g_object_unref (task);
}
@@ -1771,7 +1768,7 @@ dms_set_operating_mode_timeout_cb (MMBroadbandModemQmi *self)
GError *error = NULL;
error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Power update operation timed out");
dms_set_operating_mode_complete (self, error);
set_operating_mode_complete (self, error);
}
static void
@@ -1780,30 +1777,30 @@ power_event_report_indication_cb (QmiClientDms *client,
MMBroadbandModemQmi *self)
{
QmiDmsOperatingMode state;
GTask *task;
GError *error = NULL;
QmiDmsOperatingMode mode;
SetOperatingModeContext *ctx;
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);
set_operating_mode_complete (self, error);
return;
}
task = self->priv->set_operating_mode_task;
mode = GPOINTER_TO_UINT (g_task_get_task_data (task));
if (mode == state) {
g_assert (self->priv->set_operating_mode_task);
ctx = g_task_get_task_data (self->priv->set_operating_mode_task);
if (ctx->mode == state) {
mm_obj_dbg (self, "Power state successfully updated: '%s'", qmi_dms_operating_mode_get_string (state));
dms_set_operating_mode_complete (self, NULL);
set_operating_mode_complete (self, NULL);
return;
}
error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Requested mode (%s) and mode received (%s) did not match",
qmi_dms_operating_mode_get_string (mode),
qmi_dms_operating_mode_get_string (ctx->mode),
qmi_dms_operating_mode_get_string (state));
dms_set_operating_mode_complete (self, error);
set_operating_mode_complete (self, error);
}
static void
@@ -1811,19 +1808,17 @@ dms_set_operating_mode_ready (QmiClientDms *client,
GAsyncResult *res,
MMBroadbandModemQmi *self) /* full reference */
{
GTask *task;
QmiDmsOperatingMode mode;
GError *error = NULL;
g_autoptr (QmiMessageDmsSetOperatingModeOutput) output = NULL;
GError *error = NULL;
SetOperatingModeContext *ctx;
task = self->priv->set_operating_mode_task;
if (task == NULL) {
if (!self->priv->set_operating_mode_task) {
/* We completed the operation already via indication */
g_object_unref (self);
return;
}
mode = GPOINTER_TO_UINT (g_task_get_task_data (task));
ctx = g_task_get_task_data (self->priv->set_operating_mode_task);
output = qmi_client_dms_set_operating_mode_finish (client, res, &error);
if (!output) {
@@ -1846,7 +1841,7 @@ dms_set_operating_mode_ready (QmiClientDms *client,
* retrying. Notify this to upper layers with the special MM_CORE_ERROR_RETRY
* error.
*/
if ((mode == QMI_DMS_OPERATING_MODE_ONLINE) &&
if ((ctx->mode == QMI_DMS_OPERATING_MODE_ONLINE) &&
((g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INTERNAL) ||
g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_TRANSITION)))) {
g_clear_error (&error);
@@ -1855,7 +1850,7 @@ dms_set_operating_mode_ready (QmiClientDms *client,
}
if (error)
dms_set_operating_mode_complete (self, error);
set_operating_mode_complete (self, error);
else
mm_obj_dbg (self, "operating mode request sent, waiting for power update indication");
@@ -1865,26 +1860,15 @@ dms_set_operating_mode_ready (QmiClientDms *client,
static void
dms_set_operating_mode (MMBroadbandModemQmi *self)
{
QmiClient *client;
GError *error = NULL;
GTask *task;
QmiDmsOperatingMode mode;
g_autoptr (QmiMessageDmsSetOperatingModeInput) input = NULL;
SetOperatingModeContext *ctx;
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;
}
g_assert (self->priv->set_operating_mode_task);
ctx = g_task_get_task_data (self->priv->set_operating_mode_task);
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),
qmi_message_dms_set_operating_mode_input_set_mode (input, ctx->mode, NULL);
qmi_client_dms_set_operating_mode (ctx->client,
input,
20,
NULL,
@@ -1892,7 +1876,7 @@ dms_set_operating_mode (MMBroadbandModemQmi *self)
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,
ctx->timeout_id = g_timeout_add_seconds (10,
(GSourceFunc) dms_set_operating_mode_timeout_cb,
self);
}
@@ -1904,31 +1888,26 @@ dms_set_event_report_operating_mode_activate_ready (QmiClientDms *client,
{
g_autoptr(QmiMessageDmsSetEventReportOutput) output = NULL;
GError *error = NULL;
SetOperatingModeContext *ctx;
g_assert (self->priv->set_operating_mode_task);
ctx = g_task_get_task_data (self->priv->set_operating_mode_task);
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)) {
g_prefix_error (&error, "Power indication failed: couldn't set event report");
dms_set_operating_mode_complete (self, error);
if (!output || !qmi_message_dms_set_event_report_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't register for power indications: ");
set_operating_mode_complete (self, error);
g_object_unref (self);
return;
}
g_assert (self->priv->power_event_report_indication_id == 0);
self->priv->power_event_report_indication_id =
g_signal_connect (client,
g_assert (ctx->indication_id == 0);
ctx->indication_id = g_signal_connect (client,
"event-report",
G_CALLBACK (power_event_report_indication_cb),
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);
}
@@ -1937,24 +1916,16 @@ static void
modem_power_indication_register (MMBroadbandModemQmi *self)
{
g_autoptr(QmiMessageDmsSetEventReportInput) input = NULL;
QmiClient *client;
GError *error = NULL;
SetOperatingModeContext *ctx;
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;
}
g_assert (self->priv->set_operating_mode_task);
ctx = g_task_get_task_data (self->priv->set_operating_mode_task);
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),
ctx->client,
input,
5,
NULL,
@@ -1968,12 +1939,13 @@ common_power_up_down_off (MMIfaceModem *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
GError *error = NULL;
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
GError *error = NULL;
GTask *task;
SetOperatingModeContext *ctx;
QmiClient *client;
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");
@@ -1982,6 +1954,21 @@ common_power_up_down_off (MMIfaceModem *_self,
return;
}
client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
QMI_SERVICE_DMS,
MM_PORT_QMI_FLAG_DEFAULT,
&error);
if (!client) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
ctx = g_slice_new0 (SetOperatingModeContext);
ctx->mode = mode;
ctx->client = QMI_CLIENT_DMS (g_object_ref (client));
g_task_set_task_data (task, ctx, (GDestroyNotify)set_operating_mode_context_free);
self->priv->set_operating_mode_task = task;
modem_power_indication_register (self);
}