aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavan Holla <pholla@chromium.org>2021-04-12 15:58:09 +0000
committerAleksander Morgado <aleksander@aleksander.es>2021-04-12 21:03:57 +0000
commitb978a7045fe26f31f88acb9a1a2300cbcdfcac78 (patch)
tree6a65625a1e9d46c7ff197dd5b1a470a9aaf5d50f
parentb766ded3ca58cee202bd4567dd5530f04dc2290d (diff)
shared-qmi: Remove iteration over slots at startup
MM used to iterate over each SIM slot to load IMSI, operator name etc. However, switching slots at start up caused a few issues in the real world. a) Sierra Wireless' AUTO-SIM feature, the hidden SIM slot switching is actually triggering a full modem reset (because the firmware detects a new SIM for a different operator, and automatically resets itself to load the firmware+carrier config associated to that other operator). b) The IMSI on slot 2 is reported to be the same as slot 1. This is solved by adding a 1000ms delay before reading IMSI on slot 2, after GET_CARD_STATUS indicates the sim is ready. The delay means a 2s startup penalty when both slots are loaded. c) 2/5 developers have encountered a fw bug where the modem incorrectly reports that the SIM on slot 1 has vanished after the sequence of slot switches. This makes the modem unusable. d) If the eSIM daemon changes slots and opens a channel to the eUICC, MM will detect a hotswap, and break the channel during reprobe. e) SIMs from a certain large operator in the US take over 20s to be ready. Thus, switching to such a SIM for just loading IMSI may not be worth it. This change does not affect detection of sims in all slots. We still expose the iccid and presence of all sims. However, properties like IMSI and operator_name will not be exposed for the inactive slot.
-rw-r--r--src/mm-shared-qmi.c340
1 files changed, 2 insertions, 338 deletions
diff --git a/src/mm-shared-qmi.c b/src/mm-shared-qmi.c
index 0bd78db1..aa089db5 100644
--- a/src/mm-shared-qmi.c
+++ b/src/mm-shared-qmi.c
@@ -3203,52 +3203,13 @@ mm_shared_qmi_load_carrier_config (MMIfaceModem *self,
typedef struct {
QmiClientUim *client_uim;
GPtrArray *sim_slots;
- GList *sorted_sims;
- MMBaseSim *current_sim;
- guint current_slot_number;
guint active_slot_number;
guint active_logical_id;
- GArray *initial_slot_status;
- GArray *final_slot_status;
- gulong final_slot_status_timeout_id;
- gulong load_sim_slots_indication_id;
} LoadSimSlotsContext;
static void
-clear_load_sim_slot_callbacks (GTask *task)
-{
- MMIfaceModem *self;
- LoadSimSlotsContext *ctx;
- Private *priv;
-
- self = g_task_get_source_object (task);
- ctx = g_task_get_task_data (task);
- priv = get_private (MM_SHARED_QMI (self));
-
- if (ctx->client_uim && ctx->load_sim_slots_indication_id) {
- g_signal_handler_disconnect(ctx->client_uim, ctx->load_sim_slots_indication_id);
- ctx->load_sim_slots_indication_id = 0;
- }
- if (ctx->final_slot_status_timeout_id) {
- g_source_remove (ctx->final_slot_status_timeout_id);
- ctx->final_slot_status_timeout_id = 0;
- }
- /* Restore hot swap detection because we aren't loading slots anymore */
- if (priv->uim_slot_status_indication_id)
- g_signal_handler_unblock (ctx->client_uim, priv->uim_slot_status_indication_id);
-}
-
-static void
load_sim_slots_context_free (LoadSimSlotsContext *ctx)
{
- if (ctx->initial_slot_status)
- g_array_unref (ctx->initial_slot_status);
- if (ctx->final_slot_status)
- g_array_unref (ctx->final_slot_status);
- g_assert (ctx->load_sim_slots_indication_id == 0);
- g_assert (ctx->final_slot_status_timeout_id == 0);
- g_clear_object (&ctx->current_sim);
- g_list_free_full (ctx->sorted_sims, (GDestroyNotify)g_object_unref);
g_clear_pointer (&ctx->sim_slots, g_ptr_array_unref);
g_clear_object (&ctx->client_uim);
g_slice_free (LoadSimSlotsContext, ctx);
@@ -3282,278 +3243,6 @@ mm_shared_qmi_load_sim_slots_finish (MMIfaceModem *self,
return TRUE;
}
-/* Compares arrays of QmiPhysicalSlotStatusSlot */
-static gboolean
-compare_slot_status (GArray *slot_status1,
- GArray *slot_status2)
-{
- guint i;
- guint j;
-
- if (!slot_status1 && !slot_status2)
- return TRUE;
- if (!slot_status1 || !slot_status2 || slot_status1->len != slot_status2->len)
- return FALSE;
- for (i = 0; i < slot_status1->len; i++) {
- /* Compare slot at index i from slot_status1 and slot_status2 */
- QmiPhysicalSlotStatusSlot *slot_a;
- QmiPhysicalSlotStatusSlot *slot_b;
-
- slot_a = &g_array_index (slot_status1, QmiPhysicalSlotStatusSlot, i);
- slot_b = &g_array_index (slot_status2, QmiPhysicalSlotStatusSlot, i);
- if (slot_a->physical_card_status != slot_b->physical_card_status)
- return FALSE;
- if (slot_a->physical_slot_status != slot_b->physical_slot_status)
- return FALSE;
- if (slot_a->iccid->len != slot_b->iccid->len)
- return FALSE;
-
- for (j = 0; j < slot_a->iccid->len; j++) {
- if (g_array_index (slot_a->iccid, guint8, j) != g_array_index (slot_b->iccid, guint8, j))
- return FALSE;
- }
- }
- return TRUE;
-}
-
-static gboolean
-hotswap_while_loading_slots (GTask *task)
-{
- MMIfaceModem *self;
-
- self = g_task_get_source_object (task);
-
- mm_obj_dbg (self,
- "Slot status before loading sim slots is different from slot status after loading sim slots, "
- "assuming hotswap");
- clear_load_sim_slot_callbacks (task);
- g_task_return_new_error (task,
- MM_CORE_ERROR,
- MM_CORE_ERROR_ABORTED,
- "Timed out waiting for final slot status to match initial slot status");
- g_object_unref (task);
-
- mm_base_modem_process_sim_event (MM_BASE_MODEM (self));
- return G_SOURCE_REMOVE;
-}
-
-#define FINAL_SLOT_STATUS_TIMEOUT 3
-
-static void
-check_final_slot_status (GTask *task)
-{
- LoadSimSlotsContext *ctx;
- MMIfaceModem *self;
-
- self = g_task_get_source_object (task);
- ctx = g_task_get_task_data (task);
-
- /* We may never receive a slot status indication, which means that
- * no slot switch was performed while loading sim slots */
- if (!ctx->final_slot_status || compare_slot_status (ctx->initial_slot_status, ctx->final_slot_status)) {
- mm_obj_dbg (self, "Final slot status matches initial slot status");
- clear_load_sim_slot_callbacks (task);
- g_task_return_boolean (task, TRUE);
- g_object_unref (task);
- return;
- }
-
- if (!ctx->final_slot_status_timeout_id) {
- /* Setup a timeout before which final and initial slot status should match */
- mm_obj_dbg (self,
- "Final slot status does not match initial slot status. "
- "Waiting for final slot status indication...");
- ctx->final_slot_status_timeout_id = g_timeout_add_seconds (FINAL_SLOT_STATUS_TIMEOUT,
- (GSourceFunc) hotswap_while_loading_slots,
- task);
- }
-}
-
-static void
-active_slot_switch_ready (QmiClientUim *client,
- GAsyncResult *res,
- GTask *task)
-{
- g_autoptr(QmiMessageUimSwitchSlotOutput) output = NULL;
- g_autoptr(GError) error = NULL;
- MMIfaceModem *self;
- LoadSimSlotsContext *ctx;
-
- self = g_task_get_source_object (task);
- ctx = g_task_get_task_data (task);
-
- output = qmi_client_uim_switch_slot_finish (client, res, &error);
- if ((!output || !qmi_message_uim_switch_slot_output_get_result (output, &error)) &&
- !g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) {
- mm_obj_err (self, "couldn't switch to original slot %u", ctx->active_slot_number);
- clear_load_sim_slot_callbacks (task);
- g_task_return_error (task, g_steal_pointer (&error));
- g_object_unref (task);
- return;
- }
-
- check_final_slot_status (task);
-}
-
-static void
-reload_active_slot (GTask *task)
-{
- g_autoptr(QmiMessageUimSwitchSlotInput) input = NULL;
- LoadSimSlotsContext *ctx;
- MMIfaceModem *self;
-
- self = g_task_get_source_object (task);
- ctx = g_task_get_task_data (task);
-
- /* If we're already in the original active SIM slot, nothing else to do */
- if (ctx->current_slot_number == ctx->active_slot_number) {
- mm_obj_dbg (self,
- "Already on the original active SIM at slot %u. "
- "No more slot switches necessary", ctx->active_slot_number);
- check_final_slot_status (task);
- return;
- }
-
- mm_obj_dbg (self, "switching to original active SIM at slot %u", ctx->active_slot_number);
-
- /* Switch to the original active slot */
- input = qmi_message_uim_switch_slot_input_new ();
- qmi_message_uim_switch_slot_input_set_logical_slot (input, (guint8) ctx->active_logical_id, NULL);
- qmi_message_uim_switch_slot_input_set_physical_slot (input, ctx->active_slot_number, NULL);
- qmi_client_uim_switch_slot (ctx->client_uim,
- input,
- 10,
- NULL,
- (GAsyncReadyCallback) active_slot_switch_ready,
- task);
-}
-
-static void load_next_sim_info (GTask *task);
-
-static void
-next_sim_initialize_ready (MMBaseSim *sim,
- GAsyncResult *res,
- GTask *task)
-{
- g_autoptr(GError) error = NULL;
- MMIfaceModem *self;
- LoadSimSlotsContext *ctx;
-
- self = g_task_get_source_object (task);
- ctx = g_task_get_task_data (task);
-
- if (!mm_base_sim_initialize_finish (sim, res, &error))
- mm_obj_dbg (self, "couldn't initialize SIM at slot %u: won't load additional info",
- ctx->current_slot_number);
- else
- mm_obj_dbg (self, "initialized SIM at slot %u",
- ctx->current_slot_number);
-
- /* Iterate to next SIM */
- load_next_sim_info (task);
-}
-
-static void
-next_sim_switch_ready (QmiClientUim *client,
- GAsyncResult *res,
- GTask *task)
-{
- g_autoptr(QmiMessageUimSwitchSlotOutput) output = NULL;
- g_autoptr(GError) error = NULL;
- MMIfaceModem *self;
- LoadSimSlotsContext *ctx;
-
- self = g_task_get_source_object (task);
- ctx = g_task_get_task_data (task);
-
- output = qmi_client_uim_switch_slot_finish (client, res, &error);
- if (!output || !qmi_message_uim_switch_slot_output_get_result (output, &error)) {
- /* ignore NoEffect errors on slot switch, because that indicates we're
- * already in the desired slot */
- if (!g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) {
- mm_obj_dbg (self, "couldn't switch to SIM at slot %u: won't load additional info",
- ctx->current_slot_number);
- load_next_sim_info (task);
- return;
- }
- }
-
- mm_obj_dbg (self, "switched to SIM at slot %u: initializing...",
- ctx->current_slot_number);
-
- mm_base_sim_initialize (ctx->current_sim,
- NULL,
- (GAsyncReadyCallback) next_sim_initialize_ready,
- task);
-}
-
-static void
-load_next_sim_info (GTask *task)
-{
- g_autoptr(QmiMessageUimSwitchSlotInput) input = NULL;
- LoadSimSlotsContext *ctx;
- MMIfaceModem *self;
-
- self = g_task_get_source_object (task);
- ctx = g_task_get_task_data (task);
-
- /* All done? */
- if (!ctx->sorted_sims) {
- mm_obj_dbg (self, "no more SIMs to load info from");
- reload_active_slot (task);
- return;
- }
-
- /* Steal SIM from list */
- g_clear_object (&ctx->current_sim);
- ctx->current_sim = MM_BASE_SIM (ctx->sorted_sims->data);
- ctx->sorted_sims = g_list_delete_link (ctx->sorted_sims, ctx->sorted_sims);
- ctx->current_slot_number = mm_base_sim_get_slot_number (ctx->current_sim);
-
- mm_obj_dbg (self, "switching to SIM at slot %u: %s",
- ctx->current_slot_number, mm_base_sim_get_path (ctx->current_sim));
-
- /* Switch to the next slot */
- input = qmi_message_uim_switch_slot_input_new ();
- qmi_message_uim_switch_slot_input_set_logical_slot (input, (guint8) ctx->active_logical_id, NULL);
- qmi_message_uim_switch_slot_input_set_physical_slot (input, ctx->current_slot_number, NULL);
- qmi_client_uim_switch_slot (ctx->client_uim,
- input,
- 10,
- NULL,
- (GAsyncReadyCallback) next_sim_switch_ready,
- task);
-}
-
-static void
-load_sim_slots_indication_cb (QmiClientUim *client,
- QmiIndicationUimSlotStatusOutput *output,
- GTask *task)
-{
- g_autoptr(GError) error = NULL;
- LoadSimSlotsContext *ctx;
- MMIfaceModem *self;
- GArray *physical_slots = NULL;
-
- self = g_task_get_source_object (task);
- ctx = g_task_get_task_data (task);
-
- mm_obj_dbg (self, "received slot status indication while loading slots");
- if (!qmi_indication_uim_slot_status_output_get_physical_slot_status (output,
- &physical_slots,
- &error)) {
- mm_obj_warn (self, "could not process slot status indication: %s", error->message);
- return;
- }
-
- g_clear_object (&ctx->final_slot_status);
- ctx->final_slot_status = g_array_ref (physical_slots);
-
- /* We are awaiting the final slot status before we finish the load_sim_slots task */
- if (ctx->final_slot_status_timeout_id)
- check_final_slot_status (task);
-}
-
static void
uim_get_slot_status_ready (QmiClientUim *client,
GAsyncResult *res,
@@ -3562,7 +3251,6 @@ uim_get_slot_status_ready (QmiClientUim *client,
g_autoptr(QmiMessageUimGetSlotStatusOutput) output = NULL;
LoadSimSlotsContext *ctx;
MMIfaceModem *self;
- Private *priv;
GError *error = NULL;
GArray *physical_slots = NULL;
GArray *ext_information = NULL;
@@ -3571,7 +3259,6 @@ uim_get_slot_status_ready (QmiClientUim *client,
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
- priv = get_private (MM_SHARED_QMI (self));
output = qmi_client_uim_get_slot_status_finish (client, res, &error);
if (!output ||
@@ -3582,11 +3269,6 @@ uim_get_slot_status_ready (QmiClientUim *client,
return;
}
- /* Store the slot status before loading all sim slots.
- * We know we are done loading sim slots when the final slot status
- * indication is the same as initial slot status */
- ctx->initial_slot_status = g_array_ref (physical_slots);
-
/* It's fine if we don't have EID information, but it should be well-formed if present. If it's malformed,
* there is probably a modem firmware bug. */
if (qmi_message_uim_get_slot_status_output_get_physical_slot_information (output, &ext_information, NULL) &&
@@ -3618,7 +3300,6 @@ uim_get_slot_status_ready (QmiClientUim *client,
sim_active = TRUE;
ctx->active_logical_id = slot_status->logical_slot;
ctx->active_slot_number = i + 1;
- ctx->current_slot_number = ctx->active_slot_number;
}
if (!slot_status->iccid->len) {
@@ -3666,28 +3347,11 @@ uim_get_slot_status_ready (QmiClientUim *client,
NULL, /* operator name unknown */
NULL); /* emergency numbers unknown */
g_ptr_array_add (ctx->sim_slots, sim);
-
- if (sim_active)
- ctx->sorted_sims = g_list_append (ctx->sorted_sims, g_object_ref (sim));
- else
- ctx->sorted_sims = g_list_prepend (ctx->sorted_sims, g_object_ref (sim));
}
g_assert_cmpuint (ctx->sim_slots->len, ==, physical_slots->len);
- /* Block hotswap on slot_status_indications since
- * we will be switching slots while loading them. */
- if (priv->uim_slot_status_indication_id)
- g_signal_handler_block (client, priv->uim_slot_status_indication_id);
-
- /* Monitor slot status indications while we are loading slots. Once we are done loading slots,
- * check that no hotswap occurred. */
- ctx->load_sim_slots_indication_id = g_signal_connect (priv->uim_client,
- "slot-status",
- G_CALLBACK (load_sim_slots_indication_cb),
- task);
- /* Now, iterate over all the SIMs, we'll attempt to load info from them by
- * quickly switching over to them, leaving the active SIM to the end */
- load_next_sim_info (task);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
void