aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mm-sim-qmi.c246
1 files changed, 135 insertions, 111 deletions
diff --git a/src/mm-sim-qmi.c b/src/mm-sim-qmi.c
index 7707fc1d..03a44240 100644
--- a/src/mm-sim-qmi.c
+++ b/src/mm-sim-qmi.c
@@ -12,6 +12,7 @@
*
* Copyright (C) 2012 Google, Inc.
* Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc.
*/
#include <config.h>
@@ -42,7 +43,8 @@ enum {
static GParamSpec *properties[PROP_LAST];
struct _MMSimQmiPrivate {
- gboolean dms_uim_deprecated;
+ gboolean dms_uim_deprecated;
+ gchar *imsi;
};
static const guint16 mf_file_path[] = { 0x3F00 };
@@ -444,10 +446,13 @@ uim_get_imsi_ready (QmiClientUim *client,
GAsyncResult *res,
GTask *task)
{
+ MMSimQmi *self;
GError *error = NULL;
g_autoptr(GArray) read_result = NULL;
g_autofree gchar *imsi = NULL;
+ self = g_task_get_source_object (task);
+
read_result = uim_read_finish (client, res, &error);
if (!read_result) {
g_task_return_error (task, error);
@@ -463,12 +468,18 @@ uim_get_imsi_ready (QmiClientUim *client,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"IMSI is malformed");
- else
+ else {
/* EFimsi contains a length byte, follwed by a nibble for parity,
* and then followed by the actual IMSI in BCD. After converting
* the BCD into a decimal string, we simply skip the first 3
* decimal digits to obtain the IMSI. */
+
+ /* Cache IMSI */
+ g_free (self->priv->imsi);
+ self->priv->imsi = g_strdup (imsi + 3);
+
g_task_return_pointer (task, g_strdup (imsi + 3), g_free);
+ }
g_object_unref (task);
}
@@ -489,8 +500,11 @@ dms_uim_get_imsi_ready (QmiClientDms *client,
GAsyncResult *res,
GTask *task)
{
+ MMSimQmi *self;
QmiMessageDmsUimGetImsiOutput *output = NULL;
- GError *error = NULL;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
output = qmi_client_dms_uim_get_imsi_finish (client, res, &error);
if (!output) {
@@ -503,6 +517,11 @@ dms_uim_get_imsi_ready (QmiClientDms *client,
const gchar *str = NULL;
qmi_message_dms_uim_get_imsi_output_get_imsi (output, &str, NULL);
+
+ /* Cache IMSI */
+ g_free (self->priv->imsi);
+ self->priv->imsi = g_strdup (str);
+
g_task_return_pointer (task, g_strdup (str), g_free);
}
@@ -633,63 +652,6 @@ load_gid2 (MMBaseSim *self,
/*****************************************************************************/
/* Load operator identifier */
-static gboolean
-get_home_network (QmiClientNas *client,
- GAsyncResult *res,
- guint16 *out_mcc,
- guint16 *out_mnc,
- gboolean *out_mnc_with_pcs,
- gchar **out_operator_name,
- GError **error)
-{
- QmiMessageNasGetHomeNetworkOutput *output = NULL;
- gboolean success = FALSE;
-
- output = qmi_client_nas_get_home_network_finish (client, res, error);
- if (!output) {
- g_prefix_error (error, "QMI operation failed: ");
- } else if (!qmi_message_nas_get_home_network_output_get_result (output, error)) {
- g_prefix_error (error, "Couldn't get home network: ");
- } else {
- const gchar *name = NULL;
-
- qmi_message_nas_get_home_network_output_get_home_network (
- output,
- out_mcc,
- out_mnc,
- &name,
- NULL);
- if (out_operator_name)
- *out_operator_name = g_strdup (name);
-
- if (out_mnc_with_pcs) {
- gboolean is_3gpp;
- gboolean mnc_includes_pcs_digit;
-
- if (qmi_message_nas_get_home_network_output_get_home_network_3gpp_mnc (
- output,
- &is_3gpp,
- &mnc_includes_pcs_digit,
- NULL) &&
- is_3gpp &&
- mnc_includes_pcs_digit) {
- /* MNC should include PCS digit */
- *out_mnc_with_pcs = TRUE;
- } else {
- /* We default to NO PCS digit, unless of course the MNC is already > 99 */
- *out_mnc_with_pcs = FALSE;
- }
- }
-
- success = TRUE;
- }
-
- if (output)
- qmi_message_nas_get_home_network_output_unref (output);
-
- return success;
-}
-
static gchar *
load_operator_identifier_finish (MMBaseSim *self,
GAsyncResult *res,
@@ -699,54 +661,70 @@ load_operator_identifier_finish (MMBaseSim *self,
}
static void
-load_operator_identifier_ready (QmiClientNas *client,
- GAsyncResult *res,
- GTask *task)
+uim_read_efad_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
{
- guint16 mcc, mnc;
- gboolean mnc_with_pcs;
- GError *error = NULL;
- GString *aux;
+ MMSimQmi *self;
+ GError *error = NULL;
+ g_autoptr(GArray) read_result = NULL;
+ guint mnc_length;
+
+ self = g_task_get_source_object (task);
- if (!get_home_network (client, res, &mcc, &mnc, &mnc_with_pcs, NULL, &error)) {
+ read_result = uim_read_finish (client, res, &error);
+ if (!read_result) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
- aux = g_string_new ("");
- /* MCC always 3 digits */
- g_string_append_printf (aux, "%.3" G_GUINT16_FORMAT, mcc);
- /* Guess about MNC, if < 100 assume it's 2 digits, no PCS info here */
- if (mnc >= 100 || mnc_with_pcs)
- g_string_append_printf (aux, "%.3" G_GUINT16_FORMAT, mnc);
- else
- g_string_append_printf (aux, "%.2" G_GUINT16_FORMAT, mnc);
- g_task_return_pointer (task, g_string_free (aux, FALSE), g_free);
+ if (read_result->len < 4) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unexpected response length reading EFad: %u", read_result->len);
+ g_object_unref (task);
+ return;
+ }
+
+ /* MNC length is byte 4 of this SIM file */
+ mnc_length = read_result->data[3];
+ if (mnc_length == 2 || mnc_length == 3) {
+ g_task_return_pointer (task, g_strndup (self->priv->imsi, 3 + mnc_length), g_free);
+ } else {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "SIM returned invalid MNC length %d (should be either 2 or 3)", mnc_length);
+ }
g_object_unref (task);
}
static void
-load_operator_identifier (MMBaseSim *self,
+load_operator_identifier (MMBaseSim *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GTask *task;
- QmiClient *client = NULL;
+ MMSimQmi *self;
+ GTask *task;
+ self = MM_SIM_QMI (_self);
task = g_task_new (self, NULL, callback, user_data);
- if (!ensure_qmi_client (task,
- MM_SIM_QMI (self),
- QMI_SERVICE_NAS, &client))
- return;
mm_obj_dbg (self, "loading SIM operator identifier...");
- qmi_client_nas_get_home_network (QMI_CLIENT_NAS (client),
- NULL,
- 5,
- NULL,
- (GAsyncReadyCallback)load_operator_identifier_ready,
- task);
+
+ if (!self->priv->imsi) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't load SIM operator identifier without IMSI");
+ g_object_unref (task);
+ return;
+ }
+
+ uim_read (self,
+ 0x6FAD,
+ adf_file_path,
+ G_N_ELEMENTS (adf_file_path),
+ (GAsyncReadyCallback)uim_read_efad_ready,
+ task);
}
/*****************************************************************************/
@@ -760,42 +738,77 @@ load_operator_name_finish (MMBaseSim *self,
return g_task_propagate_pointer (G_TASK (res), error);
}
+static gchar *
+parse_spn (const guint8 *bin,
+ gsize len,
+ GError **error)
+{
+ g_autoptr(GByteArray) bin_array = NULL;
+ gsize binlen;
+
+ /* Remove the FF filler at the end */
+ binlen = len;
+ while (binlen > 1 && bin[binlen - 1] == 0xff)
+ binlen--;
+ if (binlen <= 1) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM returned empty spn");
+ return NULL;
+ }
+
+ /* Setup as bytearray.
+ * First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */
+ bin_array = g_byte_array_sized_new (binlen - 1);
+ g_byte_array_append (bin_array, bin + 1, binlen - 1);
+
+ return mm_modem_charset_bytearray_to_utf8 (bin_array, MM_MODEM_CHARSET_GSM, FALSE, error);
+}
+
static void
-load_operator_name_ready (QmiClientNas *client,
- GAsyncResult *res,
- GTask *task)
+uim_read_efspn_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
{
- gchar *operator_name = NULL;
- GError *error = NULL;
+ GError *error = NULL;
+ g_autoptr(GArray) read_result = NULL;
+ gchar *spn;
- if (!get_home_network (client, res, NULL, NULL, NULL, &operator_name, &error))
+ read_result = uim_read_finish (client, res, &error);
+ if (!read_result) {
g_task_return_error (task, error);
- else
- g_task_return_pointer (task, operator_name, g_free);
+ g_object_unref (task);
+ return;
+ }
+
+ spn = parse_spn ((const guint8 *) read_result->data, read_result->len, &error);
+
+ if (!spn) {
+ g_task_return_error (task, error);
+ } else {
+ g_task_return_pointer (task, spn, g_free);
+ }
+
g_object_unref (task);
}
static void
-load_operator_name (MMBaseSim *self,
+load_operator_name (MMBaseSim *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GTask *task;
- QmiClient *client = NULL;
+ MMSimQmi *self;
+ GTask *task;
+ self = MM_SIM_QMI (_self);
task = g_task_new (self, NULL, callback, user_data);
- if (!ensure_qmi_client (task,
- MM_SIM_QMI (self),
- QMI_SERVICE_NAS, &client))
- return;
mm_obj_dbg (self, "loading SIM operator name...");
- qmi_client_nas_get_home_network (QMI_CLIENT_NAS (client),
- NULL,
- 5,
- NULL,
- (GAsyncReadyCallback)load_operator_name_ready,
- task);
+
+ uim_read (self,
+ 0x6F46,
+ adf_file_path,
+ G_N_ELEMENTS (adf_file_path),
+ (GAsyncReadyCallback)uim_read_efspn_ready,
+ task);
}
/*****************************************************************************/
@@ -1923,6 +1936,16 @@ get_property (GObject *object,
}
static void
+finalize (GObject *object)
+{
+ MMSimQmi *self = MM_SIM_QMI (object);
+
+ g_free (self->priv->imsi);
+
+ G_OBJECT_CLASS (mm_sim_qmi_parent_class)->finalize (object);
+}
+
+static void
mm_sim_qmi_class_init (MMSimQmiClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -1932,6 +1955,7 @@ mm_sim_qmi_class_init (MMSimQmiClass *klass)
object_class->get_property = get_property;
object_class->set_property = set_property;
+ object_class->finalize = finalize;
base_sim_class->wait_sim_ready = wait_sim_ready;
base_sim_class->wait_sim_ready_finish = wait_sim_ready_finish;