blob: 0dfcd81d27d283022fc461ac5f768e51646cb318 [file] [log] [blame] [edit]
/* -*- 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) 2013 Aleksander Morgado <aleksander@gnu.org>
* Copyright (C) 2022 Google 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-mbim.h"
#include "mm-error-helpers.h"
#include "mm-iface-modem.h"
#include "mm-log-object.h"
#include "mm-modem-helpers-mbim.h"
#include "mm-sim-mbim.h"
#define MS_UICC_LOW_LEVEL_SUPPORTED_VERSION 0x01
G_DEFINE_TYPE (MMSimMbim, mm_sim_mbim, MM_TYPE_BASE_SIM)
struct _MMSimMbimPrivate {
gboolean preload;
GCancellable *preload_cancellable;
GError *preload_error;
gchar *imsi;
gchar *iccid;
GError *iccid_error;
MMSimType sim_type;
MMSimEsimStatus esim_status;
MMSimRemovability removability;
GByteArray *application_id;
GError *application_id_error;
/* need to get notified when a full sync of the info
* is needed, so that we clear the preloaded data. */
guint modem_sync_needed_id;
};
/*****************************************************************************/
static gboolean
peek_device (gpointer self,
MbimDevice **o_device,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBaseModem *modem = NULL;
MMPortMbim *port;
g_object_get (G_OBJECT (self),
MM_BASE_SIM_MODEM, &modem,
NULL);
g_assert (MM_IS_BASE_MODEM (modem));
port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (modem));
g_object_unref (modem);
if (!port) {
g_task_report_new_error (self,
callback,
user_data,
peek_device,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't peek MBIM port");
return FALSE;
}
*o_device = mm_port_mbim_peek_device (port);
return TRUE;
}
static void
update_modem_unlock_retries (MMSimMbim *self,
MbimPinType pin_type,
guint32 remaining_attempts)
{
MMBaseModem *modem = NULL;
g_object_get (G_OBJECT (self),
MM_BASE_SIM_MODEM, &modem,
NULL);
g_assert (MM_IS_BASE_MODEM (modem));
mm_broadband_modem_mbim_set_unlock_retries (MM_BROADBAND_MODEM_MBIM (modem),
mm_modem_lock_from_mbim_pin_type (pin_type),
remaining_attempts);
g_object_unref (modem);
}
/*****************************************************************************/
/* Monitor modem sync signal */
#if defined WITH_SUSPEND_RESUME
static void reset_subscriber_info (MMSimMbim *self);
static void
clear_modem_sync_monitor (MMSimMbim *self)
{
g_autoptr(MMBaseModem) modem = NULL;
if (!self->priv->modem_sync_needed_id)
return;
g_object_get (G_OBJECT (self),
MM_BASE_SIM_MODEM, &modem,
NULL);
if (g_signal_handler_is_connected (modem, self->priv->modem_sync_needed_id))
g_signal_handler_disconnect (modem, self->priv->modem_sync_needed_id);
self->priv->modem_sync_needed_id = 0;
}
static void
setup_modem_sync_monitor (MMSimMbim *self)
{
g_autoptr(MMBaseModem) modem = NULL;
if (self->priv->modem_sync_needed_id)
return;
g_object_get (G_OBJECT (self),
MM_BASE_SIM_MODEM, &modem,
NULL);
self->priv->modem_sync_needed_id = g_signal_connect_swapped (modem,
MM_BROADBAND_MODEM_SIGNAL_SYNC_NEEDED,
G_CALLBACK (reset_subscriber_info),
self);
}
#endif /* WITH_SUSPEND_RESUME */
/*****************************************************************************/
/* Preload subscriber info */
static void
reset_subscriber_info (MMSimMbim *self)
{
/* Request to stop any ongoing preload attempt */
g_cancellable_cancel (self->priv->preload_cancellable);
g_clear_object (&self->priv->preload_cancellable);
/* And reset the info right away */
self->priv->preload = FALSE;
g_clear_error (&self->priv->preload_error);
g_clear_pointer (&self->priv->imsi, g_free);
g_clear_pointer (&self->priv->iccid, g_free);
g_clear_error (&self->priv->iccid_error);
self->priv->sim_type = MM_SIM_TYPE_UNKNOWN;
self->priv->esim_status = MM_SIM_ESIM_STATUS_UNKNOWN;
self->priv->removability = MM_SIM_REMOVABILITY_UNKNOWN;
g_clear_pointer (&self->priv->application_id, g_byte_array_unref);
g_clear_error (&self->priv->application_id_error);
}
static gboolean
preload_subscriber_info_finish (MMSimMbim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
application_list_query_ready (MbimDevice *device,
GAsyncResult *res,
GTask *task)
{
MMSimMbim *self;
g_autoptr(MbimMessage) response = NULL;
guint32 version;
guint32 application_count;
guint32 active_application_index;
g_autoptr(MbimUiccApplicationArray) applications = NULL;
g_autoptr(GError) error = NULL;
self = g_task_get_source_object (task);
response = mbim_device_command_finish (device, res, &error);
if (g_task_return_error_if_cancelled (task)) {
g_object_unref (task);
return;
}
g_clear_pointer (&self->priv->application_id, g_byte_array_unref);
g_clear_error (&self->priv->application_id_error);
self->priv->application_id_error = g_steal_pointer (&error);
if (response &&
mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &self->priv->application_id_error) &&
mbim_message_ms_uicc_low_level_access_application_list_response_parse (
response,
&version,
&application_count,
&active_application_index,
NULL, /* application_list_size_bytes */
&applications,
&self->priv->application_id_error)) {
mm_obj_dbg (self, "processed MS UICC low level access application list response");
if (version != MS_UICC_LOW_LEVEL_SUPPORTED_VERSION)
self->priv->application_id_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Application list version %u is not supported",
version);
else if (active_application_index >= application_count)
self->priv->application_id_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
"Invalid active application index: %u >= %u",
active_application_index, application_count);
else
self->priv->application_id = g_byte_array_append (g_byte_array_sized_new (applications[active_application_index]->application_id_size),
applications[active_application_index]->application_id,
applications[active_application_index]->application_id_size);
}
/* At this point we just complete, as all the info and errors have already
* been stored */
g_clear_object (&self->priv->preload_cancellable);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
uicc_application_list (GTask *task,
MbimDevice *device)
{
g_autoptr(MbimMessage) message = NULL;
message = mbim_message_ms_uicc_low_level_access_application_list_query_new (NULL);
mbim_device_command (device,
message,
10,
NULL,
(GAsyncReadyCallback)application_list_query_ready,
task);
}
static void
subscriber_ready_status_ready (MbimDevice *device,
GAsyncResult *res,
GTask *task)
{
MMSimMbim *self;
g_autoptr(MbimMessage) response = NULL;
g_autofree gchar *raw_iccid = NULL;
g_autoptr(GError) error = NULL;
MbimSubscriberReadyState ready_state = MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED;
self = g_task_get_source_object (task);
response = mbim_device_command_finish (device, res, &error);
if (g_task_return_error_if_cancelled (task)) {
g_object_unref (task);
return;
}
g_clear_error (&self->priv->preload_error);
g_clear_pointer (&self->priv->imsi, g_free);
g_clear_pointer (&self->priv->iccid, g_free);
g_clear_error (&self->priv->iccid_error);
self->priv->preload_error = g_steal_pointer (&error);
if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &self->priv->preload_error)) {
if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
MbimSubscriberReadyStatusFlag flags = MBIM_SUBSCRIBER_READY_STATUS_FLAG_NONE;
if (!mbim_message_ms_basic_connect_v3_subscriber_ready_status_response_parse (
response,
&ready_state,
&flags,
&self->priv->imsi,
&raw_iccid,
NULL, /* ready_info */
NULL, /* telephone_numbers_count */
NULL, /* telephone_numbers */
&self->priv->preload_error))
g_prefix_error (&self->priv->preload_error, "Failed processing MBIMEx v3.0 subscriber ready status response: ");
else {
mm_obj_dbg (self, "processed MBIMEx v3.0 subscriber ready status response");
/* SIM type */
if (flags & MBIM_SUBSCRIBER_READY_STATUS_FLAG_ESIM) {
self->priv->sim_type = MM_SIM_TYPE_ESIM;
/* eSIM status */
if (ready_state == MBIM_SUBSCRIBER_READY_STATE_INITIALIZED)
self->priv->esim_status = MM_SIM_ESIM_STATUS_WITH_PROFILES;
else if (ready_state == MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE)
self->priv->esim_status = MM_SIM_ESIM_STATUS_NO_PROFILES;
else
mm_obj_warn (self, "couldn't load esim status: %s", mbim_subscriber_ready_state_get_string (ready_state));
} else
self->priv->sim_type = MM_SIM_TYPE_PHYSICAL;
/* Removability */
if (flags & MBIM_SUBSCRIBER_READY_STATUS_FLAG_SIM_REMOVABILITY_KNOWN) {
if (flags & MBIM_SUBSCRIBER_READY_STATUS_FLAG_SIM_REMOVABLE)
self->priv->removability = MM_SIM_REMOVABILITY_REMOVABLE;
else
self->priv->removability = MM_SIM_REMOVABILITY_NOT_REMOVABLE;
}
}
} else {
if (!mbim_message_subscriber_ready_status_response_parse (
response,
&ready_state,
&self->priv->imsi,
&raw_iccid,
NULL, /* ready_info */
NULL, /* telephone_numbers_count */
NULL, /* telephone_numbers */
&self->priv->preload_error))
g_prefix_error (&self->priv->preload_error, "Failed processing subscriber ready status response: ");
else
mm_obj_dbg (self, "processed subscriber ready status response");
}
if (raw_iccid)
self->priv->iccid = mm_3gpp_parse_iccid (raw_iccid, &self->priv->iccid_error);
}
if (self->priv->preload_error || ready_state == MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED) {
self->priv->preload = FALSE;
g_clear_object (&self->priv->preload_cancellable);
if (self->priv->preload_error)
g_task_return_error (task, g_steal_pointer (&self->priv->preload_error));
else
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_SIM_STATE,
"SIM is not ready: NotInitialized");
g_object_unref (task);
return;
}
/* Go on to preload next contents */
uicc_application_list (task, device);
}
static void
subscriber_ready_status (GTask *task,
MbimDevice *device)
{
g_autoptr(MbimMessage) message = NULL;
message = mbim_message_subscriber_ready_status_query_new (NULL);
mbim_device_command (device,
message,
10,
NULL,
(GAsyncReadyCallback)subscriber_ready_status_ready,
task);
}
static void
preload_subscriber_info (MMSimMbim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GCancellable) cancellable = NULL;
GTask *task;
MbimDevice *device;
if (!peek_device (self, &device, callback, user_data))
return;
cancellable = g_cancellable_new ();
task = g_task_new (self, cancellable, callback, user_data);
/* when preloading is successful, only preload one single time;
* the info of the SIM should not change during runtime, unless
* we're handling hotplug events */
if (self->priv->preload) {
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
self->priv->preload = TRUE;
g_assert (!self->priv->preload_cancellable);
self->priv->preload_cancellable = g_steal_pointer (&cancellable);
#if defined WITH_SUSPEND_RESUME
/* If modem reports sync needed, we will reset the preloaded info */
setup_modem_sync_monitor (self);
#endif
subscriber_ready_status (task, device);
}
/*****************************************************************************/
/* Load SIM identifier */
static gchar *
load_sim_identifier_finish (MMBaseSim *_self,
GAsyncResult *res,
GError **error)
{
MMSimMbim *self = MM_SIM_MBIM (_self);
if (!preload_subscriber_info_finish (self, res, error))
return NULL;
if (self->priv->iccid_error) {
g_propagate_error (error, g_error_copy (self->priv->iccid_error));
return NULL;
}
if (self->priv->preload_error) {
g_propagate_error (error, g_error_copy (self->priv->preload_error));
return NULL;
}
if (!self->priv->iccid) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM iccid not available");
return NULL;
}
return g_strdup (self->priv->iccid);
}
static void
load_sim_identifier (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
preload_subscriber_info (MM_SIM_MBIM (self), callback, user_data);
}
/*****************************************************************************/
/* Load IMSI */
static gchar *
load_imsi_finish (MMBaseSim *_self,
GAsyncResult *res,
GError **error)
{
MMSimMbim *self = MM_SIM_MBIM (_self);
if (!preload_subscriber_info_finish (self, res, error))
return NULL;
if (self->priv->preload_error) {
g_propagate_error (error, g_error_copy (self->priv->preload_error));
return NULL;
}
if (!self->priv->imsi) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM imsi not available");
return NULL;
}
return g_strdup (self->priv->imsi);
}
static void
load_imsi (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
preload_subscriber_info (MM_SIM_MBIM (self), callback, user_data);
}
/*****************************************************************************/
/* Load SIM identifier */
static MMSimType
load_sim_type_finish (MMBaseSim *_self,
GAsyncResult *res,
GError **error)
{
MMSimMbim *self = MM_SIM_MBIM (_self);
if (!preload_subscriber_info_finish (self, res, error))
return MM_SIM_TYPE_UNKNOWN;
if (self->priv->preload_error) {
g_propagate_error (error, g_error_copy (self->priv->preload_error));
return MM_SIM_TYPE_UNKNOWN;
}
return self->priv->sim_type;
}
static void
load_sim_type (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
preload_subscriber_info (MM_SIM_MBIM (self), callback, user_data);
}
/*****************************************************************************/
/* Load eSIM status */
static MMSimEsimStatus
load_esim_status_finish (MMBaseSim *_self,
GAsyncResult *res,
GError **error)
{
MMSimMbim *self = MM_SIM_MBIM (_self);
if (!preload_subscriber_info_finish (self, res, error))
return MM_SIM_ESIM_STATUS_UNKNOWN;
if (self->priv->preload_error) {
g_propagate_error (error, g_error_copy (self->priv->preload_error));
return MM_SIM_ESIM_STATUS_UNKNOWN;
}
return self->priv->esim_status;
}
static void
load_esim_status (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
preload_subscriber_info (MM_SIM_MBIM (self), callback, user_data);
}
/*****************************************************************************/
/* Load eSIM status */
static MMSimRemovability
load_removability_finish (MMBaseSim *_self,
GAsyncResult *res,
GError **error)
{
MMSimMbim *self = MM_SIM_MBIM (_self);
if (!preload_subscriber_info_finish (self, res, error))
return MM_SIM_REMOVABILITY_UNKNOWN;
if (self->priv->preload_error) {
g_propagate_error (error, g_error_copy (self->priv->preload_error));
return MM_SIM_REMOVABILITY_UNKNOWN;
}
return self->priv->removability;
}
static void
load_removability (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
preload_subscriber_info (MM_SIM_MBIM (self), callback, user_data);
}
/*****************************************************************************/
/* Load EID */
#define UICC_STATUS_OK 144
#define EID_APDU_HEADER 5
typedef enum {
ESIM_CHECK_STEP_FIRST,
ESIM_CHECK_STEP_UICC_OPEN_CHANNEL,
ESIM_CHECK_STEP_UICC_GET_APDU,
ESIM_CHECK_STEP_UICC_CLOSE_CHANNEL,
ESIM_CHECK_STEP_LAST
} EsimCheckStep;
typedef struct {
EsimCheckStep step;
guint32 channel;
guint32 channel_grp;
gchar *eid;
GError *saved_error;
} EsimCheckContext;
static void
esim_check_context_free (EsimCheckContext *ctx)
{
g_assert (!ctx->saved_error);
g_free (ctx->eid);
g_free (ctx);
}
static gchar *
load_eid_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void esim_check_step (MbimDevice *device,
GTask *task);
static void
check_uicc_close_channel_ready (MbimDevice *device,
GAsyncResult *res,
GTask *task)
{
g_autoptr(MbimMessage) response = NULL;
g_autoptr(GError) error = NULL;
guint32 status;
EsimCheckContext *ctx;
ctx = g_task_get_task_data (task);
response = mbim_device_command_finish (device, res, &error);
if (!response ||
!mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
!mbim_message_ms_uicc_low_level_access_close_channel_response_parse (response, &status, &error)) {
/* if we have a saved error, prefer that one */
if (!ctx->saved_error)
ctx->saved_error = g_steal_pointer (&error);
} else if (status != UICC_STATUS_OK) {
/* if we have a saved error, prefer that one */
if (!ctx->saved_error)
ctx->saved_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"UICC close channel failed");
}
/* go on to next step */
ctx->step++;
esim_check_step (device, task);
}
static void
check_uicc_apdu_ready (MbimDevice *device,
GAsyncResult *res,
GTask *task)
{
g_autoptr(MbimMessage) response = NULL;
GError *error = NULL;
guint32 status;
guint32 apdu_response_size;
const guint8 *apdu_response = NULL;
EsimCheckContext *ctx;
ctx = g_task_get_task_data (task);
response = mbim_device_command_finish (device, res, &error);
if (!response ||
!mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
!mbim_message_ms_uicc_low_level_access_apdu_response_parse (
response,
&status,
&apdu_response_size,
&apdu_response,
&error))
ctx->saved_error = error;
else {
ctx->eid = mm_decode_eid ((const gchar *)(apdu_response + EID_APDU_HEADER),
apdu_response_size - EID_APDU_HEADER);
if (!ctx->eid)
ctx->saved_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
"Invalid APDU response: unable to decode EID");
}
/* always go on to the close channel step, even on error */
ctx->step++;
esim_check_step (device, task);
}
static void
check_uicc_open_channel_ready (MbimDevice *device,
GAsyncResult *res,
GTask *task)
{
g_autoptr(MbimMessage) response = NULL;
GError *error = NULL;
guint32 status;
guint32 channel;
EsimCheckContext *ctx;
ctx = g_task_get_task_data (task);
response = mbim_device_command_finish (device, res, &error);
if (!response ||
!mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
!mbim_message_ms_uicc_low_level_access_open_channel_response_parse (
response,
&status,
&channel,
NULL,
NULL,
&error)) {
ctx->saved_error = error;
ctx->step = ESIM_CHECK_STEP_LAST;
} else if (status != UICC_STATUS_OK) {
ctx->saved_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"UICC open channel failed");
ctx->step = ESIM_CHECK_STEP_LAST;
} else {
/* channel is open, from now on we'll need to always explicitly close,
* even on errors */
ctx->channel = channel;
ctx->step++;
}
esim_check_step (device, task);
}
static void
esim_check_step (MbimDevice *device,
GTask *task)
{
MMSimMbim *self;
EsimCheckContext *ctx;
g_autoptr(MbimMessage) message = NULL;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
/* Don't run new steps if we're cancelled */
if (g_task_return_error_if_cancelled (task)) {
g_object_unref (task);
return;
}
switch (ctx->step) {
case ESIM_CHECK_STEP_FIRST:
ctx->step++;
/* fall through */
case ESIM_CHECK_STEP_UICC_OPEN_CHANNEL: {
const guint8 app_id[] = {0xa0, 0x00, 0x00, 0x05, 0x59, 0x10, 0x10, 0xff,
0xff, 0xff, 0xff, 0x89, 0x00, 0x00, 0x01, 0x00};
/* Channel group is used to bundle all logical channels opened and for
* future reference to close */
ctx->channel_grp = 1;
mm_obj_dbg (self, "opening UICC channel...");
message = mbim_message_ms_uicc_low_level_access_open_channel_set_new (
sizeof (app_id),
app_id,
4, /* SelectP2Arg: Return File Control Parameters(FCP) template.
* Refer 11.1.13 of of the ETSI TS 102 221 technical specification.
*/
ctx->channel_grp,
NULL);
mbim_device_command (device,
message,
10,
NULL,
(GAsyncReadyCallback)check_uicc_open_channel_ready,
task);
return;
}
case ESIM_CHECK_STEP_UICC_GET_APDU: {
const guint8 apdu_cmd[] = {0x81, 0xe2, 0x91, 0x00, 0x06, 0xbf,
0x3e, 0x03, 0x5c, 0x01, 0x5a, 0x00};
mm_obj_dbg (self, "reading EID...");
message = mbim_message_ms_uicc_low_level_access_apdu_set_new (
ctx->channel,
MBIM_UICC_SECURE_MESSAGING_NONE,
MBIM_UICC_CLASS_BYTE_TYPE_EXTENDED,
sizeof (apdu_cmd),
apdu_cmd,
NULL);
mbim_device_command (device,
message,
10,
NULL,
(GAsyncReadyCallback)check_uicc_apdu_ready,
task);
return;
}
case ESIM_CHECK_STEP_UICC_CLOSE_CHANNEL:
mm_obj_dbg (self, "closing UICC channel...");
message = mbim_message_ms_uicc_low_level_access_close_channel_set_new (
ctx->channel,
ctx->channel_grp,
NULL);
mbim_device_command (device,
message,
10,
NULL,
(GAsyncReadyCallback)check_uicc_close_channel_ready,
task);
return;
case ESIM_CHECK_STEP_LAST:
if (ctx->saved_error)
g_task_return_error (task, g_steal_pointer (&ctx->saved_error));
else if (ctx->eid)
g_task_return_pointer (task, g_steal_pointer (&ctx->eid), g_free);
else
g_assert_not_reached ();
g_object_unref (task);
return;
default:
break;
}
g_assert_not_reached ();
}
static void
load_eid (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
MbimDevice *device;
GTask *task;
EsimCheckContext *ctx;
if (!peek_device (self, &device, callback, user_data))
return;
task = g_task_new (self, NULL, callback, user_data);
ctx = g_new0 (EsimCheckContext, 1);
ctx->step = ESIM_CHECK_STEP_FIRST;
g_task_set_task_data (task, ctx, (GDestroyNotify)esim_check_context_free);
esim_check_step (device, task);
}
/*****************************************************************************/
/* Common method to read transparent files */
typedef struct {
MbimDevice *device;
GByteArray *file_path;
} CommonReadBinaryContext;
static void
common_read_binary_context_free (CommonReadBinaryContext *ctx)
{
g_byte_array_unref (ctx->file_path);
g_object_unref (ctx->device);
g_slice_free (CommonReadBinaryContext, ctx);
}
static GByteArray *
common_read_binary_finish (MMSimMbim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
read_binary_query_ready (MbimDevice *device,
GAsyncResult *res,
GTask *task)
{
g_autoptr(MbimMessage) response = NULL;
GError *error = NULL;
const guint8 *data;
guint32 data_size;
response = mbim_device_command_finish (device, res, &error);
if (!response ||
!mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
!mbim_message_ms_uicc_low_level_access_read_binary_response_parse (
response,
NULL, /* version */
NULL, /* status_word_1 */
NULL, /* status_word_2 */
&data_size,
&data,
&error))
g_task_return_error (task, error);
else if (!data_size || !data)
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"data not available");
else
g_task_return_pointer (task,
g_byte_array_append (g_byte_array_sized_new (data_size), data, data_size),
(GDestroyNotify)g_byte_array_unref);
g_object_unref (task);
}
static void
file_status_query_ready (MbimDevice *device,
GAsyncResult *res,
GTask *task)
{
g_autoptr(MbimMessage) request = NULL;
g_autoptr(MbimMessage) response = NULL;
MMSimMbim *self;
CommonReadBinaryContext *ctx;
GError *error = NULL;
guint32 file_item_count;
guint32 file_item_size;
guint64 read_size;
self = g_task_get_source_object (task);
ctx = (CommonReadBinaryContext *) g_task_get_task_data (task);
response = mbim_device_command_finish (device, res, &error);
if (!response ||
!mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
!mbim_message_ms_uicc_low_level_access_file_status_response_parse (
response,
NULL, /* version */
NULL, /* status_word_1 */
NULL, /* status_word_2 */
NULL, /* file_accessibility */
NULL, /* file_type */
NULL, /* file_structure */
&file_item_count,
&file_item_size,
NULL, /* access_condition_read */
NULL, /* access_condition_update */
NULL, /* access_condition_activate */
NULL, /* access_condition_deactivate */
&error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
if (!file_item_size || !file_item_count) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"file contents not available");
g_object_unref (task);
return;
}
/* Fail if we attempt to read too much. UICC operations can read up to
* 255 bytes at a time, and the SIM files we try to process are all
* (or at least should be) small. Use a 64bit value to avoid overflowing
* if the modem returns weird size/count values. */
read_size = file_item_size * file_item_count;
if (read_size > 255) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"file size too big: item size %u, item count %u",
file_item_size, file_item_count);
g_object_unref (task);
return;
}
request = mbim_message_ms_uicc_low_level_access_read_binary_query_new (MS_UICC_LOW_LEVEL_SUPPORTED_VERSION,
self->priv->application_id->len,
self->priv->application_id->data,
ctx->file_path->len,
ctx->file_path->data,
0, /* read_offset */
(guint32)read_size,
NULL, /* local_pin */
0, /* data_size */
NULL, /* data */
NULL);
mbim_device_command (ctx->device,
request,
10,
NULL,
(GAsyncReadyCallback)read_binary_query_ready,
task);
}
static void
read_binary_subscriber_info_ready (MMSimMbim *self,
GAsyncResult *res,
GTask *task)
{
g_autoptr(MbimMessage) request = NULL;
CommonReadBinaryContext *ctx;
GError *error = NULL;
ctx = (CommonReadBinaryContext *) g_task_get_task_data (task);
if (!preload_subscriber_info_finish (self, res, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
if (self->priv->application_id_error) {
g_task_return_error (task, g_error_copy (self->priv->application_id_error));
g_object_unref (task);
return;
}
if (!self->priv->application_id) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"UICC application id not available");
g_object_unref (task);
return;
}
request = mbim_message_ms_uicc_low_level_access_file_status_query_new (MS_UICC_LOW_LEVEL_SUPPORTED_VERSION,
self->priv->application_id->len,
self->priv->application_id->data,
ctx->file_path->len,
ctx->file_path->data,
NULL);
mbim_device_command (ctx->device,
request,
10,
NULL,
(GAsyncReadyCallback)file_status_query_ready,
task);
}
static void
common_read_binary (MMSimMbim *self,
const guint8 *file_path,
gsize file_path_size,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
MbimDevice *device;
CommonReadBinaryContext *ctx;
if (!peek_device (self, &device, callback, user_data))
return;
task = g_task_new (self, NULL, callback, user_data);
ctx = g_slice_new0 (CommonReadBinaryContext);
ctx->device = g_object_ref (device);
ctx->file_path = g_byte_array_append (g_byte_array_sized_new (file_path_size), file_path, file_path_size);
g_task_set_task_data (task, ctx, (GDestroyNotify) common_read_binary_context_free);
preload_subscriber_info (self,
(GAsyncReadyCallback) read_binary_subscriber_info_ready,
task);
}
/*****************************************************************************/
/* 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
parent_load_operator_id_ready (MMBaseSim *self,
GAsyncResult *res,
GTask *task)
{
g_autoptr(GError) error = NULL;
gchar *value;
value = MM_BASE_SIM_CLASS (mm_sim_mbim_parent_class)->load_operator_identifier_finish (self, res, &error);
if (value) {
g_task_return_pointer (task, value, g_free);
} else {
mm_obj_dbg (self, "failed reading operator ID using AT: %s", error->message);
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Failed reading operator ID from SIM card");
}
g_object_unref (task);
}
static void
common_read_binary_operator_id_ready (MMSimMbim *self,
GAsyncResult *res,
GTask *task)
{
g_autoptr(GError) error = NULL;
GByteArray *value;
value = common_read_binary_finish (self, res, &error);
if (value) {
guint mnc_len = mm_sim_validate_mnc_length (value->data, value->len, &error);;
if (mnc_len) {
g_task_return_pointer (task, g_strndup (self->priv->imsi, 3 + mnc_len), g_free);
g_object_unref (task);
return;
}
}
mm_obj_dbg (self, "failed reading operator ID using MBIM: %s", error->message);
/* Fallback to parent implementation if possible */
MM_BASE_SIM_CLASS (mm_sim_mbim_parent_class)->load_operator_identifier (MM_BASE_SIM (self),
(GAsyncReadyCallback)parent_load_operator_id_ready,
task);
}
static void
load_operator_identifier (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
const guint8 file_path[] = { 0x7F, 0xFF, 0x6F, 0xAD };
task = g_task_new (self, NULL, callback, user_data);
common_read_binary (MM_SIM_MBIM (self),
file_path,
G_N_ELEMENTS (file_path),
(GAsyncReadyCallback)common_read_binary_operator_id_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 void
parent_load_operator_name_ready (MMBaseSim *self,
GAsyncResult *res,
GTask *task)
{
g_autoptr(GError) error = NULL;
gchar *value;
value = MM_BASE_SIM_CLASS (mm_sim_mbim_parent_class)->load_operator_name_finish (self, res, &error);
if (value) {
g_task_return_pointer (task, value, g_free);
} else {
mm_obj_dbg (self, "failed reading operator name using AT: %s", error->message);
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Failed reading operator name from SIM card");
}
g_object_unref (task);
}
static void
common_read_binary_operator_name_ready (MMSimMbim *self,
GAsyncResult *res,
GTask *task)
{
g_autoptr(GError) error = NULL;
GByteArray *value;
value = common_read_binary_finish (self, res, &error);
if (value) {
gchar *name = mm_sim_convert_spn_to_utf8 (value->data, value->len, &error);
if (name) {
g_task_return_pointer (task, name, g_free);
g_object_unref (task);
return;
}
}
mm_obj_dbg (self, "failed reading operator name using MBIM: %s", error->message);
/* Fallback to parent implementation if possible */
MM_BASE_SIM_CLASS (mm_sim_mbim_parent_class)->load_operator_name (MM_BASE_SIM (self),
(GAsyncReadyCallback)parent_load_operator_name_ready,
task);
}
static void
load_operator_name (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
const guint8 file_path[] = { 0x7F, 0xFF, 0x6F, 0x46 };
task = g_task_new (self, NULL, callback, user_data);
common_read_binary (MM_SIM_MBIM (self),
file_path,
G_N_ELEMENTS (file_path),
(GAsyncReadyCallback)common_read_binary_operator_name_ready,
task);
}
/*****************************************************************************/
/* Read GID1 */
static GByteArray *
load_gid1_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_load_gid1_ready (MMBaseSim *self,
GAsyncResult *res,
GTask *task)
{
g_autoptr(GError) error = NULL;
GByteArray *value;
value = MM_BASE_SIM_CLASS(mm_sim_mbim_parent_class)->load_gid1_finish (self, res, &error);
if (value) {
g_task_return_pointer (task, value, (GDestroyNotify)g_byte_array_unref);
} else {
mm_obj_dbg (self, "failed reading GID1 using AT: %s", error->message);
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Failed reading GID1 from SIM card");
}
g_object_unref (task);
}
static void
common_read_binary_gid1_ready (MMSimMbim *self,
GAsyncResult *res,
GTask *task)
{
g_autoptr(GError) error = NULL;
GByteArray *value;
value = common_read_binary_finish (self, res, &error);
if (value) {
g_task_return_pointer (task, value, (GDestroyNotify)g_byte_array_unref);
g_object_unref (task);
return;
}
/* Fallback to parent implementation if possible */
mm_obj_dbg (self, "failed reading GID1 using MBIM: %s", error->message);
MM_BASE_SIM_CLASS(mm_sim_mbim_parent_class)->load_gid1 (MM_BASE_SIM (self),
(GAsyncReadyCallback)parent_load_gid1_ready,
task);
}
static void
load_gid1 (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
const guint8 file_path[] = { 0x7F, 0xFF, 0x6F, 0x3E };
task = g_task_new (self, NULL, callback, user_data);
common_read_binary (MM_SIM_MBIM (self),
file_path,
G_N_ELEMENTS (file_path),
(GAsyncReadyCallback)common_read_binary_gid1_ready,
task);
}
/*****************************************************************************/
/* Read GID2 */
static GByteArray *
load_gid2_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_load_gid2_ready (MMBaseSim *self,
GAsyncResult *res,
GTask *task)
{
g_autoptr(GError) error = NULL;
GByteArray *value;
value = MM_BASE_SIM_CLASS(mm_sim_mbim_parent_class)->load_gid2_finish (self, res, &error);
if (value) {
g_task_return_pointer (task, value, (GDestroyNotify)g_byte_array_unref);
} else {
mm_obj_dbg (self, "failed reading GID2 using AT: %s", error->message);
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Failed reading GID2 from SIM card");
}
g_object_unref (task);
}
static void
common_read_binary_gid2_ready (MMSimMbim *self,
GAsyncResult *res,
GTask *task)
{
g_autoptr(GError) error = NULL;
GByteArray *value;
value = common_read_binary_finish (self, res, &error);
if (value) {
g_task_return_pointer (task, value, (GDestroyNotify)g_byte_array_unref);
g_object_unref (task);
return;
}
/* Fallback to parent implementation if possible */
mm_obj_dbg (self, "failed reading GID2 using MBIM: %s", error->message);
MM_BASE_SIM_CLASS(mm_sim_mbim_parent_class)->load_gid2 (MM_BASE_SIM (self),
(GAsyncReadyCallback)parent_load_gid2_ready,
task);
}
static void
load_gid2 (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
const guint8 file_path[] = { 0x7F, 0xFF, 0x6F, 0x3F };
task = g_task_new (self, NULL, callback, user_data);
common_read_binary (MM_SIM_MBIM (self),
file_path,
G_N_ELEMENTS (file_path),
(GAsyncReadyCallback)common_read_binary_gid2_ready,
task);
}
/*****************************************************************************/
/* Send PIN */
static gboolean
send_pin_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
pin_set_enter_ready (MbimDevice *device,
GAsyncResult *res,
GTask *task)
{
MMSimMbim *self;
GError *error = NULL;
MbimMessage *response;
gboolean success;
MbimPinType pin_type;
MbimPinState pin_state;
guint32 remaining_attempts;
self = g_task_get_source_object (task);
response = mbim_device_command_finish (device, res, &error);
if (response) {
success = mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error);
if (mbim_message_pin_response_parse (response,
&pin_type,
&pin_state,
&remaining_attempts,
NULL)) {
update_modem_unlock_retries (self, pin_type, remaining_attempts);
if (!success) {
/* Sending PIN failed, build a better error to report */
if (pin_type == MBIM_PIN_TYPE_PIN1 && pin_state == MBIM_PIN_STATE_LOCKED) {
g_error_free (error);
error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD, self);
} else if (pin_type == MBIM_PIN_TYPE_PUK1 && pin_state == MBIM_PIN_STATE_LOCKED) {
g_error_free (error);
error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK, self);
}
}
}
mbim_message_unref (response);
}
if (error) {
g_task_return_error (task, error);
} else {
g_task_return_boolean (task, TRUE);
/* Reset cached SIM subscriber info */
reset_subscriber_info (self);
}
g_object_unref (task);
}
static void
send_pin (MMBaseSim *self,
const gchar *pin,
GAsyncReadyCallback callback,
gpointer user_data)
{
MbimDevice *device;
MbimMessage *message;
GTask *task;
GError *error = NULL;
if (!peek_device (self, &device, callback, user_data))
return;
task = g_task_new (self, NULL, callback, user_data);
mm_obj_dbg (self, "sending PIN...");
message = (mbim_message_pin_set_new (
MBIM_PIN_TYPE_PIN1,
MBIM_PIN_OPERATION_ENTER,
pin,
"",
&error));
if (!message) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
mbim_device_command (device,
message,
10,
NULL,
(GAsyncReadyCallback)pin_set_enter_ready,
task);
mbim_message_unref (message);
}
/*****************************************************************************/
/* Send PUK */
static gboolean
send_puk_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
puk_set_enter_ready (MbimDevice *device,
GAsyncResult *res,
GTask *task)
{
MMSimMbim *self;
GError *error = NULL;
MbimMessage *response;
gboolean success;
MbimPinType pin_type;
MbimPinState pin_state;
guint32 remaining_attempts;
self = g_task_get_source_object (task);
response = mbim_device_command_finish (device, res, &error);
if (response) {
success = mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error);
if (mbim_message_pin_response_parse (response,
&pin_type,
&pin_state,
&remaining_attempts,
NULL)) {
update_modem_unlock_retries (self, pin_type, remaining_attempts);
if (!success) {
/* Sending PUK failed, build a better error to report */
if (pin_type == MBIM_PIN_TYPE_PUK1 && pin_state == MBIM_PIN_STATE_LOCKED) {
g_error_free (error);
if (remaining_attempts == 0)
error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG, self);
else
error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD, self);
}
}
}
mbim_message_unref (response);
}
if (error) {
g_task_return_error (task, error);
} else {
g_task_return_boolean (task, TRUE);
/* Reset cached SIM subscriber info */
reset_subscriber_info (self);
}
g_object_unref (task);
}
static void
send_puk (MMBaseSim *self,
const gchar *puk,
const gchar *new_pin,
GAsyncReadyCallback callback,
gpointer user_data)
{
MbimDevice *device;
MbimMessage *message;
GTask *task;
GError *error = NULL;
if (!peek_device (self, &device, callback, user_data))
return;
task = g_task_new (self, NULL, callback, user_data);
mm_obj_dbg (self, "sending PUK...");
message = (mbim_message_pin_set_new (
MBIM_PIN_TYPE_PUK1,
MBIM_PIN_OPERATION_ENTER,
puk,
new_pin,
&error));
if (!message) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
mbim_device_command (device,
message,
10,
NULL,
(GAsyncReadyCallback)puk_set_enter_ready,
task);
mbim_message_unref (message);
}
/*****************************************************************************/
/* Enable PIN */
static gboolean
enable_pin_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
pin_set_enable_ready (MbimDevice *device,
GAsyncResult *res,
GTask *task)
{
MMSimMbim *self;
GError *error = NULL;
MbimMessage *response;
MbimPinType pin_type;
guint32 remaining_attempts;
self = g_task_get_source_object (task);
response = mbim_device_command_finish (device, res, &error);
if (response) {
mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error);
if (mbim_message_pin_response_parse (response,
&pin_type,
NULL,
&remaining_attempts,
NULL))
update_modem_unlock_retries (self, pin_type, remaining_attempts);
mbim_message_unref (response);
}
if (error) {
if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_PIN_REQUIRED)) {
g_error_free (error);
error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Need to be unlocked to allow enabling/disabling PIN");
}
g_task_return_error (task, error);
} else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
enable_pin (MMBaseSim *self,
const gchar *pin,
gboolean enabled,
GAsyncReadyCallback callback,
gpointer user_data)
{
MbimDevice *device;
MbimMessage *message;
GTask *task;
GError *error = NULL;
if (!peek_device (self, &device, callback, user_data))
return;
task = g_task_new (self, NULL, callback, user_data);
mm_obj_dbg (self, "%s PIN ...", enabled ? "enabling" : "disabling");
message = (mbim_message_pin_set_new (
MBIM_PIN_TYPE_PIN1,
enabled ? MBIM_PIN_OPERATION_ENABLE : MBIM_PIN_OPERATION_DISABLE,
pin,
"",
&error));
if (!message) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
mbim_device_command (device,
message,
10,
NULL,
(GAsyncReadyCallback)pin_set_enable_ready,
task);
mbim_message_unref (message);
}
/*****************************************************************************/
/* Change PIN */
static gboolean
change_pin_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
pin_set_change_ready (MbimDevice *device,
GAsyncResult *res,
GTask *task)
{
MMSimMbim *self;
GError *error = NULL;
MbimMessage *response;
MbimPinType pin_type;
guint32 remaining_attempts;
self = g_task_get_source_object (task);
response = mbim_device_command_finish (device, res, &error);
if (response) {
mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error);
if (mbim_message_pin_response_parse (response,
&pin_type,
NULL,
&remaining_attempts,
NULL))
update_modem_unlock_retries (self, pin_type, remaining_attempts);
mbim_message_unref (response);
}
if (error) {
if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_PIN_REQUIRED)) {
g_error_free (error);
error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Need to be unlocked to allow changing PIN");
}
g_task_return_error (task, error);
} else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
change_pin (MMBaseSim *self,
const gchar *old_pin,
const gchar *new_pin,
GAsyncReadyCallback callback,
gpointer user_data)
{
MbimDevice *device;
MbimMessage *message;
GTask *task;
GError *error = NULL;
if (!peek_device (self, &device, callback, user_data))
return;
task = g_task_new (self, NULL, callback, user_data);
mm_obj_dbg (self, "changing PIN...");
message = (mbim_message_pin_set_new (
MBIM_PIN_TYPE_PIN1,
MBIM_PIN_OPERATION_CHANGE,
old_pin,
new_pin,
&error));
if (!message) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
mbim_device_command (device,
message,
10,
NULL,
(GAsyncReadyCallback)pin_set_change_ready,
task);
mbim_message_unref (message);
}
/*****************************************************************************/
MMBaseSim *
mm_sim_mbim_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_mbim_new (MMBaseModem *modem,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_async_initable_new_async (MM_TYPE_SIM_MBIM,
G_PRIORITY_DEFAULT,
cancellable,
callback,
user_data,
MM_BASE_SIM_MODEM, modem,
"active", TRUE, /* by default always active */
NULL);
}
MMBaseSim *
mm_sim_mbim_new_initialized (MMBaseModem *modem,
guint slot_number,
gboolean active,
MMSimType sim_type,
MMSimEsimStatus esim_status,
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_MBIM,
MM_BASE_SIM_MODEM, modem,
MM_BASE_SIM_SLOT_NUMBER, slot_number,
"active", active,
"sim-type", sim_type,
"esim-status", esim_status,
"sim-identifier", sim_identifier,
"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_mbim_init (MMSimMbim *self)
{
/* Initialize private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MM_TYPE_SIM_MBIM,
MMSimMbimPrivate);
reset_subscriber_info (self);
}
static void
finalize (GObject *object)
{
MMSimMbim *self = MM_SIM_MBIM (object);
reset_subscriber_info (self);
G_OBJECT_CLASS (mm_sim_mbim_parent_class)->finalize (object);
}
static void
dispose (GObject *object)
{
#if defined WITH_SUSPEND_RESUME
MMSimMbim *self = MM_SIM_MBIM (object);
clear_modem_sync_monitor (self);
#endif
G_OBJECT_CLASS (mm_sim_mbim_parent_class)->dispose (object);
}
static void
mm_sim_mbim_class_init (MMSimMbimClass *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 (MMSimMbimPrivate));
object_class->finalize = finalize;
object_class->dispose = dispose;
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_eid = load_eid;
base_sim_class->load_eid_finish = load_eid_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_sim_type = load_sim_type;
base_sim_class->load_sim_type_finish = load_sim_type_finish;
base_sim_class->load_esim_status = load_esim_status;
base_sim_class->load_esim_status_finish = load_esim_status_finish;
base_sim_class->load_removability = load_removability;
base_sim_class->load_removability_finish = load_removability_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->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->enable_pin = enable_pin;
base_sim_class->enable_pin_finish = enable_pin_finish;
base_sim_class->change_pin = change_pin;
base_sim_class->change_pin_finish = change_pin_finish;
}