diff options
author | Lukas Voegl <lvoegl@tdt.de> | 2023-03-10 13:54:27 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2023-10-05 11:36:08 +0000 |
commit | 1341c65b5c9ebd8d3a2e21169e0a7f0cfe7a8972 (patch) | |
tree | 8758d0794113d4a9da9d7e1c009b0101eb89bdb1 | |
parent | 9da04f6158cd64a92a41e3c9795f3e94afc26600 (diff) |
cinterion: implement loading multiple SIMs
Signed-off-by: Lukas Voegl <lvoegl@tdt.de>
-rw-r--r-- | src/plugins/cinterion/mm-broadband-modem-cinterion.c | 215 | ||||
-rw-r--r-- | src/plugins/cinterion/mm-modem-helpers-cinterion.c | 114 | ||||
-rw-r--r-- | src/plugins/cinterion/mm-modem-helpers-cinterion.h | 21 |
3 files changed, 350 insertions, 0 deletions
diff --git a/src/plugins/cinterion/mm-broadband-modem-cinterion.c b/src/plugins/cinterion/mm-broadband-modem-cinterion.c index 9612b23e..8573109d 100644 --- a/src/plugins/cinterion/mm-broadband-modem-cinterion.c +++ b/src/plugins/cinterion/mm-broadband-modem-cinterion.c @@ -96,6 +96,8 @@ struct _MMBroadbandModemCinterionPrivate { GRegex *sysstart_regex; /* +CIEV indications as configured via AT^SIND */ GRegex *ciev_regex; + /* +CIEV indication for simlocal configured via AT^SIND */ + GRegex *simlocal_regex; /* Ignore SIM hotswap SCKS msg, until ready */ GRegex *scks_regex; @@ -3109,6 +3111,10 @@ setup_ports (MMBroadbandModem *_self) ports[i], self->priv->scks_regex, NULL, NULL, NULL); + mm_port_serial_at_add_unsolicited_msg_handler ( + ports[i], + self->priv->simlocal_regex, + NULL, NULL, NULL); } } @@ -3158,6 +3164,8 @@ mm_broadband_modem_cinterion_init (MMBroadbandModemCinterion *self) G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->scks_regex = g_regex_new ("\\^SCKS:\\s*([0-3])\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + self->priv->simlocal_regex = g_regex_new ("\\r\\n\\+CIEV:\\s*simlocal,((\\d,)*\\d)\\r\\n", + G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->any_allowed = MM_MODEM_MODE_NONE; } @@ -3187,10 +3195,215 @@ finalize (GObject *object) g_regex_unref (self->priv->ciev_regex); g_regex_unref (self->priv->sysstart_regex); g_regex_unref (self->priv->scks_regex); + g_regex_unref (self->priv->simlocal_regex); G_OBJECT_CLASS (mm_broadband_modem_cinterion_parent_class)->finalize (object); } +/*****************************************************************************/ +/* Load SIM slots (modem interface) */ + +typedef struct { + GPtrArray *sim_slots; + guint number_slots; + guint active_slot_index; /* range [1,number_slots] */ +} LoadSimSlotsContext; + +static void +load_sim_slots_context_free (LoadSimSlotsContext *ctx) +{ + g_clear_pointer (&ctx->sim_slots, g_ptr_array_unref); + g_slice_free (LoadSimSlotsContext, ctx); +} + +static void +sim_slot_free (MMBaseSim *sim) +{ + if (sim) + g_object_unref (sim); +} + +static gboolean +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_index; + + return TRUE; +} + +static void +scfg_query_ready (MMBaseModem *_self, + GAsyncResult *res, + GTask *task) +{ + MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); + LoadSimSlotsContext *ctx; + MMBaseSim *active_sim; + const gchar *response; + GError *error = NULL; + guint active_slot; + + ctx = g_task_get_task_data (task); + response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (!response || + !mm_cinterion_parse_scfg_sim_response (response, + &active_slot, + &error)) { + g_task_return_error (task, error); + return; + } + + mm_obj_info (self, "active SIM slot request successful"); + + ctx->active_slot_index = active_slot; + active_sim = g_ptr_array_index (ctx->sim_slots, active_slot - 1); + if (active_sim != NULL) + g_object_set (G_OBJECT (active_sim), "active", TRUE, NULL); + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void +cinterion_simlocal_unsolicited_handler (MMPortSerialAt *port, + GMatchInfo *match_info, + MMBroadbandModemCinterion *self) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GArray) available = NULL; + g_autoptr(GPtrArray) sim_slots = NULL; + g_autofree gchar *response = NULL; + guint i; + + response = g_match_info_fetch (match_info, 1); + if (!response || !mm_cinterion_get_available_from_simlocal (response, &available, &error)) { + mm_obj_warn (self, "Could not parse list of available SIMs: %s", error->message); + return; + } + + g_object_get (self, + MM_IFACE_MODEM_SIM_SLOTS, &sim_slots, + NULL); + + for (i = 0; i < sim_slots->len; i++) { + MMBaseSim *sim; + gboolean is_available; + + sim = g_ptr_array_index (sim_slots, i); + is_available = g_array_index (available, gboolean, i); + + if (sim == NULL && is_available) { + mm_obj_info (self, "SIM in slot %i inserted", i + 1); + sim = mm_base_sim_new_initialized (MM_BASE_MODEM (self), i + 1, FALSE, + NULL, NULL, NULL, NULL, NULL, NULL); + mm_iface_modem_modify_sim (MM_IFACE_MODEM (self), i, sim); + } else if (sim != NULL && !is_available) { + mm_obj_info (self, "SIM in slot %i removed", i + 1); + mm_iface_modem_modify_sim (MM_IFACE_MODEM (self), i, NULL); + } + } +} + +static void +cinterion_slot_availability_init_ready (MMBaseModem *_self, + GAsyncResult *res, + GTask *task) +{ + MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self); + const gchar *response; + MMPortSerialAt *primary; + MMPortSerialAt *secondary; + LoadSimSlotsContext *ctx; + g_autoptr(GArray) available = NULL; + g_autoptr(GError) error = NULL; + guint i; + + response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (!response || !mm_cinterion_parse_sind_simlocal_response (response, &available, &error)) { + g_prefix_error (&error, "Could not enable simlocal: "); + g_task_return_error (task, g_steal_pointer (&error)); + g_object_unref (task); + return; + } + + primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); + mm_port_serial_at_add_unsolicited_msg_handler ( + primary, + self->priv->simlocal_regex, + (MMPortSerialAtUnsolicitedMsgFn) cinterion_simlocal_unsolicited_handler, + self, + NULL); + + secondary = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); + if (secondary) + mm_port_serial_at_add_unsolicited_msg_handler ( + secondary, + self->priv->simlocal_regex, + (MMPortSerialAtUnsolicitedMsgFn) cinterion_simlocal_unsolicited_handler, + self, + NULL); + + mm_obj_info (self, "SIM availability change with simlocal successfully enabled"); + + ctx = g_task_get_task_data (task); + ctx->number_slots = available->len; + ctx->sim_slots = g_ptr_array_new_full (ctx->number_slots, (GDestroyNotify)sim_slot_free); + + for (i = 0; i < ctx->number_slots; i++) { + MMBaseSim *sim = NULL; + gboolean is_available; + + is_available = g_array_index (available, gboolean, i); + + if (is_available) + sim = mm_base_sim_new_initialized (MM_BASE_MODEM (self), i + 1, FALSE, + NULL, NULL, NULL, NULL, NULL, NULL); + g_ptr_array_add (ctx->sim_slots, sim); + } + + mm_base_modem_at_command (MM_BASE_MODEM (self), + "^SCFG?", + 10, + FALSE, + (GAsyncReadyCallback)scfg_query_ready, + task); +} + +static void +load_sim_slots (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + LoadSimSlotsContext *ctx; + + task = g_task_new (self, NULL, callback, user_data); + ctx = g_slice_new0 (LoadSimSlotsContext); + g_task_set_task_data (task, ctx, (GDestroyNotify)load_sim_slots_context_free); + + mm_base_modem_at_command (MM_BASE_MODEM (self), + "^SIND=\"simlocal\",1", + 3, + FALSE, + (GAsyncReadyCallback)cinterion_slot_availability_init_ready, + task); +} + static void iface_modem_init (MMIfaceModem *iface) { @@ -3225,6 +3438,8 @@ iface_modem_init (MMIfaceModem *iface) iface->setup_sim_hot_swap = modem_setup_sim_hot_swap; iface->setup_sim_hot_swap_finish = modem_setup_sim_hot_swap_finish; iface->cleanup_sim_hot_swap = modem_cleanup_sim_hot_swap; + iface->load_sim_slots = load_sim_slots; + iface->load_sim_slots_finish = load_sim_slots_finish; } static MMIfaceModem * diff --git a/src/plugins/cinterion/mm-modem-helpers-cinterion.c b/src/plugins/cinterion/mm-modem-helpers-cinterion.c index f22a998c..8af032b1 100644 --- a/src/plugins/cinterion/mm-modem-helpers-cinterion.c +++ b/src/plugins/cinterion/mm-modem-helpers-cinterion.c @@ -538,6 +538,120 @@ finish: } /*****************************************************************************/ +/* ^SCFG response sim parser + * + * Example: + * ... + * ^SCFG: "SIM/CS","SIM_1" + * ... + */ + +gboolean +mm_cinterion_parse_scfg_sim_response (const gchar *response, + guint *active_slot, + GError **error) +{ + g_autoptr(GRegex) r = NULL; + g_autoptr(GMatchInfo) match_info = NULL; + GError *inner_error = NULL; + + if (!response) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response"); + return FALSE; + } + + r = g_regex_new ("\\^SCFG:\\s*\"SIM/CS\",\".*?(\\d)\"", 0, 0, NULL); + g_assert (r != NULL); + + g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); + if (inner_error) { + g_prefix_error (&inner_error, "No valid SIM/CS entry found in ^SCFG response: "); + g_propagate_error (error, inner_error); + return FALSE; + } + + if (!mm_get_uint_from_match_info (match_info, 1, active_slot)) { + g_prefix_error (&inner_error, "Could not parse SIM slot index: "); + g_propagate_error (error, inner_error); + return FALSE; + } + + return TRUE; +} + +gboolean +mm_cinterion_get_available_from_simlocal (const gchar *response, + GArray **available, + GError **error) +{ + g_autoptr(GArray) tmp_available = NULL; + GError *inner_error = NULL; + + if (!response) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response"); + return FALSE; + } + + tmp_available = mm_parse_uint_list (response, &inner_error); + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + + *available = g_steal_pointer (&tmp_available); + + return TRUE; +} + +/*****************************************************************************/ +/* ^SIND response simlocal parser + * + * Example: + * ^SIND: simlocal,1,0,0 + */ + +gboolean +mm_cinterion_parse_sind_simlocal_response (const gchar *response, + GArray **available, + GError **error) +{ + g_autoptr(GRegex) r = NULL; + g_autoptr(GMatchInfo) match_info = NULL; + g_autofree gchar *str = NULL; + GError *inner_error = NULL; + + if (!response) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response"); + return FALSE; + } + + r = g_regex_new ("\\^SIND:\\s*simlocal,\\d+,((\\d,)*\\d)", 0, 0, NULL); + g_assert (r != NULL); + + g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); + if (inner_error) { + g_prefix_error (&inner_error, "No valid SIM/CS entry found in ^SCFG response: "); + g_propagate_error (error, inner_error); + + return FALSE; + } + + if (!g_match_info_matches (match_info)) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't match SIM slot index"); + return FALSE; + } + + str = g_match_info_fetch (match_info, 1); + if (!mm_cinterion_get_available_from_simlocal (str, available, &inner_error)) { + g_propagate_error (error, inner_error); + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ /* +CNMI test parser * * Example (PHS8): diff --git a/src/plugins/cinterion/mm-modem-helpers-cinterion.h b/src/plugins/cinterion/mm-modem-helpers-cinterion.h index 3155d0c1..ad794c86 100644 --- a/src/plugins/cinterion/mm-modem-helpers-cinterion.h +++ b/src/plugins/cinterion/mm-modem-helpers-cinterion.h @@ -73,6 +73,27 @@ gboolean mm_cinterion_parse_scfg_response (const gchar *respon GError **error); /*****************************************************************************/ +/* ^SCFG response sim parser */ + +gboolean mm_cinterion_parse_scfg_sim_response (const gchar *response, + guint *sim_count, + GError **error); + +/*****************************************************************************/ +/* ^SIND simlocal helper */ + +gboolean mm_cinterion_get_available_from_simlocal (const gchar *response, + GArray **available, + GError **error); + +/*****************************************************************************/ +/* ^SIND response simlocal parser */ + +gboolean mm_cinterion_parse_sind_simlocal_response (const gchar *response, + GArray **available, + GError **error); + +/*****************************************************************************/ /* +CNMI test parser */ gboolean mm_cinterion_parse_cnmi_test (const gchar *response, |