diff options
author | Pavan Holla <pholla@chromium.org> | 2021-04-12 15:58:09 +0000 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2021-04-12 21:03:57 +0000 |
commit | b978a7045fe26f31f88acb9a1a2300cbcdfcac78 (patch) | |
tree | 6a65625a1e9d46c7ff197dd5b1a470a9aaf5d50f | |
parent | b766ded3ca58cee202bd4567dd5530f04dc2290d (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.c | 340 |
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 |