[PATCH v2] telit-plugin: handle QSS unsolicited due to power state transitions
Carlo Lobrano
c.lobrano at gmail.com
Wed Aug 30 12:40:22 UTC 2017
When transitioning between power-low and power-on modes, Telit modems
switch the SIM off/on, which leads to the emission of #QSS unsolicited not
related to an actual SIM swap.
To handle this use case, this patch:
* disables reacting on #QSS unsolicited when modem_power_down is received
* implements modem_after_power_up that:
- checks whether the SIM has been changed, matching cached SIM Identifier
with the value in the current SIM. If SIM Identifier, is different,
sim_hot_swap_ports_detected is called.
- re-enables reacting on #QSS unsolicited
---
Hi Aleksander,
here is my second patch version for the problem.
Not 100% sure this is what you expected, please let me know what do you think.
Thanks,
Carlo
---
plugins/telit/mm-broadband-modem-telit.c | 200 ++++++++++++++++++++++++++++++-
1 file changed, 196 insertions(+), 4 deletions(-)
diff --git a/plugins/telit/mm-broadband-modem-telit.c b/plugins/telit/mm-broadband-modem-telit.c
index a9fc5f0..2f3dcca 100644
--- a/plugins/telit/mm-broadband-modem-telit.c
+++ b/plugins/telit/mm-broadband-modem-telit.c
@@ -58,6 +58,7 @@ struct _MMBroadbandModemTelitPrivate {
MMTelitCsimLockState csim_lock_state;
GTask *csim_lock_task;
guint csim_lock_timeout_id;
+ gboolean parse_qss;
};
/*****************************************************************************/
@@ -151,10 +152,15 @@ telit_qss_unsolicited_handler (MMPortSerialAt *port,
}
if (cur_qss_status != prev_qss_status)
- mm_dbg ("QSS handler: status changed '%s -> %s",
+ mm_dbg ("QSS handler: status changed '%s -> %s'",
mm_telit_qss_status_get_string (prev_qss_status),
mm_telit_qss_status_get_string (cur_qss_status));
+ if (self->priv->parse_qss == FALSE) {
+ mm_dbg ("QSS: ignore QSS is enabled");
+ return;
+ }
+
if ((prev_qss_status == QSS_STATUS_SIM_REMOVED && cur_qss_status != QSS_STATUS_SIM_REMOVED) ||
(prev_qss_status > QSS_STATUS_SIM_REMOVED && cur_qss_status == QSS_STATUS_SIM_REMOVED)) {
mm_info ("QSS handler: SIM swap detected");
@@ -903,14 +909,193 @@ modem_load_unlock_retries (MMIfaceModem *self,
}
/*****************************************************************************/
+/* Modem power up (Modem interface) */
+static void
+modem_power_up (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CFUN=1",
+ 5,
+ FALSE,
+ (GAsyncReadyCallback) callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Modem after power up (Modem interface) */
+typedef enum {
+ AFTER_POWER_UP_STEP_FIRST,
+ AFTER_POWER_UP_STEP_GET_SIM_IDENTIFIER,
+ AFTER_POWER_UP_STEP_ENABLE_QSS_PARSING,
+ AFTER_POWER_UP_STEP_LAST
+} ModemAfterPowerUpStep;
+
+typedef struct {
+ MMBroadbandModemTelit *self;
+ gboolean has_sim_changed;
+ guint retries;
+ ModemAfterPowerUpStep step;
+ GError *error;
+} AfterPowerUpContext;
+
+static void after_power_up_step (GTask *task);
+
+static void
+after_power_up_context_free (AfterPowerUpContext *ctx)
+{
+ g_clear_object (&(ctx->error));
+ g_object_unref (ctx->self);
+ g_slice_free (AfterPowerUpContext, ctx);
+}
+
+static void
+load_sim_identifier_ready (MMBaseSim *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ AfterPowerUpContext *ctx;
+ GError *error = NULL;
+ gchar *current_simid;
+ gchar *cached_simid;
+
+ ctx = g_task_get_task_data (task);
+
+ cached_simid = (gchar *)mm_gdbus_sim_get_sim_identifier (MM_GDBUS_SIM (self));
+ current_simid = MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_finish (self, res, &error);
+ if (error) {
+ if (ctx->retries > 0) {
+ ctx->retries--;
+ mm_warn ("Couldn't read SIM identifier: '%s', %d retries left", error->message, ctx->retries);
+
+ g_clear_error (&error);
+ g_timeout_add_seconds (1, (GSourceFunc) after_power_up_step, task);
+ return;
+ }
+
+ mm_warn ("Couldn't read SIM identifier: '%s'", error->message);
+ g_clear_error (&error);
+ goto out;
+ }
+
+ if (g_strcmp0 (current_simid, cached_simid) != 0) {
+ mm_warn ("sim identifier has changed, possible SIM swap during power down/low");
+ ctx->has_sim_changed = TRUE;
+ }
+
+out:
+ g_free (current_simid);
+ g_object_unref (self);
+ ctx->step++;
+ after_power_up_step (task);
+}
+
+static void
+after_power_up_step (GTask *task)
+{
+ AfterPowerUpContext *ctx;
+ MMBaseSim *sim;
+
+ ctx = g_task_get_task_data (task);
+
+ g_object_get (MM_BROADBAND_MODEM_TELIT (ctx->self),
+ MM_IFACE_MODEM_SIM, &sim,
+ NULL);
+ if (!sim) {
+ mm_dbg ("Could not acquire MMBaseSim object");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Could not acquire MMBaseSim object");
+ return;
+ }
+
+ switch (ctx->step) {
+ case AFTER_POWER_UP_STEP_FIRST:
+ /* Fall back on next step */
+ ctx->step++;
+ case AFTER_POWER_UP_STEP_GET_SIM_IDENTIFIER:
+ MM_BASE_SIM_GET_CLASS (sim)->load_sim_identifier (sim,
+ (GAsyncReadyCallback)load_sim_identifier_ready,
+ task);
+ return;
+ case AFTER_POWER_UP_STEP_ENABLE_QSS_PARSING:
+ mm_dbg ("Stop ignoring #QSS unsolicited");
+ ctx->self->priv->parse_qss = TRUE;
+
+ /* Fall back on next step */
+ ctx->step++;
+ case AFTER_POWER_UP_STEP_LAST:
+ if (ctx->has_sim_changed)
+ mm_broadband_modem_update_sim_hot_swap_detected (MM_BROADBAND_MODEM (ctx->self));
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static gboolean
+modem_after_power_up_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+modem_after_power_up (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ AfterPowerUpContext *ctx;
+
+ ctx = g_slice_new0 (AfterPowerUpContext);
+ ctx->self = g_object_ref (MM_BROADBAND_MODEM_TELIT (self));
+ ctx->step = AFTER_POWER_UP_STEP_FIRST;
+ ctx->has_sim_changed = FALSE;
+ ctx->retries = 3;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) after_power_up_context_free);
+
+ after_power_up_step (task);
+}
+
+
+/*****************************************************************************/
/* Modem power down (Modem interface) */
+static void
+telit_modem_power_down_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ mm_base_modem_at_command_finish (self, res, &error);
+ /* By default, errors in the power up/down command are ignored in parent broadband modem.
+ * The Telit plugin keeps the same behavior, but ignore next QSS only when the command
+ * did not fail. */
+ if (!error) {
+ mm_dbg ("Ignore #QSS unsolicited during power down/low");
+ MM_BROADBAND_MODEM_TELIT (self)->priv->parse_qss = FALSE;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
static gboolean
modem_power_down_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
@@ -918,12 +1103,15 @@ modem_power_down (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CFUN=4",
20,
FALSE,
- callback,
- user_data);
+ (GAsyncReadyCallback) telit_modem_power_down_ready,
+ task);
}
/*****************************************************************************/
@@ -1457,6 +1645,7 @@ mm_broadband_modem_telit_init (MMBroadbandModemTelit *self)
self->priv->csim_lock_support = FEATURE_SUPPORT_UNKNOWN;
self->priv->csim_lock_state = CSIM_LOCK_STATE_UNKNOWN;
self->priv->qss_status = QSS_STATUS_UNKNOWN;
+ self->priv->parse_qss = TRUE;
}
static void
@@ -1474,6 +1663,9 @@ iface_modem_init (MMIfaceModem *iface)
iface->load_unlock_retries = modem_load_unlock_retries;
iface->reset = modem_reset;
iface->reset_finish = modem_reset_finish;
+ iface->modem_power_up = modem_power_up;
+ iface->modem_after_power_up = modem_after_power_up;
+ iface->modem_after_power_up_finish = modem_after_power_up_finish;
iface->modem_power_down = modem_power_down;
iface->modem_power_down_finish = modem_power_down_finish;
iface->load_access_technologies = load_access_technologies;
--
2.9.3
More information about the ModemManager-devel
mailing list