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.
This commit is contained in:
Aleksander Morgado
2023-03-03 13:36:56 +00:00
committed by Aleksander Morgado
parent 4903a1ed74
commit dad3e82747

View File

@@ -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,
@@ -6695,6 +6844,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,
@@ -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,
@@ -6770,25 +6951,77 @@ modem_3gpp_profile_manager_delete_profile_finish (MMIfaceModem3gppProfileManager
return g_task_propagate_boolean (G_TASK (res), error);
}
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'", ctx->profile_id);
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);
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++;