aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksandermj@chromium.org>2023-03-03 13:36:56 +0000
committerAleksander Morgado <aleksander@aleksander.es>2023-03-10 10:59:42 +0000
commitdad3e8274723805e3957f166ba158a37cf6a96ec (patch)
tree486c32cb293bfceaf8effc5aa41fd8c473afe020
parent4903a1ed74eb8dd723330cd49006a5938bf8afb9 (diff)
broadband-modem-qmi: disable profile changed indications during our operations
Just ignoring the received indications is not enough, because they could arrive after the operation response has been processed. We now explicitly disable the indications by reconfiguring the modem before and after every profile update operation triggered by our own logic.
-rw-r--r--src/mm-broadband-modem-qmi.c402
1 files changed, 299 insertions, 103 deletions
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c
index ee4a97b5..d692cef7 100644
--- a/src/mm-broadband-modem-qmi.c
+++ b/src/mm-broadband-modem-qmi.c
@@ -176,6 +176,7 @@ struct _MMBroadbandModemQmiPrivate {
/* WDS Profile changed notification ID (3gpp Profile Manager) */
guint profile_changed_indication_id;
+ gint profile_changed_indication_enabled;
gint profile_changed_indication_ignored;
/* PS registration helpers when using NAS System Info and DSD
@@ -6503,12 +6504,130 @@ modem_3gpp_profile_manager_list_profiles (MMIfaceModem3gppProfileManager *self,
task);
}
+/*******************************************************************************/
+/* Enable/Disable/Ignore profile changed events (3gppProfileManager interface) */
+
+static gboolean
+profile_changed_indication_configure_finish (MMBroadbandModemQmi *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+register_wds_profile_change_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageWdsIndicationRegisterOutput) output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_wds_indication_register_finish (client, res, &error);
+ if (!output || !qmi_message_wds_indication_register_output_get_result (output, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+profile_changed_indication_configure (MMBroadbandModemQmi *self,
+ QmiClientWds *client,
+ gboolean state_needed,
+ GTask *task)
+{
+ g_autoptr(QmiMessageWdsIndicationRegisterInput) input = NULL;
+
+ input = qmi_message_wds_indication_register_input_new ();
+ qmi_message_wds_indication_register_input_set_report_profile_changes (input, state_needed, NULL);
+ qmi_client_wds_indication_register (client,
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) register_wds_profile_change_ready,
+ task);
+}
+
+static void
+profile_changed_indication_configure_ignore (MMBroadbandModemQmi *self,
+ QmiClientWds *client,
+ gboolean ignore,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ gboolean state_needed_before;
+ gboolean state_needed_after;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ state_needed_before = (self->priv->profile_changed_indication_enabled &&
+ self->priv->profile_changed_indication_ignored == 0);
+
+ /* Note: multiple concurrent profile create/update/deletes may be happening,
+ * so ensure the indication ignore logic applies as long as at least one
+ * operation is ongoing. */
+ if (ignore) {
+ g_assert_cmpint (self->priv->profile_changed_indication_ignored, >=, 0);
+ self->priv->profile_changed_indication_ignored++;
+ mm_obj_dbg (self, "ignore profile update indications during our own operations (%d ongoing)",
+ self->priv->profile_changed_indication_ignored);
+ } else {
+ g_assert_cmpint (self->priv->profile_changed_indication_ignored, >, 0);
+ self->priv->profile_changed_indication_ignored--;
+ if (self->priv->profile_changed_indication_ignored > 0)
+ mm_obj_dbg (self, "still ignoring profile update indications during our own operations (%d ongoing)",
+ self->priv->profile_changed_indication_ignored);
+ else
+ mm_obj_dbg (self, "no longer ignoring profile update indications during our own operations");
+ }
+
+ state_needed_after = (self->priv->profile_changed_indication_enabled &&
+ self->priv->profile_changed_indication_ignored == 0);
+
+ if (state_needed_before == state_needed_after) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ profile_changed_indication_configure (self, client, state_needed_after, task);
+}
+
+static void
+profile_changed_indication_configure_enable (MMBroadbandModemQmi *self,
+ QmiClientWds *client,
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ gboolean state_needed_before;
+ gboolean state_needed_after;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ state_needed_before = (self->priv->profile_changed_indication_enabled &&
+ self->priv->profile_changed_indication_ignored == 0);
+
+ self->priv->profile_changed_indication_enabled = enable;
+
+ state_needed_after = (self->priv->profile_changed_indication_enabled &&
+ self->priv->profile_changed_indication_ignored == 0);
+
+ if (state_needed_before == state_needed_after) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ profile_changed_indication_configure (self, client, state_needed_after, task);
+}
+
/*****************************************************************************/
/* Store profile (3GPP profile management interface) */
-static void profile_changed_indication_ignore (MMBroadbandModemQmi *self,
- gboolean ignore);
-
typedef struct {
QmiClientWds *client;
gint profile_id;
@@ -6519,11 +6638,13 @@ typedef struct {
QmiWdsApnTypeMask qmi_apn_type;
QmiWdsAuthentication qmi_auth;
QmiWdsPdpType qmi_pdp_type;
+ GError *saved_error;
} StoreProfileContext;
static void
store_profile_context_free (StoreProfileContext *ctx)
{
+ g_assert (!ctx->saved_error);
g_free (ctx->profile_name);
g_free (ctx->apn);
g_free (ctx->user);
@@ -6552,6 +6673,46 @@ modem_3gpp_profile_manager_store_profile_finish (MMIfaceModem3gppProfileManager
return TRUE;
}
+static void
+profile_changed_indication_configure_after_store_ready (MMBroadbandModemQmi *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ StoreProfileContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!profile_changed_indication_configure_finish (self, res, &error))
+ mm_obj_warn (self, "Couldn't configure profile change indications after store operation: %s", error->message);
+
+ if (ctx->saved_error)
+ g_task_return_error (task, g_steal_pointer (&ctx->saved_error));
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+store_profile_complete (GTask *task,
+ GError *error)
+{
+ MMBroadbandModemQmi *self;
+ StoreProfileContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ ctx->saved_error = error;
+
+ profile_changed_indication_configure_ignore (
+ self,
+ ctx->client,
+ FALSE,
+ (GAsyncReadyCallback) profile_changed_indication_configure_after_store_ready,
+ task);
+}
+
static void store_profile_run (GTask *task);
static void
@@ -6559,17 +6720,14 @@ modify_profile_ready (QmiClientWds *client,
GAsyncResult *res,
GTask *task)
{
- MMBroadbandModemQmi *self;
- GError *error = NULL;
- g_autoptr(QmiMessageWdsModifyProfileOutput) output = NULL;
+ MMBroadbandModemQmi *self;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(QmiMessageWdsModifyProfileOutput) output = NULL;
self = g_task_get_source_object (task);
- profile_changed_indication_ignore (self, FALSE);
output = qmi_client_wds_modify_profile_finish (client, res, &error);
- if (!output) {
- g_task_return_error (task, error);
- } else if (!qmi_message_wds_modify_profile_output_get_result (output, &error)) {
+ if (output && !qmi_message_wds_modify_profile_output_get_result (output, &error)) {
QmiWdsDsProfileError ds_profile_error;
if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_PROFILE_TYPE) &&
@@ -6578,20 +6736,18 @@ modify_profile_ready (QmiClientWds *client,
* in newer devices. */
mm_obj_dbg (self, "APN type flagged as not supported: failed to modify profile");
self->priv->apn_type_not_supported = TRUE;
- g_clear_error (&error);
store_profile_run (task);
return;
}
+
if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) &&
qmi_message_wds_modify_profile_output_get_extended_error_code (output, &ds_profile_error, NULL)) {
g_prefix_error (&error, "DS profile error: %s: ", qmi_wds_ds_profile_error_get_string (ds_profile_error));
}
g_prefix_error (&error, "Couldn't modify profile: ");
- g_task_return_error (task, error);
- } else {
- g_task_return_boolean (task, TRUE);
}
- g_object_unref (task);
+
+ store_profile_complete (task, g_steal_pointer (&error));
}
static void
@@ -6601,18 +6757,15 @@ create_profile_ready (QmiClientWds *client,
{
MMBroadbandModemQmi *self;
StoreProfileContext *ctx;
- GError *error = NULL;
guint8 profile_index;
+ g_autoptr(GError) error = NULL;
g_autoptr(QmiMessageWdsCreateProfileOutput) output = NULL;
ctx = g_task_get_task_data (task);
self = g_task_get_source_object (task);
- profile_changed_indication_ignore (self, FALSE);
output = qmi_client_wds_create_profile_finish (client, res, &error);
- if (!output) {
- g_task_return_error (task, error);
- } else if (!qmi_message_wds_create_profile_output_get_result (output, &error)) {
+ if (output && !qmi_message_wds_create_profile_output_get_result (output, &error)) {
QmiWdsDsProfileError ds_profile_error;
if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_PROFILE_TYPE) &&
@@ -6621,23 +6774,21 @@ create_profile_ready (QmiClientWds *client,
* in newer devices. */
mm_obj_dbg (self, "APN type flagged as not supported: failed to create profile");
self->priv->apn_type_not_supported = TRUE;
- g_clear_error (&error);
store_profile_run (task);
return;
}
+
if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) &&
qmi_message_wds_create_profile_output_get_extended_error_code (output, &ds_profile_error, NULL)) {
g_prefix_error (&error, "DS profile error: %s: ", qmi_wds_ds_profile_error_get_string (ds_profile_error));
}
g_prefix_error (&error, "Couldn't create profile: ");
- g_task_return_error (task, error);
- } else if (!qmi_message_wds_create_profile_output_get_profile_identifier (output, NULL, &profile_index, &error)) {
- g_task_return_error (task, error);
- } else {
- ctx->profile_id = profile_index;
- g_task_return_boolean (task, TRUE);
}
- g_object_unref (task);
+
+ if (!error && qmi_message_wds_create_profile_output_get_profile_identifier (output, NULL, &profile_index, &error))
+ ctx->profile_id = profile_index;
+
+ store_profile_complete (task, g_steal_pointer (&error));
}
static void
@@ -6664,7 +6815,6 @@ store_profile_run (GTask *task)
if (!self->priv->apn_type_not_supported)
qmi_message_wds_create_profile_input_set_apn_type_mask (input, ctx->qmi_apn_type, NULL);
- profile_changed_indication_ignore (self, TRUE);
qmi_client_wds_create_profile (ctx->client,
input,
10,
@@ -6685,7 +6835,6 @@ store_profile_run (GTask *task)
if (!self->priv->apn_type_not_supported)
qmi_message_wds_modify_profile_input_set_apn_type_mask (input, ctx->qmi_apn_type, NULL);
- profile_changed_indication_ignore (self, TRUE);
qmi_client_wds_modify_profile (ctx->client,
input,
10,
@@ -6696,6 +6845,19 @@ store_profile_run (GTask *task)
}
static void
+profile_changed_indication_configure_before_store_ready (MMBroadbandModemQmi *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (!profile_changed_indication_configure_finish (self, res, &error))
+ mm_obj_warn (self, "Couldn't configure profile change indications before store operation: %s", error->message);
+
+ store_profile_run (task);
+}
+
+static void
modem_3gpp_profile_manager_store_profile (MMIfaceModem3gppProfileManager *self,
MM3gppProfile *profile,
const gchar *index_field,
@@ -6756,12 +6918,31 @@ modem_3gpp_profile_manager_store_profile (MMIfaceModem3gppProfileManager *self,
return;
}
- store_profile_run (task);
+ profile_changed_indication_configure_ignore (
+ MM_BROADBAND_MODEM_QMI (self),
+ ctx->client,
+ TRUE,
+ (GAsyncReadyCallback) profile_changed_indication_configure_before_store_ready,
+ task);
}
/*****************************************************************************/
/* Delete profile (3GPP profile management interface) */
+typedef struct {
+ QmiClientWds *client;
+ gint profile_id;
+ GError *saved_error;
+} DeleteProfileContext;
+
+static void
+delete_profile_context_free (DeleteProfileContext *ctx)
+{
+ g_assert (!ctx->saved_error);
+ g_clear_object (&ctx->client);
+ g_slice_free (DeleteProfileContext, ctx);
+}
+
static gboolean
modem_3gpp_profile_manager_delete_profile_finish (MMIfaceModem3gppProfileManager *self,
GAsyncResult *res,
@@ -6771,24 +6952,76 @@ modem_3gpp_profile_manager_delete_profile_finish (MMIfaceModem3gppProfileManager
}
static void
+profile_changed_indication_configure_after_delete_ready (MMBroadbandModemQmi *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ DeleteProfileContext *ctx;
+ g_autoptr(QmiMessageWdsDeleteProfileInput) input = NULL;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!profile_changed_indication_configure_finish (self, res, &error))
+ mm_obj_warn (self, "Couldn't configure profile change indications after delete operation: %s", error->message);
+
+ if (ctx->saved_error)
+ g_task_return_error (task, g_steal_pointer (&ctx->saved_error));
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
delete_profile_ready (QmiClientWds *client,
GAsyncResult *res,
GTask *task)
{
- MMBroadbandModemQmi *self;
- GError *error = NULL;
+ MMBroadbandModemQmi *self;
+ DeleteProfileContext *ctx;
+ g_autoptr(GError) error = NULL;
g_autoptr(QmiMessageWdsDeleteProfileOutput) output = NULL;
self = g_task_get_source_object (task);
- profile_changed_indication_ignore (self, FALSE);
+ ctx = g_task_get_task_data (task);
output = qmi_client_wds_delete_profile_finish (client, res, &error);
- if (!output || !qmi_message_wds_delete_profile_output_get_result (output, &error)) {
+ if (!output || !qmi_message_wds_delete_profile_output_get_result (output, &error))
g_prefix_error (&error, "Couldn't delete profile: ");
- g_task_return_error (task, error);
- } else
- g_task_return_boolean (task, TRUE);
- g_object_unref (task);
+
+ /* Store as task data to complete the task later */
+ ctx->saved_error = g_steal_pointer (&error);
+
+ profile_changed_indication_configure_ignore (
+ self,
+ ctx->client,
+ FALSE,
+ (GAsyncReadyCallback) profile_changed_indication_configure_after_delete_ready,
+ task);
+}
+
+static void
+profile_changed_indication_configure_before_delete_ready (MMBroadbandModemQmi *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ DeleteProfileContext *ctx;
+ g_autoptr(QmiMessageWdsDeleteProfileInput) input = NULL;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!profile_changed_indication_configure_finish (self, res, &error))
+ mm_obj_warn (self, "Couldn't configure profile change indications before delete operation: %s", error->message);
+
+ input = qmi_message_wds_delete_profile_input_new ();
+ qmi_message_wds_delete_profile_input_set_profile_identifier (input, QMI_WDS_PROFILE_TYPE_3GPP, ctx->profile_id, NULL);
+ qmi_client_wds_delete_profile (QMI_CLIENT_WDS (ctx->client),
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)delete_profile_ready,
+ task);
}
static void
@@ -6798,10 +7031,9 @@ modem_3gpp_profile_manager_delete_profile (MMIfaceModem3gppProfileManager *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GTask *task;
- QmiClient *client = NULL;
- gint profile_id;
- g_autoptr(QmiMessageWdsDeleteProfileInput) input = NULL;
+ DeleteProfileContext *ctx;
+ GTask *task;
+ QmiClient *client = NULL;
g_assert (g_strcmp0 (index_field, "profile-id") == 0);
@@ -6811,22 +7043,20 @@ modem_3gpp_profile_manager_delete_profile (MMIfaceModem3gppProfileManager *self,
return;
task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_slice_new0 (DeleteProfileContext);
+ ctx->client = QMI_CLIENT_WDS (g_object_ref (client));
+ ctx->profile_id = mm_3gpp_profile_get_profile_id (profile);
+ g_assert (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) delete_profile_context_free);
- profile_id = mm_3gpp_profile_get_profile_id (profile);
- g_assert (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
-
- mm_obj_dbg (self, "deleting profile '%d'", profile_id);
-
- input = qmi_message_wds_delete_profile_input_new ();
- qmi_message_wds_delete_profile_input_set_profile_identifier (input, QMI_WDS_PROFILE_TYPE_3GPP, profile_id, NULL);
+ mm_obj_dbg (self, "deleting profile '%d'", ctx->profile_id);
- profile_changed_indication_ignore (MM_BROADBAND_MODEM_QMI (self), TRUE);
- qmi_client_wds_delete_profile (QMI_CLIENT_WDS (client),
- input,
- 10,
- NULL,
- (GAsyncReadyCallback)delete_profile_ready,
- task);
+ profile_changed_indication_configure_ignore (
+ MM_BROADBAND_MODEM_QMI (self),
+ ctx->client,
+ TRUE,
+ (GAsyncReadyCallback) profile_changed_indication_configure_before_delete_ready,
+ task);
}
/*****************************************************************************/
@@ -6849,38 +7079,10 @@ profile_changed_indication_received (QmiClientWds *clien
QmiIndicationWdsProfileChangedOutput *output,
MMBroadbandModemQmi *self)
{
- if (self->priv->profile_changed_indication_ignored > 0) {
- mm_obj_dbg (self, "profile changed indication ignored");
- return;
- }
-
mm_obj_dbg (self, "profile changed indication was received");
mm_iface_modem_3gpp_profile_manager_updated (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self));
}
-static void
-profile_changed_indication_ignore (MMBroadbandModemQmi *self,
- gboolean ignore)
-{
- /* Note: multiple concurrent profile create/update/deletes may be happening,
- * so ensure the indication ignore logic applies as long as at least one
- * operation is ongoing. */
- if (ignore) {
- g_assert_cmpint (self->priv->profile_changed_indication_ignored, >=, 0);
- self->priv->profile_changed_indication_ignored++;
- mm_obj_dbg (self, "ignoring profile update indications during our own operations (%d ongoing)",
- self->priv->profile_changed_indication_ignored);
- } else {
- g_assert_cmpint (self->priv->profile_changed_indication_ignored, >, 0);
- self->priv->profile_changed_indication_ignored--;
- if (self->priv->profile_changed_indication_ignored > 0)
- mm_obj_dbg (self, "still ignoring profile update indications during our own operations (%d ongoing)",
- self->priv->profile_changed_indication_ignored);
- else
- mm_obj_dbg (self, "no longer ignoring profile update indications during our own operations");
- }
-}
-
/*****************************************************************************/
/* Enable/Disable unsolicited events (3gppProfileManager interface) */
@@ -6940,18 +7142,16 @@ register_pdc_refresh_ready (QmiClientPdc *client,
}
static void
-register_wds_profile_change_ready (QmiClientWds *client,
- GAsyncResult *res,
- GTask *task)
+profile_changed_indication_configure_ready (MMBroadbandModemQmi *self,
+ GAsyncResult *res,
+ GTask *task)
{
- g_autoptr(QmiMessageWdsIndicationRegisterOutput) output = NULL;
- RegisterProfileRefreshContext *ctx;
- GError *error = NULL;
+ RegisterProfileRefreshContext *ctx;
+ GError *error = NULL;
ctx = g_task_get_task_data (task);
- output = qmi_client_wds_indication_register_finish (client, res, &error);
- if (!output || !qmi_message_wds_indication_register_output_get_result (output, &error)) {
+ if (!profile_changed_indication_configure_finish (self, res, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
@@ -7016,16 +7216,12 @@ register_profile_refresh_context_step (GTask *task)
case REGISTER_PROFILE_REFRESH_STEP_PROFILE_CHANGE:
if (ctx->client_wds) {
- g_autoptr(QmiMessageWdsIndicationRegisterInput) input = NULL;
-
- input = qmi_message_wds_indication_register_input_new ();
- qmi_message_wds_indication_register_input_set_report_profile_changes (input, ctx->enable, NULL);
- qmi_client_wds_indication_register (ctx->client_wds,
- input,
- 10,
- NULL,
- (GAsyncReadyCallback) register_wds_profile_change_ready,
- task);
+ profile_changed_indication_configure_enable (
+ self,
+ ctx->client_wds,
+ ctx->enable,
+ (GAsyncReadyCallback) profile_changed_indication_configure_ready,
+ task);
return;
}
ctx->step++;