Output of NAS Get Home Network depends on network state, and we may fail to read SIM card info if device is not attached. Load card operator name and identifier using UIM Read Transparent.
1995 lines
66 KiB
C
1995 lines
66 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details:
|
|
*
|
|
* Copyright (C) 2012 Google, Inc.
|
|
* Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
|
|
* Copyright (c) 2022 Qualcomm Innovation Center, Inc.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include <ModemManager.h>
|
|
#define _LIBMM_INSIDE_MM
|
|
#include <libmm-glib.h>
|
|
|
|
#include "mm-broadband-modem-qmi.h"
|
|
#include "mm-log-object.h"
|
|
#include "mm-sim-qmi.h"
|
|
#include "mm-modem-helpers-qmi.h"
|
|
#include "mm-shared-qmi.h"
|
|
|
|
G_DEFINE_TYPE (MMSimQmi, mm_sim_qmi, MM_TYPE_BASE_SIM)
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_DMS_UIM_DEPRECATED,
|
|
PROP_LAST
|
|
};
|
|
|
|
static GParamSpec *properties[PROP_LAST];
|
|
|
|
struct _MMSimQmiPrivate {
|
|
gboolean dms_uim_deprecated;
|
|
gchar *imsi;
|
|
};
|
|
|
|
static const guint16 mf_file_path[] = { 0x3F00 };
|
|
static const guint16 adf_file_path[] = { 0x3F00, 0x7FFF };
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
ensure_qmi_client (GTask *task,
|
|
MMSimQmi *self,
|
|
QmiService service,
|
|
QmiClient **o_client)
|
|
{
|
|
MMBaseModem *modem = NULL;
|
|
QmiClient *client;
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
g_object_get (self,
|
|
MM_BASE_SIM_MODEM, &modem,
|
|
NULL);
|
|
g_assert (MM_IS_BASE_MODEM (modem));
|
|
|
|
g_assert (MM_IS_SHARED_QMI (modem));
|
|
client = mm_shared_qmi_peek_client (MM_SHARED_QMI (modem),
|
|
service,
|
|
MM_PORT_QMI_FLAG_DEFAULT,
|
|
&error);
|
|
|
|
g_object_unref (modem);
|
|
|
|
if (!client) {
|
|
if (task) {
|
|
g_task_return_error (task, g_steal_pointer (&error));
|
|
g_object_unref (task);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
*o_client = client;
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Wait for SIM ready */
|
|
|
|
#define SIM_READY_CHECKS_MAX 5
|
|
#define SIM_READY_CHECKS_TIMEOUT_SECS 1
|
|
|
|
typedef struct {
|
|
QmiClient *client_uim;
|
|
guint ready_checks_n;
|
|
} WaitSimReadyContext;
|
|
|
|
static void
|
|
wait_sim_ready_context_free (WaitSimReadyContext *ctx)
|
|
{
|
|
g_clear_object (&ctx->client_uim);
|
|
g_slice_free (WaitSimReadyContext, ctx);
|
|
}
|
|
|
|
static gboolean
|
|
wait_sim_ready_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void sim_ready_check (GTask *task);
|
|
|
|
static gboolean
|
|
sim_ready_retry_cb (GTask *task)
|
|
{
|
|
sim_ready_check (task);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
sim_ready_retry (GTask *task)
|
|
{
|
|
g_timeout_add_seconds (SIM_READY_CHECKS_TIMEOUT_SECS, (GSourceFunc) sim_ready_retry_cb, task);
|
|
}
|
|
|
|
static void
|
|
uim_get_card_status_ready (QmiClientUim *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
g_autoptr(QmiMessageUimGetCardStatusOutput) output = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
MMSimQmi *self;
|
|
|
|
self = g_task_get_source_object (task);
|
|
|
|
output = qmi_client_uim_get_card_status_finish (client, res, &error);
|
|
if (!output ||
|
|
!qmi_message_uim_get_card_status_output_get_result (output, &error) ||
|
|
(!mm_qmi_uim_get_card_status_output_parse (self, output, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &error) &&
|
|
(g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) ||
|
|
g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)))) {
|
|
mm_obj_dbg (self, "sim not yet considered ready... retrying");
|
|
sim_ready_retry (task);
|
|
return;
|
|
}
|
|
|
|
/* SIM is considered ready now */
|
|
mm_obj_dbg (self, "sim is ready");
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
sim_ready_check (GTask *task)
|
|
{
|
|
WaitSimReadyContext *ctx;
|
|
MMSimQmi *self;
|
|
|
|
self = g_task_get_source_object (task);
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
ctx->ready_checks_n++;
|
|
if (ctx->ready_checks_n == SIM_READY_CHECKS_MAX) {
|
|
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"failed waiting for SIM readiness");
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
mm_obj_dbg (self, "checking SIM readiness");
|
|
qmi_client_uim_get_card_status (QMI_CLIENT_UIM (ctx->client_uim),
|
|
NULL,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback) uim_get_card_status_ready,
|
|
task);
|
|
}
|
|
|
|
static void
|
|
wait_sim_ready (MMBaseSim *_self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
QmiClient *client;
|
|
MMSimQmi *self;
|
|
GTask *task;
|
|
WaitSimReadyContext *ctx;
|
|
|
|
self = MM_SIM_QMI (_self);
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
mm_obj_dbg (self, "waiting for SIM to be ready...");
|
|
if (!self->priv->dms_uim_deprecated) {
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
if (!ensure_qmi_client (task, self, QMI_SERVICE_UIM, &client))
|
|
return;
|
|
|
|
ctx = g_slice_new0 (WaitSimReadyContext);
|
|
ctx->client_uim = g_object_ref (client);
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify) wait_sim_ready_context_free);
|
|
|
|
sim_ready_check (task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Load SIM ID (ICCID) */
|
|
|
|
static GArray *
|
|
uim_read_finish (QmiClientUim *client,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_pointer (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
uim_read_ready (QmiClientUim *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimReadTransparentOutput *output;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_uim_read_transparent_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_uim_read_transparent_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't read data from UIM: ");
|
|
g_task_return_error (task, error);
|
|
} else {
|
|
GArray *read_result = NULL;
|
|
|
|
qmi_message_uim_read_transparent_output_get_read_result (output, &read_result, NULL);
|
|
if (read_result)
|
|
g_task_return_pointer (task,
|
|
g_array_ref (read_result),
|
|
(GDestroyNotify) g_array_unref);
|
|
else
|
|
g_task_return_new_error (task,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"Read malformed data from UIM");
|
|
}
|
|
|
|
if (output)
|
|
qmi_message_uim_read_transparent_output_unref (output);
|
|
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
uim_read (MMSimQmi *self,
|
|
guint16 file_id,
|
|
const guint16 *file_path,
|
|
gsize file_path_len,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
QmiClient *client = NULL;
|
|
GArray *file_path_bytes;
|
|
gsize i;
|
|
QmiMessageUimReadTransparentInput *input;
|
|
GArray *aid;
|
|
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
if (!ensure_qmi_client (task,
|
|
self,
|
|
QMI_SERVICE_UIM, &client))
|
|
return;
|
|
|
|
file_path_bytes = g_array_sized_new (FALSE, FALSE, 1, file_path_len * 2);
|
|
for (i = 0; i < file_path_len; ++i) {
|
|
guint8 byte;
|
|
|
|
byte = file_path[i] & 0xFF;
|
|
g_array_append_val (file_path_bytes, byte);
|
|
byte = (file_path[i] >> 8) & 0xFF;
|
|
g_array_append_val (file_path_bytes, byte);
|
|
}
|
|
|
|
input = qmi_message_uim_read_transparent_input_new ();
|
|
aid = g_array_new (FALSE, FALSE, sizeof (guint8)); /* empty AID */
|
|
qmi_message_uim_read_transparent_input_set_session (
|
|
input,
|
|
QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING,
|
|
aid,
|
|
NULL);
|
|
g_array_unref (aid);
|
|
qmi_message_uim_read_transparent_input_set_file (input,
|
|
file_id,
|
|
file_path_bytes,
|
|
NULL);
|
|
qmi_message_uim_read_transparent_input_set_read_information (input,
|
|
0,
|
|
0,
|
|
NULL);
|
|
g_array_unref (file_path_bytes);
|
|
|
|
qmi_client_uim_read_transparent (QMI_CLIENT_UIM (client),
|
|
input,
|
|
10,
|
|
NULL,
|
|
(GAsyncReadyCallback)uim_read_ready,
|
|
task);
|
|
qmi_message_uim_read_transparent_input_unref (input);
|
|
}
|
|
|
|
static gchar *
|
|
load_sim_identifier_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_pointer (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
uim_get_iccid_ready (QmiClientUim *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
GError *error = NULL;
|
|
g_autoptr(GArray) read_result = NULL;
|
|
g_autofree gchar *raw_iccid = NULL;
|
|
gchar *iccid;
|
|
|
|
read_result = uim_read_finish (client, res, &error);
|
|
if (!read_result) {
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
raw_iccid = mm_utils_bin2hexstr ((const guint8 *) read_result->data, read_result->len);
|
|
g_assert (raw_iccid);
|
|
iccid = mm_3gpp_parse_iccid (raw_iccid, &error);
|
|
if (!iccid)
|
|
g_task_return_error (task, error);
|
|
else
|
|
g_task_return_pointer (task, iccid, g_free);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
uim_get_iccid (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
uim_read (self,
|
|
0x2FE2,
|
|
mf_file_path,
|
|
G_N_ELEMENTS (mf_file_path),
|
|
(GAsyncReadyCallback)uim_get_iccid_ready,
|
|
task);
|
|
}
|
|
|
|
static void
|
|
dms_uim_get_iccid_ready (QmiClientDms *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimGetIccidOutput *output = NULL;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_dms_uim_get_iccid_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_dms_uim_get_iccid_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't get UIM ICCID: ");
|
|
g_task_return_error (task, error);
|
|
} else {
|
|
const gchar *str = NULL;
|
|
|
|
qmi_message_dms_uim_get_iccid_output_get_iccid (output, &str, NULL);
|
|
g_task_return_pointer (task, g_strdup (str), g_free);
|
|
}
|
|
|
|
if (output)
|
|
qmi_message_dms_uim_get_iccid_output_unref (output);
|
|
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
dms_uim_get_iccid (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiClient *client = NULL;
|
|
|
|
if (!ensure_qmi_client (task,
|
|
self,
|
|
QMI_SERVICE_DMS, &client))
|
|
return;
|
|
|
|
qmi_client_dms_uim_get_iccid (QMI_CLIENT_DMS (client),
|
|
NULL,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback)dms_uim_get_iccid_ready,
|
|
task);
|
|
}
|
|
|
|
static void
|
|
load_sim_identifier (MMBaseSim *_self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
MMSimQmi *self;
|
|
GTask *task;
|
|
|
|
self = MM_SIM_QMI (_self);
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
mm_obj_dbg (self, "loading SIM identifier...");
|
|
if (!self->priv->dms_uim_deprecated)
|
|
dms_uim_get_iccid (self, task);
|
|
else
|
|
uim_get_iccid (self, task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Load IMSI */
|
|
|
|
static gchar *
|
|
load_imsi_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_pointer (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
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);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
imsi = mm_bcd_to_string ((const guint8 *) read_result->data, read_result->len,
|
|
TRUE /* low_nybble_first */);
|
|
g_assert (imsi);
|
|
if (strlen (imsi) < 3)
|
|
g_task_return_new_error (task,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"IMSI is malformed");
|
|
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);
|
|
}
|
|
|
|
static void
|
|
uim_get_imsi (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
uim_read (self,
|
|
0x6F07,
|
|
adf_file_path,
|
|
G_N_ELEMENTS (adf_file_path),
|
|
(GAsyncReadyCallback)uim_get_imsi_ready,
|
|
task);
|
|
}
|
|
|
|
static void
|
|
dms_uim_get_imsi_ready (QmiClientDms *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
MMSimQmi *self;
|
|
QmiMessageDmsUimGetImsiOutput *output = NULL;
|
|
GError *error = NULL;
|
|
|
|
self = g_task_get_source_object (task);
|
|
|
|
output = qmi_client_dms_uim_get_imsi_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_dms_uim_get_imsi_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't get UIM IMSI: ");
|
|
g_task_return_error (task, error);
|
|
} else {
|
|
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);
|
|
}
|
|
|
|
if (output)
|
|
qmi_message_dms_uim_get_imsi_output_unref (output);
|
|
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
dms_uim_get_imsi (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiClient *client = NULL;
|
|
|
|
if (!ensure_qmi_client (task,
|
|
self,
|
|
QMI_SERVICE_DMS, &client))
|
|
return;
|
|
|
|
qmi_client_dms_uim_get_imsi (QMI_CLIENT_DMS (client),
|
|
NULL,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback)dms_uim_get_imsi_ready,
|
|
task);
|
|
}
|
|
|
|
static void
|
|
load_imsi (MMBaseSim *_self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
MMSimQmi *self;
|
|
GTask *task;
|
|
|
|
self = MM_SIM_QMI (_self);
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
mm_obj_dbg (self, "loading IMSI...");
|
|
if (!self->priv->dms_uim_deprecated)
|
|
dms_uim_get_imsi (self, task);
|
|
else
|
|
uim_get_imsi (self, task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Load GID1 and GID2 */
|
|
|
|
static GByteArray *
|
|
common_load_gid_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_pointer (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
uim_get_gid_ready (QmiClientUim *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
GError *error = NULL;
|
|
g_autoptr(GArray) read_result = NULL;
|
|
|
|
read_result = uim_read_finish (client, res, &error);
|
|
if (!read_result)
|
|
g_task_return_error (task, error);
|
|
else
|
|
g_task_return_pointer (task,
|
|
g_byte_array_append (g_byte_array_sized_new (read_result->len),
|
|
(const guint8 *)(read_result->data),
|
|
read_result->len),
|
|
(GDestroyNotify)g_byte_array_unref);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
common_load_gid (MMBaseSim *self,
|
|
guint16 file_id,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
uim_read (MM_SIM_QMI (self),
|
|
file_id,
|
|
adf_file_path,
|
|
G_N_ELEMENTS (adf_file_path),
|
|
(GAsyncReadyCallback)uim_get_gid_ready,
|
|
task);
|
|
}
|
|
|
|
static GByteArray *
|
|
load_gid1_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return common_load_gid_finish (self, res, error);
|
|
}
|
|
|
|
static void
|
|
load_gid1 (MMBaseSim *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
common_load_gid (self, 0x6F3E, callback, user_data);
|
|
}
|
|
|
|
static GByteArray *
|
|
load_gid2_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return common_load_gid_finish (self, res, error);
|
|
}
|
|
|
|
static void
|
|
load_gid2 (MMBaseSim *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
common_load_gid (self, 0x6F3F, callback, user_data);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Load operator identifier */
|
|
|
|
static gchar *
|
|
load_operator_identifier_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_pointer (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
uim_read_efad_ready (QmiClientUim *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
MMSimQmi *self;
|
|
GError *error = NULL;
|
|
g_autoptr(GArray) read_result = NULL;
|
|
guint mnc_length;
|
|
|
|
self = g_task_get_source_object (task);
|
|
|
|
read_result = uim_read_finish (client, res, &error);
|
|
if (!read_result) {
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
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,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
MMSimQmi *self;
|
|
GTask *task;
|
|
|
|
self = MM_SIM_QMI (_self);
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
mm_obj_dbg (self, "loading SIM operator identifier...");
|
|
|
|
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);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Load operator name */
|
|
|
|
static gchar *
|
|
load_operator_name_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
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
|
|
uim_read_efspn_ready (QmiClientUim *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
GError *error = NULL;
|
|
g_autoptr(GArray) read_result = NULL;
|
|
gchar *spn;
|
|
|
|
read_result = uim_read_finish (client, res, &error);
|
|
if (!read_result) {
|
|
g_task_return_error (task, error);
|
|
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,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
MMSimQmi *self;
|
|
GTask *task;
|
|
|
|
self = MM_SIM_QMI (_self);
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
mm_obj_dbg (self, "loading SIM operator name...");
|
|
|
|
uim_read (self,
|
|
0x6F46,
|
|
adf_file_path,
|
|
G_N_ELEMENTS (adf_file_path),
|
|
(GAsyncReadyCallback)uim_read_efspn_ready,
|
|
task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Load preferred networks */
|
|
|
|
static GList *
|
|
load_preferred_networks_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_pointer (G_TASK (res), error);
|
|
}
|
|
|
|
static GList *
|
|
parse_get_preferred_networks (QmiMessageNasGetPreferredNetworksOutput *output)
|
|
{
|
|
GList *result = NULL;
|
|
GArray *preferred_nets_array = NULL;
|
|
GArray *preferred_nets_mnc_pcs_digit_array = NULL;
|
|
guint i;
|
|
|
|
if (qmi_message_nas_get_preferred_networks_output_get_preferred_networks (output,
|
|
&preferred_nets_array,
|
|
NULL)) {
|
|
qmi_message_nas_get_preferred_networks_output_get_mnc_pcs_digit_include_status (output,
|
|
&preferred_nets_mnc_pcs_digit_array,
|
|
NULL);
|
|
for (i = 0; i < preferred_nets_array->len; i++) {
|
|
QmiMessageNasGetPreferredNetworksOutputPreferredNetworksElement *net;
|
|
QmiMessageNasGetPreferredNetworksOutputMncPcsDigitIncludeStatusElement *mnc_pcs_digit = NULL;
|
|
MMSimPreferredNetwork *new_item;
|
|
g_autofree gchar *operator_code = NULL;
|
|
MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
|
|
|
|
net = &g_array_index (preferred_nets_array,
|
|
QmiMessageNasGetPreferredNetworksOutputPreferredNetworksElement, i);
|
|
if (preferred_nets_mnc_pcs_digit_array && i < preferred_nets_mnc_pcs_digit_array->len)
|
|
mnc_pcs_digit = &g_array_index (preferred_nets_mnc_pcs_digit_array,
|
|
QmiMessageNasGetPreferredNetworksOutputMncPcsDigitIncludeStatusElement, i);
|
|
|
|
new_item = mm_sim_preferred_network_new ();
|
|
|
|
if (net->mnc > 99 || (mnc_pcs_digit != NULL && mnc_pcs_digit->includes_pcs_digit))
|
|
operator_code = g_strdup_printf ("%03d%03d", net->mcc, net->mnc);
|
|
else
|
|
operator_code = g_strdup_printf ("%03d%02d", net->mcc, net->mnc);
|
|
mm_sim_preferred_network_set_operator_code (new_item, operator_code);
|
|
|
|
if (net->radio_access_technology & QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_GSM)
|
|
act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM;
|
|
if (net->radio_access_technology & QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_GSM_COMPACT)
|
|
act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT;
|
|
if (net->radio_access_technology & QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_UTRAN)
|
|
act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
|
|
if (net->radio_access_technology & QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_EUTRAN)
|
|
act |= MM_MODEM_ACCESS_TECHNOLOGY_LTE;
|
|
if (net->radio_access_technology & QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_NGRAN)
|
|
act |= MM_MODEM_ACCESS_TECHNOLOGY_5GNR;
|
|
mm_sim_preferred_network_set_access_technology (new_item, act);
|
|
|
|
result = g_list_append (result, new_item);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
load_preferred_networks_ready (QmiClientNas *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageNasGetPreferredNetworksOutput *output;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_nas_get_preferred_networks_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_nas_get_preferred_networks_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't get preferred networks: ");
|
|
g_task_return_error (task, error);
|
|
} else
|
|
g_task_return_pointer (task, parse_get_preferred_networks (output), (GDestroyNotify) mm_sim_preferred_network_list_free);
|
|
|
|
if (output)
|
|
qmi_message_nas_get_preferred_networks_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
load_preferred_networks (MMBaseSim *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
QmiClient *client = NULL;
|
|
|
|
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 preferred network list...");
|
|
qmi_client_nas_get_preferred_networks (QMI_CLIENT_NAS (client),
|
|
NULL,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback)load_preferred_networks_ready,
|
|
task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Set preferred networks */
|
|
|
|
typedef struct {
|
|
/* Preferred network list to be set, used for after-check comparison */
|
|
GList *set_list;
|
|
} SetPreferredNetworksContext;
|
|
|
|
static void
|
|
set_preferred_network_context_free (SetPreferredNetworksContext *ctx)
|
|
{
|
|
g_list_free_full (ctx->set_list, (GDestroyNotify) mm_sim_preferred_network_free);
|
|
g_slice_free (SetPreferredNetworksContext, ctx);
|
|
}
|
|
|
|
static gboolean
|
|
set_preferred_networks_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
set_preferred_networks_reload_ready (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
GError *error = NULL;
|
|
GList *loaded_list;
|
|
GList *loaded_iter;
|
|
GList *set_iter;
|
|
SetPreferredNetworksContext *ctx;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
loaded_list = load_preferred_networks_finish (self, res, &error);
|
|
if (error) {
|
|
mm_obj_warn (self, "couldn't reload list of preferred networks: %s", error->message);
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
/* Compare the set and loaded network list for differences */
|
|
loaded_iter = loaded_list;
|
|
set_iter = ctx->set_list;
|
|
|
|
while (loaded_iter && set_iter) {
|
|
const gchar *loaded_op_code;
|
|
const gchar *set_op_code;
|
|
MMModemAccessTechnology loaded_act;
|
|
MMModemAccessTechnology set_act;
|
|
|
|
loaded_op_code = mm_sim_preferred_network_get_operator_code (loaded_iter->data);
|
|
set_op_code = mm_sim_preferred_network_get_operator_code (set_iter->data);
|
|
loaded_act = mm_sim_preferred_network_get_access_technology (loaded_iter->data);
|
|
set_act = mm_sim_preferred_network_get_access_technology (set_iter->data);
|
|
|
|
/* Operator code mismatch is never expected, but check it just in case */
|
|
if (g_strcmp0 (loaded_op_code, set_op_code)) {
|
|
mm_obj_warn (self, "operator code mismatch, expected '%s' loaded '%s'",
|
|
set_op_code, loaded_op_code);
|
|
error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Mismatch in requested and set operator code");
|
|
break;
|
|
}
|
|
/* Check if there are access technology bits requested but unset */
|
|
if ((loaded_act & set_act) != set_act) {
|
|
MMModemAccessTechnology unset = set_act & ~loaded_act;
|
|
|
|
mm_obj_warn (self, "access technologies '%s' not set for operator code '%s'",
|
|
mm_modem_access_technology_build_string_from_mask (unset),
|
|
set_op_code);
|
|
error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
|
"Access technology unsupported by modem or SIM");
|
|
break;
|
|
}
|
|
loaded_iter = g_list_next (loaded_iter);
|
|
set_iter = g_list_next (set_iter);
|
|
}
|
|
if (!error && loaded_iter == NULL && set_iter != NULL) {
|
|
/* Not all networks were written; some modems silently discard networks
|
|
* that exceed the SIM card capacity.
|
|
*/
|
|
error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_TOO_MANY,
|
|
"Too many networks; %u networks written",
|
|
g_list_length (loaded_list));
|
|
}
|
|
|
|
if (error) {
|
|
/* Update the PreferredNetworks property to real SIM contents */
|
|
mm_gdbus_sim_set_preferred_networks (MM_GDBUS_SIM (self),
|
|
mm_sim_preferred_network_list_get_variant (loaded_list));
|
|
g_task_return_error (task, error);
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
g_list_free_full (loaded_list, (GDestroyNotify) mm_sim_preferred_network_free);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
set_preferred_networks_ready (QmiClientNas *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageNasSetPreferredNetworksOutput *output;
|
|
GError *error = NULL;
|
|
MMBaseSim *self;
|
|
|
|
self = g_task_get_source_object (task);
|
|
|
|
output = qmi_client_nas_set_preferred_networks_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_nas_set_preferred_networks_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't set preferred networks: ");
|
|
g_task_return_error (task, error);
|
|
} else {
|
|
/* Reload the networks from modem to check whether everything was written */
|
|
load_preferred_networks (self, (GAsyncReadyCallback) set_preferred_networks_reload_ready, task);
|
|
qmi_message_nas_set_preferred_networks_output_unref (output);
|
|
return;
|
|
}
|
|
|
|
if (output)
|
|
qmi_message_nas_set_preferred_networks_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
set_preferred_networks (MMBaseSim *self,
|
|
GList *preferred_network_list,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
QmiMessageNasSetPreferredNetworksInput *input;
|
|
QmiClient *client = NULL;
|
|
GArray *preferred_nets_array;
|
|
GArray *preferred_nets_mnc_pcs_digit_array;
|
|
SetPreferredNetworksContext *ctx;
|
|
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
if (!ensure_qmi_client (task,
|
|
MM_SIM_QMI (self),
|
|
QMI_SERVICE_NAS, &client))
|
|
return;
|
|
|
|
ctx = g_slice_new0 (SetPreferredNetworksContext);
|
|
ctx->set_list = mm_sim_preferred_network_list_copy (preferred_network_list);
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify) set_preferred_network_context_free);
|
|
|
|
mm_obj_dbg (self, "setting preferred networks...");
|
|
|
|
input = qmi_message_nas_set_preferred_networks_input_new ();
|
|
|
|
preferred_nets_array = g_array_new (FALSE, TRUE, sizeof (QmiMessageNasSetPreferredNetworksInputPreferredNetworksElement));
|
|
preferred_nets_mnc_pcs_digit_array = g_array_new (FALSE, TRUE, sizeof (QmiMessageNasSetPreferredNetworksInputMncPcsDigitIncludeStatusElement));
|
|
|
|
while (preferred_network_list) {
|
|
QmiMessageNasSetPreferredNetworksInputPreferredNetworksElement preferred_nets_element;
|
|
QmiMessageNasSetPreferredNetworksInputMncPcsDigitIncludeStatusElement pcs_digit_element;
|
|
const gchar *operator_code;
|
|
MMModemAccessTechnology act;
|
|
|
|
memset (&preferred_nets_element, 0, sizeof (preferred_nets_element));
|
|
memset (&pcs_digit_element, 0, sizeof (pcs_digit_element));
|
|
|
|
operator_code = mm_sim_preferred_network_get_operator_code (preferred_network_list->data);
|
|
act = mm_sim_preferred_network_get_access_technology (preferred_network_list->data);
|
|
if (mm_3gpp_parse_operator_id (operator_code, &preferred_nets_element.mcc, &preferred_nets_element.mnc,
|
|
&pcs_digit_element.includes_pcs_digit, NULL)) {
|
|
pcs_digit_element.mcc = preferred_nets_element.mcc;
|
|
pcs_digit_element.mnc = preferred_nets_element.mnc;
|
|
|
|
preferred_nets_element.radio_access_technology = QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_UNSPECIFIED;
|
|
if (act & MM_MODEM_ACCESS_TECHNOLOGY_GSM)
|
|
preferred_nets_element.radio_access_technology |= QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_GSM;
|
|
if (act & MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT)
|
|
preferred_nets_element.radio_access_technology |= QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_GSM_COMPACT;
|
|
if (act & MM_MODEM_ACCESS_TECHNOLOGY_UMTS)
|
|
preferred_nets_element.radio_access_technology |= QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_UTRAN;
|
|
if (act & MM_MODEM_ACCESS_TECHNOLOGY_LTE)
|
|
preferred_nets_element.radio_access_technology |= QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_EUTRAN;
|
|
if (act & MM_MODEM_ACCESS_TECHNOLOGY_5GNR)
|
|
preferred_nets_element.radio_access_technology |= QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_NGRAN;
|
|
|
|
g_array_append_val (preferred_nets_array, preferred_nets_element);
|
|
g_array_append_val (preferred_nets_mnc_pcs_digit_array, pcs_digit_element);
|
|
}
|
|
|
|
preferred_network_list = g_list_next (preferred_network_list);
|
|
}
|
|
|
|
qmi_message_nas_set_preferred_networks_input_set_preferred_networks (input, preferred_nets_array, NULL);
|
|
qmi_message_nas_set_preferred_networks_input_set_mnc_pcs_digit_include_status (input, preferred_nets_mnc_pcs_digit_array, NULL);
|
|
/* Always clear any pre-existing networks */
|
|
qmi_message_nas_set_preferred_networks_input_set_clear_previous_preferred_networks (input, TRUE, NULL);
|
|
|
|
qmi_client_nas_set_preferred_networks (QMI_CLIENT_NAS (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback)set_preferred_networks_ready,
|
|
task);
|
|
|
|
qmi_message_nas_set_preferred_networks_input_unref (input);
|
|
g_array_unref (preferred_nets_array);
|
|
g_array_unref (preferred_nets_mnc_pcs_digit_array);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Send PIN */
|
|
|
|
static GError *
|
|
pin_qmi_error_to_mobile_equipment_error (GError *qmi_error)
|
|
{
|
|
GError *me_error = NULL;
|
|
|
|
if (g_error_matches (qmi_error,
|
|
QMI_PROTOCOL_ERROR,
|
|
QMI_PROTOCOL_ERROR_INCORRECT_PIN)) {
|
|
me_error = g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR,
|
|
MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD,
|
|
qmi_error->message);
|
|
} else if (g_error_matches (qmi_error,
|
|
QMI_PROTOCOL_ERROR,
|
|
QMI_PROTOCOL_ERROR_PIN_BLOCKED)) {
|
|
me_error = g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR,
|
|
MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK,
|
|
qmi_error->message);
|
|
}
|
|
|
|
if (me_error) {
|
|
g_error_free (qmi_error);
|
|
return me_error;
|
|
}
|
|
|
|
return qmi_error;
|
|
}
|
|
|
|
static gboolean
|
|
send_pin_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
uim_verify_pin_ready (QmiClientUim *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimVerifyPinOutput *output = NULL;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_uim_verify_pin_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_uim_verify_pin_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't verify PIN: ");
|
|
g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
if (output)
|
|
qmi_message_uim_verify_pin_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
uim_verify_pin (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimVerifyPinInput *input;
|
|
QmiClient *client = NULL;
|
|
GArray *aid;
|
|
|
|
if (!ensure_qmi_client (task,
|
|
self,
|
|
QMI_SERVICE_UIM, &client))
|
|
return;
|
|
|
|
input = qmi_message_uim_verify_pin_input_new ();
|
|
qmi_message_uim_verify_pin_input_set_info (
|
|
input,
|
|
QMI_UIM_PIN_ID_PIN1,
|
|
g_task_get_task_data (task),
|
|
NULL);
|
|
aid = g_array_new (FALSE, FALSE, sizeof (guint8)); /* empty AID */
|
|
qmi_message_uim_verify_pin_input_set_session (
|
|
input,
|
|
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
|
|
aid,
|
|
NULL);
|
|
g_array_unref (aid);
|
|
qmi_client_uim_verify_pin (QMI_CLIENT_UIM (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback) uim_verify_pin_ready,
|
|
task);
|
|
qmi_message_uim_verify_pin_input_unref (input);
|
|
}
|
|
|
|
static void
|
|
dms_uim_verify_pin_ready (QmiClientDms *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimVerifyPinOutput *output = NULL;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_dms_uim_verify_pin_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_dms_uim_verify_pin_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't verify PIN: ");
|
|
g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
if (output)
|
|
qmi_message_dms_uim_verify_pin_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
dms_uim_verify_pin (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimVerifyPinInput *input;
|
|
QmiClient *client = NULL;
|
|
|
|
if (!ensure_qmi_client (NULL,
|
|
self,
|
|
QMI_SERVICE_DMS, &client)) {
|
|
/* Very unlikely that this will ever happen, but anyway, try with
|
|
* UIM service instead */
|
|
uim_verify_pin (self, task);
|
|
return;
|
|
}
|
|
|
|
mm_obj_dbg (self, "sending PIN...");
|
|
input = qmi_message_dms_uim_verify_pin_input_new ();
|
|
qmi_message_dms_uim_verify_pin_input_set_info (
|
|
input,
|
|
QMI_DMS_UIM_PIN_ID_PIN,
|
|
g_task_get_task_data (task),
|
|
NULL);
|
|
qmi_client_dms_uim_verify_pin (QMI_CLIENT_DMS (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback) dms_uim_verify_pin_ready,
|
|
task);
|
|
qmi_message_dms_uim_verify_pin_input_unref (input);
|
|
}
|
|
|
|
static void
|
|
send_pin (MMBaseSim *_self,
|
|
const gchar *pin,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
MMSimQmi *self;
|
|
|
|
self = MM_SIM_QMI (_self);
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
g_task_set_task_data (task, g_strdup (pin), g_free);
|
|
|
|
mm_obj_dbg (self, "verifying PIN...");
|
|
if (!self->priv->dms_uim_deprecated)
|
|
dms_uim_verify_pin (self, task);
|
|
else
|
|
uim_verify_pin (self, task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Send PUK */
|
|
|
|
typedef struct {
|
|
gchar *puk;
|
|
gchar *new_pin;
|
|
} UnblockPinContext;
|
|
|
|
static void
|
|
unblock_pin_context_free (UnblockPinContext *ctx)
|
|
{
|
|
g_free (ctx->puk);
|
|
g_free (ctx->new_pin);
|
|
g_slice_free (UnblockPinContext, ctx);
|
|
}
|
|
|
|
static gboolean
|
|
send_puk_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
uim_unblock_pin_ready (QmiClientUim *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimUnblockPinOutput *output = NULL;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_uim_unblock_pin_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_uim_unblock_pin_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't unblock PIN: ");
|
|
g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
if (output)
|
|
qmi_message_uim_unblock_pin_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
uim_unblock_pin (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimUnblockPinInput *input;
|
|
QmiClient *client = NULL;
|
|
UnblockPinContext *ctx;
|
|
GArray *aid;
|
|
|
|
if (!ensure_qmi_client (task,
|
|
self,
|
|
QMI_SERVICE_UIM, &client))
|
|
return;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
input = qmi_message_uim_unblock_pin_input_new ();
|
|
qmi_message_uim_unblock_pin_input_set_info (
|
|
input,
|
|
QMI_UIM_PIN_ID_PIN1,
|
|
ctx->puk,
|
|
ctx->new_pin,
|
|
NULL);
|
|
aid = g_array_new (FALSE, FALSE, sizeof (guint8)); /* empty AID */
|
|
qmi_message_uim_unblock_pin_input_set_session (
|
|
input,
|
|
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
|
|
aid,
|
|
NULL);
|
|
g_array_unref (aid);
|
|
qmi_client_uim_unblock_pin (QMI_CLIENT_UIM (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback) uim_unblock_pin_ready,
|
|
task);
|
|
qmi_message_uim_unblock_pin_input_unref (input);
|
|
}
|
|
|
|
static void
|
|
dms_uim_unblock_pin_ready (QmiClientDms *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimUnblockPinOutput *output = NULL;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_dms_uim_unblock_pin_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_dms_uim_unblock_pin_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't unblock PIN: ");
|
|
g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
if (output)
|
|
qmi_message_dms_uim_unblock_pin_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
dms_uim_unblock_pin (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimUnblockPinInput *input;
|
|
QmiClient *client = NULL;
|
|
UnblockPinContext *ctx;
|
|
|
|
if (!ensure_qmi_client (NULL,
|
|
self,
|
|
QMI_SERVICE_DMS, &client)) {
|
|
/* Very unlikely that this will ever happen, but anyway, try with
|
|
* UIM service instead */
|
|
uim_unblock_pin (self, task);
|
|
return;
|
|
}
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
input = qmi_message_dms_uim_unblock_pin_input_new ();
|
|
qmi_message_dms_uim_unblock_pin_input_set_info (
|
|
input,
|
|
QMI_DMS_UIM_PIN_ID_PIN,
|
|
ctx->puk,
|
|
ctx->new_pin,
|
|
NULL);
|
|
qmi_client_dms_uim_unblock_pin (QMI_CLIENT_DMS (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback)dms_uim_unblock_pin_ready,
|
|
task);
|
|
qmi_message_dms_uim_unblock_pin_input_unref (input);
|
|
}
|
|
|
|
static void
|
|
send_puk (MMBaseSim *_self,
|
|
const gchar *puk,
|
|
const gchar *new_pin,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
UnblockPinContext *ctx;
|
|
MMSimQmi *self;
|
|
|
|
self = MM_SIM_QMI (_self);
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
ctx = g_slice_new (UnblockPinContext);
|
|
ctx->puk = g_strdup (puk);
|
|
ctx->new_pin = g_strdup (new_pin);
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify) unblock_pin_context_free);
|
|
|
|
mm_obj_dbg (self, "unblocking PIN...");
|
|
if (!self->priv->dms_uim_deprecated)
|
|
dms_uim_unblock_pin (self, task);
|
|
else
|
|
uim_unblock_pin (self, task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Change PIN */
|
|
|
|
typedef struct {
|
|
gchar *old_pin;
|
|
gchar *new_pin;
|
|
} ChangePinContext;
|
|
|
|
static void
|
|
change_pin_context_free (ChangePinContext *ctx)
|
|
{
|
|
g_free (ctx->old_pin);
|
|
g_free (ctx->new_pin);
|
|
g_slice_free (ChangePinContext, ctx);
|
|
}
|
|
|
|
static gboolean
|
|
change_pin_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
uim_change_pin_ready (QmiClientUim *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimChangePinOutput *output = NULL;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_uim_change_pin_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_uim_change_pin_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't change PIN: ");
|
|
g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
if (output)
|
|
qmi_message_uim_change_pin_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
uim_change_pin (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimChangePinInput *input;
|
|
QmiClient *client = NULL;
|
|
ChangePinContext *ctx;
|
|
GArray *aid;
|
|
|
|
if (!ensure_qmi_client (task,
|
|
self,
|
|
QMI_SERVICE_UIM, &client))
|
|
return;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
input = qmi_message_uim_change_pin_input_new ();
|
|
qmi_message_uim_change_pin_input_set_info (
|
|
input,
|
|
QMI_UIM_PIN_ID_PIN1,
|
|
ctx->old_pin,
|
|
ctx->new_pin,
|
|
NULL);
|
|
aid = g_array_new (FALSE, FALSE, sizeof (guint8)); /* empty AID */
|
|
qmi_message_uim_change_pin_input_set_session (
|
|
input,
|
|
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
|
|
aid,
|
|
NULL);
|
|
g_array_unref (aid);
|
|
qmi_client_uim_change_pin (QMI_CLIENT_UIM (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback) uim_change_pin_ready,
|
|
task);
|
|
qmi_message_uim_change_pin_input_unref (input);
|
|
}
|
|
|
|
static void
|
|
dms_uim_change_pin_ready (QmiClientDms *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimChangePinOutput *output = NULL;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_dms_uim_change_pin_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_dms_uim_change_pin_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't change PIN: ");
|
|
g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
if (output)
|
|
qmi_message_dms_uim_change_pin_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
dms_uim_change_pin (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimChangePinInput *input;
|
|
QmiClient *client = NULL;
|
|
ChangePinContext *ctx;
|
|
|
|
if (!ensure_qmi_client (NULL,
|
|
self,
|
|
QMI_SERVICE_DMS, &client)) {
|
|
/* Very unlikely that this will ever happen, but anyway, try with
|
|
* UIM service instead */
|
|
uim_change_pin (self, task);
|
|
return;
|
|
}
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
input = qmi_message_dms_uim_change_pin_input_new ();
|
|
qmi_message_dms_uim_change_pin_input_set_info (
|
|
input,
|
|
QMI_DMS_UIM_PIN_ID_PIN,
|
|
ctx->old_pin,
|
|
ctx->new_pin,
|
|
NULL);
|
|
qmi_client_dms_uim_change_pin (QMI_CLIENT_DMS (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback) dms_uim_change_pin_ready,
|
|
task);
|
|
qmi_message_dms_uim_change_pin_input_unref (input);
|
|
}
|
|
|
|
static void
|
|
change_pin (MMBaseSim *_self,
|
|
const gchar *old_pin,
|
|
const gchar *new_pin,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
ChangePinContext *ctx;
|
|
MMSimQmi *self;
|
|
|
|
self = MM_SIM_QMI (_self);
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
ctx = g_slice_new (ChangePinContext);
|
|
ctx->old_pin = g_strdup (old_pin);
|
|
ctx->new_pin = g_strdup (new_pin);
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify) change_pin_context_free);
|
|
|
|
mm_obj_dbg (self, "changing PIN...");
|
|
if (!self->priv->dms_uim_deprecated)
|
|
dms_uim_change_pin (self, task);
|
|
else
|
|
uim_change_pin (self, task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Enable PIN */
|
|
|
|
typedef struct {
|
|
gchar *pin;
|
|
gboolean enabled;
|
|
} EnablePinContext;
|
|
|
|
static void
|
|
enable_pin_context_free (EnablePinContext *ctx)
|
|
{
|
|
g_free (ctx->pin);
|
|
g_slice_free (EnablePinContext, ctx);
|
|
}
|
|
|
|
static gboolean
|
|
enable_pin_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
uim_set_pin_protection_ready (QmiClientUim *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimSetPinProtectionOutput *output = NULL;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_uim_set_pin_protection_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_uim_set_pin_protection_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't enable PIN: ");
|
|
g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
if (output)
|
|
qmi_message_uim_set_pin_protection_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
uim_enable_pin (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimSetPinProtectionInput *input;
|
|
QmiClient *client = NULL;
|
|
EnablePinContext *ctx;
|
|
GArray *aid;
|
|
|
|
if (!ensure_qmi_client (task,
|
|
MM_SIM_QMI (self),
|
|
QMI_SERVICE_UIM, &client))
|
|
return;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
input = qmi_message_uim_set_pin_protection_input_new ();
|
|
qmi_message_uim_set_pin_protection_input_set_info (
|
|
input,
|
|
QMI_UIM_PIN_ID_PIN1,
|
|
ctx->enabled,
|
|
ctx->pin,
|
|
NULL);
|
|
aid = g_array_new (FALSE, FALSE, sizeof (guint8)); /* empty AID */
|
|
qmi_message_uim_set_pin_protection_input_set_session (
|
|
input,
|
|
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
|
|
aid,
|
|
NULL);
|
|
g_array_unref (aid);
|
|
qmi_client_uim_set_pin_protection (QMI_CLIENT_UIM (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback)uim_set_pin_protection_ready,
|
|
task);
|
|
qmi_message_uim_set_pin_protection_input_unref (input);
|
|
}
|
|
|
|
static void
|
|
dms_uim_set_pin_protection_ready (QmiClientDms *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimSetPinProtectionOutput *output = NULL;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_dms_uim_set_pin_protection_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_dms_uim_set_pin_protection_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't enable PIN: ");
|
|
g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
if (output)
|
|
qmi_message_dms_uim_set_pin_protection_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
dms_uim_enable_pin (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimSetPinProtectionInput *input;
|
|
QmiClient *client = NULL;
|
|
EnablePinContext *ctx;
|
|
|
|
if (!ensure_qmi_client (NULL,
|
|
MM_SIM_QMI (self),
|
|
QMI_SERVICE_DMS, &client)) {
|
|
/* Very unlikely that this will ever happen, but anyway, try with
|
|
* UIM service instead */
|
|
uim_enable_pin (self, task);
|
|
return;
|
|
}
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
input = qmi_message_dms_uim_set_pin_protection_input_new ();
|
|
qmi_message_dms_uim_set_pin_protection_input_set_info (
|
|
input,
|
|
QMI_DMS_UIM_PIN_ID_PIN,
|
|
ctx->enabled,
|
|
ctx->pin,
|
|
NULL);
|
|
qmi_client_dms_uim_set_pin_protection (QMI_CLIENT_DMS (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback)dms_uim_set_pin_protection_ready,
|
|
task);
|
|
qmi_message_dms_uim_set_pin_protection_input_unref (input);
|
|
}
|
|
|
|
static void
|
|
enable_pin (MMBaseSim *_self,
|
|
const gchar *pin,
|
|
gboolean enabled,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
EnablePinContext *ctx;
|
|
MMSimQmi *self;
|
|
|
|
self = MM_SIM_QMI (_self);
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
ctx = g_slice_new (EnablePinContext);
|
|
ctx->pin = g_strdup (pin);
|
|
ctx->enabled = enabled;
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify) enable_pin_context_free);
|
|
|
|
mm_obj_dbg (self, "%s PIN...", enabled ? "enabling" : "disabling");
|
|
if (!self->priv->dms_uim_deprecated)
|
|
dms_uim_enable_pin (self, task);
|
|
else
|
|
uim_enable_pin (self, task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
MMBaseSim *
|
|
mm_sim_qmi_new_finish (GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
GObject *source;
|
|
GObject *sim;
|
|
|
|
source = g_async_result_get_source_object (res);
|
|
sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error);
|
|
g_object_unref (source);
|
|
|
|
if (!sim)
|
|
return NULL;
|
|
|
|
/* Only export valid SIMs */
|
|
mm_base_sim_export (MM_BASE_SIM (sim));
|
|
|
|
return MM_BASE_SIM (sim);
|
|
}
|
|
|
|
void
|
|
mm_sim_qmi_new (MMBaseModem *modem,
|
|
gboolean dms_uim_deprecated,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
g_async_initable_new_async (MM_TYPE_SIM_QMI,
|
|
G_PRIORITY_DEFAULT,
|
|
cancellable,
|
|
callback,
|
|
user_data,
|
|
MM_BASE_SIM_MODEM, modem,
|
|
MM_SIM_QMI_DMS_UIM_DEPRECATED, dms_uim_deprecated,
|
|
"active", TRUE, /* by default always active */
|
|
NULL);
|
|
}
|
|
|
|
MMBaseSim *
|
|
mm_sim_qmi_new_initialized (MMBaseModem *modem,
|
|
gboolean dms_uim_deprecated,
|
|
guint slot_number,
|
|
gboolean active,
|
|
const gchar *sim_identifier,
|
|
const gchar *imsi,
|
|
const gchar *eid,
|
|
const gchar *operator_identifier,
|
|
const gchar *operator_name,
|
|
const GStrv emergency_numbers)
|
|
{
|
|
MMBaseSim *sim;
|
|
|
|
sim = MM_BASE_SIM (g_object_new (MM_TYPE_SIM_QMI,
|
|
MM_BASE_SIM_MODEM, modem,
|
|
MM_SIM_QMI_DMS_UIM_DEPRECATED, dms_uim_deprecated,
|
|
MM_BASE_SIM_SLOT_NUMBER, slot_number,
|
|
"active", active,
|
|
"sim-identifier", sim_identifier,
|
|
"imsi", imsi,
|
|
"eid", eid,
|
|
"operator-identifier", operator_identifier,
|
|
"operator-name", operator_name,
|
|
"emergency-numbers", emergency_numbers,
|
|
NULL));
|
|
|
|
mm_base_sim_export (sim);
|
|
return sim;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
mm_sim_qmi_init (MMSimQmi *self)
|
|
{
|
|
/* Initialize private data */
|
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
|
MM_TYPE_SIM_QMI,
|
|
MMSimQmiPrivate);
|
|
}
|
|
|
|
static void
|
|
set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MMSimQmi *self = MM_SIM_QMI (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_DMS_UIM_DEPRECATED:
|
|
self->priv->dms_uim_deprecated = g_value_get_boolean (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MMSimQmi *self = MM_SIM_QMI (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_DMS_UIM_DEPRECATED:
|
|
g_value_set_boolean (value, self->priv->dms_uim_deprecated);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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);
|
|
MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass);
|
|
|
|
g_type_class_add_private (object_class, sizeof (MMSimQmiPrivate));
|
|
|
|
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;
|
|
base_sim_class->load_sim_identifier = load_sim_identifier;
|
|
base_sim_class->load_sim_identifier_finish = load_sim_identifier_finish;
|
|
base_sim_class->load_imsi = load_imsi;
|
|
base_sim_class->load_imsi_finish = load_imsi_finish;
|
|
base_sim_class->load_operator_identifier = load_operator_identifier;
|
|
base_sim_class->load_operator_identifier_finish = load_operator_identifier_finish;
|
|
base_sim_class->load_operator_name = load_operator_name;
|
|
base_sim_class->load_operator_name_finish = load_operator_name_finish;
|
|
base_sim_class->load_gid1 = load_gid1;
|
|
base_sim_class->load_gid1_finish = load_gid1_finish;
|
|
base_sim_class->load_gid2 = load_gid2;
|
|
base_sim_class->load_gid2_finish = load_gid2_finish;
|
|
base_sim_class->load_preferred_networks = load_preferred_networks;
|
|
base_sim_class->load_preferred_networks_finish = load_preferred_networks_finish;
|
|
base_sim_class->set_preferred_networks = set_preferred_networks;
|
|
base_sim_class->set_preferred_networks_finish = set_preferred_networks_finish;
|
|
base_sim_class->send_pin = send_pin;
|
|
base_sim_class->send_pin_finish = send_pin_finish;
|
|
base_sim_class->send_puk = send_puk;
|
|
base_sim_class->send_puk_finish = send_puk_finish;
|
|
base_sim_class->change_pin = change_pin;
|
|
base_sim_class->change_pin_finish = change_pin_finish;
|
|
base_sim_class->enable_pin = enable_pin;
|
|
base_sim_class->enable_pin_finish = enable_pin_finish;
|
|
|
|
properties[PROP_DMS_UIM_DEPRECATED] =
|
|
g_param_spec_boolean (MM_SIM_QMI_DMS_UIM_DEPRECATED,
|
|
"DMS UIM deprecated",
|
|
"Whether DMS UIM commands should be skipped",
|
|
FALSE,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
|
g_object_class_install_property (object_class, PROP_DMS_UIM_DEPRECATED, properties[PROP_DMS_UIM_DEPRECATED]);
|
|
}
|