telit-plugin: handle QSS unsolicited due to power state transitions
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 actual SIM swaps. To handle this #QSS unsolicited, 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
This commit is contained in:

committed by
Aleksander Morgado

parent
5014cf3976
commit
76916de313
@@ -58,10 +58,12 @@ struct _MMBroadbandModemTelitPrivate {
|
|||||||
MMTelitCsimLockState csim_lock_state;
|
MMTelitCsimLockState csim_lock_state;
|
||||||
GTask *csim_lock_task;
|
GTask *csim_lock_task;
|
||||||
guint csim_lock_timeout_id;
|
guint csim_lock_timeout_id;
|
||||||
|
gboolean parse_qss;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* After Sim Unlock (Modem interface) */
|
/* After Sim Unlock (Modem interface) */
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
modem_after_sim_unlock_finish (MMIfaceModem *self,
|
modem_after_sim_unlock_finish (MMIfaceModem *self,
|
||||||
GAsyncResult *res,
|
GAsyncResult *res,
|
||||||
@@ -151,10 +153,15 @@ telit_qss_unsolicited_handler (MMPortSerialAt *port,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cur_qss_status != prev_qss_status)
|
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 (prev_qss_status),
|
||||||
mm_telit_qss_status_get_string (cur_qss_status));
|
mm_telit_qss_status_get_string (cur_qss_status));
|
||||||
|
|
||||||
|
if (self->priv->parse_qss == FALSE) {
|
||||||
|
mm_dbg ("QSS: message ignored");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((prev_qss_status == QSS_STATUS_SIM_REMOVED && cur_qss_status != QSS_STATUS_SIM_REMOVED) ||
|
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)) {
|
(prev_qss_status > QSS_STATUS_SIM_REMOVED && cur_qss_status == QSS_STATUS_SIM_REMOVED)) {
|
||||||
mm_info ("QSS handler: SIM swap detected");
|
mm_info ("QSS handler: SIM swap detected");
|
||||||
@@ -902,15 +909,175 @@ modem_load_unlock_retries (MMIfaceModem *self,
|
|||||||
load_unlock_retries_step (task);
|
load_unlock_retries_step (task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* 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 {
|
||||||
|
gboolean has_sim_changed;
|
||||||
|
guint retries;
|
||||||
|
ModemAfterPowerUpStep step;
|
||||||
|
} AfterPowerUpContext;
|
||||||
|
|
||||||
|
static void after_power_up_step (GTask *task);
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
modem_after_power_up_finish (MMIfaceModem *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return g_task_propagate_boolean (G_TASK (res), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
load_sim_identifier_ready (MMBaseSim *sim,
|
||||||
|
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 (sim));
|
||||||
|
current_simid = mm_base_sim_load_sim_identifier_finish (sim, res, &error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
|
||||||
|
mm_warn ("could not load SIM identifier: %s", error->message);
|
||||||
|
g_clear_error (&error);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->retries-- > 0) {
|
||||||
|
mm_warn ("could not load 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 ("could not load 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);
|
||||||
|
ctx->step++;
|
||||||
|
after_power_up_step (task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
after_power_up_step (GTask *task)
|
||||||
|
{
|
||||||
|
AfterPowerUpContext *ctx;
|
||||||
|
MMBroadbandModemTelit *self;
|
||||||
|
MMBaseSim *sim = NULL;
|
||||||
|
|
||||||
|
ctx = g_task_get_task_data (task);
|
||||||
|
self = g_task_get_source_object (task);
|
||||||
|
|
||||||
|
g_object_get (MM_BROADBAND_MODEM_TELIT (self),
|
||||||
|
MM_IFACE_MODEM_SIM, &sim,
|
||||||
|
NULL);
|
||||||
|
if (!sim) {
|
||||||
|
g_task_return_new_error (task,
|
||||||
|
MM_CORE_ERROR,
|
||||||
|
MM_CORE_ERROR_FAILED,
|
||||||
|
"could not acquire sim 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_load_sim_identifier (sim,
|
||||||
|
(GAsyncReadyCallback)load_sim_identifier_ready,
|
||||||
|
task);
|
||||||
|
g_object_unref (sim);
|
||||||
|
return;
|
||||||
|
case AFTER_POWER_UP_STEP_ENABLE_QSS_PARSING:
|
||||||
|
mm_dbg ("Stop ignoring #QSS");
|
||||||
|
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 (self));
|
||||||
|
|
||||||
|
g_task_return_boolean (task, TRUE);
|
||||||
|
g_object_unref (task);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
modem_after_power_up (MMIfaceModem *self,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GTask *task;
|
||||||
|
AfterPowerUpContext *ctx;
|
||||||
|
|
||||||
|
ctx = g_new0 (AfterPowerUpContext, 1);
|
||||||
|
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) g_free);
|
||||||
|
|
||||||
|
after_power_up_step (task);
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* Modem power down (Modem interface) */
|
/* Modem power down (Modem interface) */
|
||||||
|
|
||||||
|
static void
|
||||||
|
telit_modem_power_down_ready (MMBaseModem *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GTask *task)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
if (mm_base_modem_at_command_finish (self, res, &error)) {
|
||||||
|
mm_dbg ("Ignore #QSS unsolicited during power down/low");
|
||||||
|
MM_BROADBAND_MODEM_TELIT (self)->priv->parse_qss = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
mm_err ("modem power down: %s", error->message);
|
||||||
|
g_clear_error (&error);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_task_return_boolean (task, TRUE);
|
||||||
|
g_object_unref (task);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
modem_power_down_finish (MMIfaceModem *self,
|
modem_power_down_finish (MMIfaceModem *self,
|
||||||
GAsyncResult *res,
|
GAsyncResult *res,
|
||||||
GError **error)
|
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
|
static void
|
||||||
@@ -918,12 +1085,15 @@ modem_power_down (MMIfaceModem *self,
|
|||||||
GAsyncReadyCallback callback,
|
GAsyncReadyCallback callback,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
|
GTask *task;
|
||||||
|
|
||||||
|
task = g_task_new (self, NULL, callback, user_data);
|
||||||
mm_base_modem_at_command (MM_BASE_MODEM (self),
|
mm_base_modem_at_command (MM_BASE_MODEM (self),
|
||||||
"+CFUN=4",
|
"+CFUN=4",
|
||||||
20,
|
20,
|
||||||
FALSE,
|
FALSE,
|
||||||
callback,
|
(GAsyncReadyCallback) telit_modem_power_down_ready,
|
||||||
user_data);
|
task);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@@ -1457,6 +1627,7 @@ mm_broadband_modem_telit_init (MMBroadbandModemTelit *self)
|
|||||||
self->priv->csim_lock_support = FEATURE_SUPPORT_UNKNOWN;
|
self->priv->csim_lock_support = FEATURE_SUPPORT_UNKNOWN;
|
||||||
self->priv->csim_lock_state = CSIM_LOCK_STATE_UNKNOWN;
|
self->priv->csim_lock_state = CSIM_LOCK_STATE_UNKNOWN;
|
||||||
self->priv->qss_status = QSS_STATUS_UNKNOWN;
|
self->priv->qss_status = QSS_STATUS_UNKNOWN;
|
||||||
|
self->priv->parse_qss = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -1474,6 +1645,8 @@ iface_modem_init (MMIfaceModem *iface)
|
|||||||
iface->load_unlock_retries = modem_load_unlock_retries;
|
iface->load_unlock_retries = modem_load_unlock_retries;
|
||||||
iface->reset = modem_reset;
|
iface->reset = modem_reset;
|
||||||
iface->reset_finish = modem_reset_finish;
|
iface->reset_finish = modem_reset_finish;
|
||||||
|
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 = modem_power_down;
|
||||||
iface->modem_power_down_finish = modem_power_down_finish;
|
iface->modem_power_down_finish = modem_power_down_finish;
|
||||||
iface->load_access_technologies = load_access_technologies;
|
iface->load_access_technologies = load_access_technologies;
|
||||||
|
@@ -700,6 +700,59 @@ mm_base_sim_send_puk (MMBaseSim *self,
|
|||||||
task);
|
task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* LOAD SIM IDENTIFIER */
|
||||||
|
|
||||||
|
gchar *
|
||||||
|
mm_base_sim_load_sim_identifier_finish (MMBaseSim *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GError *inner_error = NULL;
|
||||||
|
gchar *simid;
|
||||||
|
|
||||||
|
if (g_async_result_is_tagged (res, mm_base_sim_load_sim_identifier) ||
|
||||||
|
!MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_finish) {
|
||||||
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
||||||
|
"not implemented");
|
||||||
|
g_propagate_error (error, inner_error);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
simid = MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_finish (self, res, &inner_error);
|
||||||
|
if (inner_error) {
|
||||||
|
g_propagate_error (error, inner_error);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return simid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mm_base_sim_load_sim_identifier (MMBaseSim *self,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
if (!MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier &&
|
||||||
|
!MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_finish) {
|
||||||
|
g_task_report_new_error (self,
|
||||||
|
callback,
|
||||||
|
user_data,
|
||||||
|
mm_base_sim_load_sim_identifier,
|
||||||
|
MM_CORE_ERROR,
|
||||||
|
MM_CORE_ERROR_UNSUPPORTED,
|
||||||
|
"not implemented");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier (
|
||||||
|
self,
|
||||||
|
(GAsyncReadyCallback)callback,
|
||||||
|
user_data);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* SEND PIN (DBus call handling) */
|
/* SEND PIN (DBus call handling) */
|
||||||
|
|
||||||
|
@@ -129,40 +129,47 @@ struct _MMBaseSimClass {
|
|||||||
|
|
||||||
GType mm_base_sim_get_type (void);
|
GType mm_base_sim_get_type (void);
|
||||||
|
|
||||||
void mm_base_sim_new (MMBaseModem *modem,
|
void mm_base_sim_new (MMBaseModem *modem,
|
||||||
GCancellable *cancellable,
|
GCancellable *cancellable,
|
||||||
GAsyncReadyCallback callback,
|
GAsyncReadyCallback callback,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
MMBaseSim *mm_base_sim_new_finish (GAsyncResult *res,
|
MMBaseSim *mm_base_sim_new_finish (GAsyncResult *res,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
void mm_base_sim_initialize (MMBaseSim *self,
|
void mm_base_sim_initialize (MMBaseSim *self,
|
||||||
GCancellable *cancellable,
|
GCancellable *cancellable,
|
||||||
GAsyncReadyCallback callback,
|
GAsyncReadyCallback callback,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
gboolean mm_base_sim_initialize_finish (MMBaseSim *self,
|
gboolean mm_base_sim_initialize_finish (MMBaseSim *self,
|
||||||
GAsyncResult *result,
|
GAsyncResult *result,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
void mm_base_sim_send_pin (MMBaseSim *self,
|
void mm_base_sim_send_pin (MMBaseSim *self,
|
||||||
const gchar *pin,
|
const gchar *pin,
|
||||||
GAsyncReadyCallback callback,
|
GAsyncReadyCallback callback,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
gboolean mm_base_sim_send_pin_finish (MMBaseSim *self,
|
gboolean mm_base_sim_send_pin_finish (MMBaseSim *self,
|
||||||
GAsyncResult *res,
|
GAsyncResult *res,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
void mm_base_sim_send_puk (MMBaseSim *self,
|
void mm_base_sim_send_puk (MMBaseSim *self,
|
||||||
const gchar *puk,
|
const gchar *puk,
|
||||||
const gchar *new_pin,
|
const gchar *new_pin,
|
||||||
GAsyncReadyCallback callback,
|
GAsyncReadyCallback callback,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
gboolean mm_base_sim_send_puk_finish (MMBaseSim *self,
|
gboolean mm_base_sim_send_puk_finish (MMBaseSim *self,
|
||||||
GAsyncResult *res,
|
GAsyncResult *res,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
void mm_base_sim_export (MMBaseSim *self);
|
void mm_base_sim_export (MMBaseSim *self);
|
||||||
|
|
||||||
const gchar *mm_base_sim_get_path (MMBaseSim *sim);
|
const gchar *mm_base_sim_get_path (MMBaseSim *sim);
|
||||||
|
|
||||||
|
void mm_base_sim_load_sim_identifier (MMBaseSim *self,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gchar *mm_base_sim_load_sim_identifier_finish (MMBaseSim *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
#endif /* MM_BASE_SIM_H */
|
#endif /* MM_BASE_SIM_H */
|
||||||
|
Reference in New Issue
Block a user