aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@aleksander.es>2020-08-01 09:59:54 +0200
committerAleksander Morgado <aleksander@aleksander.es>2020-08-28 14:59:06 +0000
commit7c76450c15555204d14a8ce0550dc2c639d288b3 (patch)
tree7f68b0afa45b0c32d793c2cd114e263d84ae9b19 /src
parent48ca1aea3c12262307c2ce13fbdedb1d91c640c6 (diff)
shared-qmi: implement SIM slots loading during initialization
This commit introduces Multi-SIM Single-Standby support in all QMI capable devices that support multiple SIM slots. The 'UIM Get Slot Status' method is used to list all available physical slots as well as the availability of SIM cards in the different slots. This method also provides UICC already, so the SIM objects are created early and exposed early in DBus. Once all slots are listed, the logic will briefly make each of the available SIM cards active in order to read additional settings like IMSI, MCCMNC or the Operator name. This brief switching is required because in a Single-Standby setup only one SIM can be active at any given time. The last slot to probe is always the one that was active originally, making sure that the modem initialization logic can go on with the correct SIM slot.
Diffstat (limited to 'src')
-rw-r--r--src/mm-broadband-modem-mbim.c4
-rw-r--r--src/mm-broadband-modem-qmi.c2
-rw-r--r--src/mm-shared-qmi.c312
-rw-r--r--src/mm-shared-qmi.h9
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,