Files
ModemManager/src/mm-sim-qmi.c
T
Aleksander Morgado 18c8dd60a7 qmi: just one InvalidCommand error is enough to avoid all DMS UIM commands
The MMBroadbandModemQmi will not create a MMSimQmi until the unlock status has
been checked, and therefore this means that when the SIM object is being created
we already know whether the modem supports DMS UIM commands or not, so avoid
further fallbacks in the SIM object.
2016-02-11 10:52:09 +01:00

1184 lines
36 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>
*/
#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-log.h"
#include "mm-sim-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;
};
/*****************************************************************************/
static gboolean
ensure_qmi_client (GTask *task,
MMSimQmi *self,
QmiService service,
QmiClient **o_client)
{
MMBaseModem *modem = NULL;
QmiClient *client;
MMPortQmi *port;
g_object_get (self,
MM_BASE_SIM_MODEM, &modem,
NULL);
g_assert (MM_IS_BASE_MODEM (modem));
port = mm_base_modem_peek_port_qmi (modem);
g_object_unref (modem);
if (!port) {
if (task) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Couldn't peek QMI port");
g_object_unref (task);
}
return FALSE;
}
client = mm_port_qmi_peek_client (port,
service,
MM_PORT_QMI_FLAG_DEFAULT);
if (!client) {
if (task) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Couldn't peek client for service '%s'",
qmi_service_get_string (service));
g_object_unref (task);
}
return FALSE;
}
*o_client = client;
return TRUE;
}
/*****************************************************************************/
/* Load SIM ID (ICCID) */
static gchar *
load_sim_identifier_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
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), (GDestroyNotify) g_free);
}
if (output)
qmi_message_dms_uim_get_iccid_output_unref (output);
g_object_unref (task);
}
static void
load_sim_identifier (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_DMS, &client))
return;
mm_dbg ("loading SIM identifier...");
qmi_client_dms_uim_get_iccid (QMI_CLIENT_DMS (client),
NULL,
5,
NULL,
(GAsyncReadyCallback)dms_uim_get_iccid_ready,
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
dms_uim_get_imsi_ready (QmiClientDms *client,
GAsyncResult *res,
GTask *task)
{
QmiMessageDmsUimGetImsiOutput *output = NULL;
GError *error = NULL;
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);
g_task_return_pointer (task, g_strdup (str), (GDestroyNotify) g_free);
}
if (output)
qmi_message_dms_uim_get_imsi_output_unref (output);
g_object_unref (task);
}
static void
load_imsi (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_DMS, &client))
return;
mm_dbg ("loading IMSI...");
qmi_client_dms_uim_get_imsi (QMI_CLIENT_DMS (client),
NULL,
5,
NULL,
(GAsyncReadyCallback)dms_uim_get_imsi_ready,
task);
}
/*****************************************************************************/
/* Load operator identifier */
static gboolean
get_home_network (QmiClientNas *client,
GAsyncResult *res,
guint16 *out_mcc,
guint16 *out_mnc,
gboolean *out_mnc_with_pcs,
gchar **out_operator_name,
GError **error)
{
QmiMessageNasGetHomeNetworkOutput *output = NULL;
gboolean success = FALSE;
output = qmi_client_nas_get_home_network_finish (client, res, error);
if (!output) {
g_prefix_error (error, "QMI operation failed: ");
} else if (!qmi_message_nas_get_home_network_output_get_result (output, error)) {
g_prefix_error (error, "Couldn't get home network: ");
} else {
const gchar *name = NULL;
qmi_message_nas_get_home_network_output_get_home_network (
output,
out_mcc,
out_mnc,
&name,
NULL);
if (out_operator_name)
*out_operator_name = g_strdup (name);
if (out_mnc_with_pcs) {
gboolean is_3gpp;
gboolean mnc_includes_pcs_digit;
if (qmi_message_nas_get_home_network_output_get_home_network_3gpp_mnc (
output,
&is_3gpp,
&mnc_includes_pcs_digit,
NULL) &&
is_3gpp &&
mnc_includes_pcs_digit) {
/* MNC should include PCS digit */
*out_mnc_with_pcs = TRUE;
} else {
/* We default to NO PCS digit, unless of course the MNC is already > 99 */
*out_mnc_with_pcs = FALSE;
}
}
success = TRUE;
}
if (output)
qmi_message_nas_get_home_network_output_unref (output);
return success;
}
static gchar *
load_operator_identifier_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
load_operator_identifier_ready (QmiClientNas *client,
GAsyncResult *res,
GTask *task)
{
guint16 mcc, mnc;
gboolean mnc_with_pcs;
GError *error = NULL;
GString *aux;
if (!get_home_network (client, res, &mcc, &mnc, &mnc_with_pcs, NULL, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
aux = g_string_new ("");
/* MCC always 3 digits */
g_string_append_printf (aux, "%.3" G_GUINT16_FORMAT, mcc);
/* Guess about MNC, if < 100 assume it's 2 digits, no PCS info here */
if (mnc >= 100 || mnc_with_pcs)
g_string_append_printf (aux, "%.3" G_GUINT16_FORMAT, mnc);
else
g_string_append_printf (aux, "%.2" G_GUINT16_FORMAT, mnc);
g_task_return_pointer (task, g_string_free (aux, FALSE), (GDestroyNotify) g_free);
g_object_unref (task);
}
static void
load_operator_identifier (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_dbg ("loading SIM operator identifier...");
qmi_client_nas_get_home_network (QMI_CLIENT_NAS (client),
NULL,
5,
NULL,
(GAsyncReadyCallback)load_operator_identifier_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
load_operator_name_ready (QmiClientNas *client,
GAsyncResult *res,
GTask *task)
{
gchar *operator_name = NULL;
GError *error = NULL;
if (!get_home_network (client, res, NULL, NULL, NULL, &operator_name, &error))
g_task_return_error (task, error);
else
g_task_return_pointer (task, operator_name, (GDestroyNotify) g_free);
g_object_unref (task);
}
static void
load_operator_name (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_dbg ("loading SIM operator name...");
qmi_client_nas_get_home_network (QMI_CLIENT_NAS (client),
NULL,
5,
NULL,
(GAsyncReadyCallback)load_operator_name_ready,
task);
}
/*****************************************************************************/
/* 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;
if (!ensure_qmi_client (task,
MM_SIM_QMI (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);
qmi_message_uim_verify_pin_input_set_session_information (
input,
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
"", /* ignored */
NULL);
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;
MMSimQmi *self;
self = g_task_get_source_object (task);
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,
MM_SIM_QMI (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_dbg ("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), (GDestroyNotify) g_free);
mm_dbg ("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;
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_unblock_pin_input_new ();
qmi_message_uim_unblock_pin_input_set_info (
input,
QMI_UIM_PIN_ID_PIN1,
ctx->puk,
ctx->new_pin,
NULL);
qmi_message_uim_unblock_pin_input_set_session_information (
input,
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
"", /* ignored */
NULL);
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;
MMSimQmi *self;
self = g_task_get_source_object (task);
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,
MM_SIM_QMI (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_dbg ("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;
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_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);
qmi_message_uim_change_pin_input_set_session_information (
input,
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
"", /* ignored */
NULL);
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;
MMSimQmi *self;
self = g_task_get_source_object (task);
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,
MM_SIM_QMI (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_dbg ("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;
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);
qmi_message_uim_set_pin_protection_input_set_session_information (
input,
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
"", /* ignored */
NULL);
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;
MMSimQmi *self;
self = g_task_get_source_object (task);
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_dbg ("%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,
NULL);
}
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
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;
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->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]);
}