diff options
-rw-r--r-- | src/mm-broadband-modem-mbim.c | 4 | ||||
-rw-r--r-- | src/mm-broadband-modem-qmi.c | 2 | ||||
-rw-r--r-- | src/mm-shared-qmi.c | 312 | ||||
-rw-r--r-- | src/mm-shared-qmi.h | 9 |
4 files changed, 326 insertions, 1 deletions
diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c index d3800666..f77e9af5 100644 --- a/src/mm-broadband-modem-mbim.c +++ b/src/mm-broadband-modem-mbim.c @@ -5476,6 +5476,10 @@ iface_modem_init (MMIfaceModem *iface) /* Create MBIM-specific SIM */ iface->create_sim = create_sim; iface->create_sim_finish = create_sim_finish; +#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED + iface->load_sim_slots = mm_shared_qmi_load_sim_slots; + iface->load_sim_slots_finish = mm_shared_qmi_load_sim_slots_finish; +#endif /* Create MBIM-specific bearer */ iface->create_bearer = modem_create_bearer; diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c index caa30d5b..69a72075 100644 --- a/src/mm-broadband-modem-qmi.c +++ b/src/mm-broadband-modem-qmi.c @@ -9273,6 +9273,8 @@ iface_modem_init (MMIfaceModem *iface) /* Create QMI-specific SIM */ iface->create_sim = create_sim; iface->create_sim_finish = create_sim_finish; + iface->load_sim_slots = mm_shared_qmi_load_sim_slots; + iface->load_sim_slots_finish = mm_shared_qmi_load_sim_slots_finish; /* Create QMI-specific bearer */ iface->create_bearer = modem_create_bearer; diff --git a/src/mm-shared-qmi.c b/src/mm-shared-qmi.c index fb4943f7..7f312b95 100644 --- a/src/mm-shared-qmi.c +++ b/src/mm-shared-qmi.c @@ -29,6 +29,7 @@ #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-location.h" +#include "mm-sim-qmi.h" #include "mm-shared-qmi.h" #include "mm-modem-helpers-qmi.h" @@ -3163,6 +3164,317 @@ mm_shared_qmi_load_carrier_config (MMIfaceModem *self, } /*****************************************************************************/ +/* Load SIM slots (modem interface) */ + +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; +} LoadSimSlotsContext; + +static void +load_sim_slots_context_free (LoadSimSlotsContext *ctx) +{ + 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); +} + +static void +sim_slot_free (MMBaseSim *sim) +{ + if (sim) + g_object_unref (sim); +} + +gboolean +mm_shared_qmi_load_sim_slots_finish (MMIfaceModem *self, + GAsyncResult *res, + GPtrArray **sim_slots, + guint *primary_sim_slot, + GError **error) +{ + LoadSimSlotsContext *ctx; + + if (!g_task_propagate_boolean (G_TASK (res), error)) + return FALSE; + + ctx = g_task_get_task_data (G_TASK (res)); + if (sim_slots) + *sim_slots = g_steal_pointer (&ctx->sim_slots); + if (primary_sim_slot) + *primary_sim_slot = ctx->active_slot_number; + return TRUE; +} + +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); + g_task_return_error (task, g_steal_pointer (&error)); + } else + g_task_return_boolean (task, TRUE); + g_object_unref (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) { + g_task_return_boolean (task, TRUE); + g_object_unref (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)) { + 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 +uim_get_slot_status_ready (QmiClientUim *client, + GAsyncResult *res, + GTask *task) +{ + g_autoptr(QmiMessageUimGetSlotStatusOutput) output = NULL; + LoadSimSlotsContext *ctx; + MMIfaceModem *self; + GError *error = NULL; + GArray *physical_slots = NULL; + guint i; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + output = qmi_client_uim_get_slot_status_finish (client, res, &error); + if (!output || + !qmi_message_uim_get_slot_status_output_get_result (output, &error) || + !qmi_message_uim_get_slot_status_output_get_physical_slot_status (output, &physical_slots, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + ctx->sim_slots = g_ptr_array_new_full (physical_slots->len, (GDestroyNotify) sim_slot_free); + + for (i = 0; i < physical_slots->len; i++) { + QmiPhysicalSlotStatusSlot *slot_status; + MMBaseSim *sim; + g_autofree gchar *raw_iccid = NULL; + g_autofree gchar *iccid = NULL; + g_autoptr(GError) inner_error = NULL; + gboolean sim_active = FALSE; + + /* Store active slot info */ + slot_status = &g_array_index (physical_slots, QmiPhysicalSlotStatusSlot, i); + if (slot_status->physical_slot_status == QMI_UIM_SLOT_STATE_ACTIVE) { + 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) { + mm_obj_dbg (self, "not creating SIM object: no SIM in slot %u", i + 1); + g_ptr_array_add (ctx->sim_slots, NULL); + continue; + } + + raw_iccid = mm_bcd_to_string ((const guint8 *)slot_status->iccid->data, slot_status->iccid->len); + if (!raw_iccid) { + mm_obj_warn (self, "not creating SIM object: failed to convert ICCID from BCD"); + g_ptr_array_add (ctx->sim_slots, NULL); + continue; + } + + iccid = mm_3gpp_parse_iccid (raw_iccid, &inner_error); + if (!iccid) { + mm_obj_warn (self, "not creating SIM object: couldn't parse SIM iccid: %s", inner_error->message); + g_ptr_array_add (ctx->sim_slots, NULL); + continue; + } + + sim = mm_sim_qmi_new_initialized (MM_BASE_MODEM (self), + TRUE, /* consider DMS UIM deprecated if we're creating SIM slots */ + i + 1, /* slot number is the array index starting at 1 */ + sim_active, + iccid, + NULL, /* imsi unknown */ + NULL, /* operator id unknown */ + 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); + + /* 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); +} + +void +mm_shared_qmi_load_sim_slots (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + LoadSimSlotsContext *ctx; + GTask *task; + QmiClient *client = NULL; + + if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), + QMI_SERVICE_UIM, &client, + callback, user_data)) + return; + + task = g_task_new (self, NULL, callback, user_data); + + ctx = g_slice_new0 (LoadSimSlotsContext); + ctx->client_uim = g_object_ref (client); + g_task_set_task_data (task, ctx, (GDestroyNotify) load_sim_slots_context_free); + + qmi_client_uim_get_slot_status (ctx->client_uim, + NULL, + 10, + NULL, + (GAsyncReadyCallback) uim_get_slot_status_ready, + task); +} + +/*****************************************************************************/ /* Location: Set SUPL server */ typedef struct { diff --git a/src/mm-shared-qmi.h b/src/mm-shared-qmi.h index 093896e0..4a9567b8 100644 --- a/src/mm-shared-qmi.h +++ b/src/mm-shared-qmi.h @@ -163,7 +163,14 @@ void mm_shared_qmi_setup_carrier_config (MMIfaceMode gboolean mm_shared_qmi_setup_carrier_config_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); - +void mm_shared_qmi_load_sim_slots (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_shared_qmi_load_sim_slots_finish (MMIfaceModem *self, + GAsyncResult *res, + GPtrArray **sim_slots, + guint *primary_sim_slot, + GError **error); /* Shared QMI location support */ void mm_shared_qmi_location_load_capabilities (MMIfaceModemLocation *self, |