aboutsummaryrefslogtreecommitdiff
path: root/src/mm-sim.c
diff options
context:
space:
mode:
authorGuido Günther <agx@sigxcpu.org>2014-02-05 08:38:30 +0100
committerGuido Günther <agx@sigxcpu.org>2014-02-05 08:38:30 +0100
commit13ed135b9ae78c692dc359976eb8b54d0a3629b8 (patch)
treeae2ea713ad51d73980cf83db1411d6589dac5e8b /src/mm-sim.c
parent14d771b90f5a7d3887e5e900d1fb4737477ad305 (diff)
Imported Upstream version 0.7.991upstream/0.7.991
Diffstat (limited to 'src/mm-sim.c')
-rw-r--r--src/mm-sim.c1865
1 files changed, 1865 insertions, 0 deletions
diff --git a/src/mm-sim.c b/src/mm-sim.c
new file mode 100644
index 0000000..3e1ca46
--- /dev/null
+++ b/src/mm-sim.c
@@ -0,0 +1,1865 @@
+/* -*- 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) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2011 Red Hat, Inc.
+ * Copyright (C) 2011 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-iface-modem.h"
+#include "mm-sim.h"
+#include "mm-base-modem-at.h"
+#include "mm-base-modem.h"
+#include "mm-log.h"
+#include "mm-modem-helpers.h"
+#include "mm-marshal.h"
+
+static void async_initable_iface_init (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMSim, mm_sim, MM_GDBUS_TYPE_SIM_SKELETON, 0,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
+ async_initable_iface_init));
+
+enum {
+ PROP_0,
+ PROP_PATH,
+ PROP_CONNECTION,
+ PROP_MODEM,
+ PROP_LAST
+};
+
+enum {
+ SIGNAL_PIN_LOCK_ENABLED,
+ SIGNAL_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+struct _MMSimPrivate {
+ /* The connection to the system bus */
+ GDBusConnection *connection;
+ /* The modem which owns this SIM */
+ MMBaseModem *modem;
+ /* The path where the SIM object is exported */
+ gchar *path;
+};
+
+static guint signals[SIGNAL_LAST] = { 0 };
+
+/*****************************************************************************/
+
+void
+mm_sim_export (MMSim *self)
+{
+ static guint id = 0;
+ gchar *path;
+
+ path = g_strdup_printf (MM_DBUS_SIM_PREFIX "/%d", id++);
+ g_object_set (self,
+ MM_SIM_PATH, path,
+ NULL);
+ g_free (path);
+}
+
+/*****************************************************************************/
+/* CHANGE PIN (Generic implementation) */
+
+static gboolean
+change_pin_finish (MMSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+change_pin_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GSimpleAsyncResult *simple)
+{
+ GError *error = NULL;
+
+ mm_base_modem_at_command_finish (modem, res, &error);
+ if (error)
+ g_simple_async_result_take_error (simple, error);
+ else
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+change_pin (MMSim *self,
+ const gchar *old_pin,
+ const gchar *new_pin,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ gchar *command;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ change_pin);
+
+ command = g_strdup_printf ("+CPWD=\"SC\",\"%s\",\"%s\"",
+ old_pin,
+ new_pin);
+ mm_base_modem_at_command (MM_BASE_MODEM (self->priv->modem),
+ command,
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)change_pin_ready,
+ result);
+ g_free (command);
+}
+
+/*****************************************************************************/
+/* CHANGE PIN (DBus call handling) */
+
+typedef struct {
+ MMSim *self;
+ GDBusMethodInvocation *invocation;
+ gchar *old_pin;
+ gchar *new_pin;
+ GError *save_error;
+} HandleChangePinContext;
+
+static void
+handle_change_pin_context_free (HandleChangePinContext *ctx)
+{
+ g_assert (ctx->save_error == NULL);
+
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_free (ctx->old_pin);
+ g_free (ctx->new_pin);
+ g_free (ctx);
+}
+
+static void
+after_change_update_lock_info_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ HandleChangePinContext *ctx)
+{
+ /* We just want to ensure that we tried to update the unlock
+ * retries, no big issue if it failed */
+ mm_iface_modem_update_lock_info_finish (self, res, NULL);
+
+ if (ctx->save_error) {
+ g_dbus_method_invocation_take_error (ctx->invocation, ctx->save_error);
+ ctx->save_error = NULL;
+ } else {
+ mm_gdbus_sim_complete_change_pin (MM_GDBUS_SIM (ctx->self), ctx->invocation);
+ }
+
+ handle_change_pin_context_free (ctx);
+}
+
+static void
+handle_change_pin_ready (MMSim *self,
+ GAsyncResult *res,
+ HandleChangePinContext *ctx)
+{
+ MMModemLock known_lock = MM_MODEM_LOCK_UNKNOWN;
+
+ if (!MM_SIM_GET_CLASS (self)->change_pin_finish (self, res, &ctx->save_error)) {
+ if (g_error_matches (ctx->save_error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK))
+ known_lock = MM_MODEM_LOCK_SIM_PUK;
+ }
+
+ mm_iface_modem_update_lock_info (
+ MM_IFACE_MODEM (self->priv->modem),
+ known_lock,
+ (GAsyncReadyCallback)after_change_update_lock_info_ready,
+ ctx);
+}
+
+static void
+handle_change_pin_auth_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ HandleChangePinContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (modem, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_change_pin_context_free (ctx);
+ return;
+ }
+
+ /* If changing PIN is not implemented, report an error */
+ if (!MM_SIM_GET_CLASS (ctx->self)->change_pin ||
+ !MM_SIM_GET_CLASS (ctx->self)->change_pin_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot change PIN: "
+ "operation not supported");
+ handle_change_pin_context_free (ctx);
+ return;
+ }
+
+ MM_SIM_GET_CLASS (ctx->self)->change_pin (ctx->self,
+ ctx->old_pin,
+ ctx->new_pin,
+ (GAsyncReadyCallback)handle_change_pin_ready,
+ ctx);
+}
+
+static gboolean
+handle_change_pin (MMSim *self,
+ GDBusMethodInvocation *invocation,
+ const gchar *old_pin,
+ const gchar *new_pin,
+ gboolean changed)
+{
+ HandleChangePinContext *ctx;
+
+ ctx = g_new0 (HandleChangePinContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->old_pin = g_strdup (old_pin);
+ ctx->new_pin = g_strdup (new_pin);
+
+ mm_base_modem_authorize (self->priv->modem,
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_change_pin_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* ENABLE PIN (Generic implementation) */
+
+static gboolean
+enable_pin_finish (MMSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+enable_pin_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GSimpleAsyncResult *simple)
+{
+ GError *error = NULL;
+
+ mm_base_modem_at_command_finish (modem, res, &error);
+ if (error)
+ g_simple_async_result_take_error (simple, error);
+ else
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+enable_pin (MMSim *self,
+ const gchar *pin,
+ gboolean enabled,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ gchar *command;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ enable_pin);
+
+ command = g_strdup_printf ("+CLCK=\"SC\",%d,\"%s\"",
+ enabled ? 1 : 0,
+ pin);
+ mm_base_modem_at_command (MM_BASE_MODEM (self->priv->modem),
+ command,
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)enable_pin_ready,
+ result);
+ g_free (command);
+}
+
+/*****************************************************************************/
+/* ENABLE PIN (DBus call handling) */
+
+typedef struct {
+ MMSim *self;
+ GDBusMethodInvocation *invocation;
+ gchar *pin;
+ gboolean enabled;
+ GError *save_error;
+} HandleEnablePinContext;
+
+static void
+handle_enable_pin_context_free (HandleEnablePinContext *ctx)
+{
+ g_assert (ctx->save_error == NULL);
+
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_free (ctx->pin);
+ g_free (ctx);
+}
+
+static void
+after_enable_update_lock_info_ready (MMIfaceModem *modem,
+ GAsyncResult *res,
+ HandleEnablePinContext *ctx)
+{
+ /* We just want to ensure that we tried to update the unlock
+ * retries, no big issue if it failed */
+ mm_iface_modem_update_lock_info_finish (modem, res, NULL);
+
+ if (ctx->save_error) {
+ g_dbus_method_invocation_take_error (ctx->invocation, ctx->save_error);
+ ctx->save_error = NULL;
+ } else {
+ /* Signal about the new lock state */
+ g_signal_emit (ctx->self, signals[SIGNAL_PIN_LOCK_ENABLED], 0, ctx->enabled);
+ mm_gdbus_sim_complete_enable_pin (MM_GDBUS_SIM (ctx->self), ctx->invocation);
+ }
+
+ handle_enable_pin_context_free (ctx);
+}
+
+static void
+handle_enable_pin_ready (MMSim *self,
+ GAsyncResult *res,
+ HandleEnablePinContext *ctx)
+{
+ MMModemLock known_lock = MM_MODEM_LOCK_UNKNOWN;
+
+ if (!MM_SIM_GET_CLASS (self)->enable_pin_finish (self, res, &ctx->save_error)) {
+ if (g_error_matches (ctx->save_error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK))
+ known_lock = MM_MODEM_LOCK_SIM_PUK;
+ }
+
+ mm_iface_modem_update_lock_info (
+ MM_IFACE_MODEM (self->priv->modem),
+ known_lock,
+ (GAsyncReadyCallback)after_enable_update_lock_info_ready,
+ ctx);
+}
+
+static void
+handle_enable_pin_auth_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ HandleEnablePinContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (modem, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_enable_pin_context_free (ctx);
+ return;
+ }
+
+ /* If changing PIN is not implemented, report an error */
+ if (!MM_SIM_GET_CLASS (ctx->self)->enable_pin ||
+ !MM_SIM_GET_CLASS (ctx->self)->enable_pin_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot enable/disable PIN: "
+ "operation not supported");
+ handle_enable_pin_context_free (ctx);
+ return;
+ }
+
+ MM_SIM_GET_CLASS (ctx->self)->enable_pin (ctx->self,
+ ctx->pin,
+ ctx->enabled,
+ (GAsyncReadyCallback)handle_enable_pin_ready,
+ ctx);
+}
+
+static gboolean
+handle_enable_pin (MMSim *self,
+ GDBusMethodInvocation *invocation,
+ const gchar *pin,
+ gboolean enabled)
+{
+ HandleEnablePinContext *ctx;
+
+ ctx = g_new0 (HandleEnablePinContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->pin = g_strdup (pin);
+ ctx->enabled = enabled;
+
+ mm_base_modem_authorize (self->priv->modem,
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_enable_pin_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* SEND PIN/PUK (Generic implementation) */
+
+static gboolean
+common_send_pin_puk_finish (MMSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+send_pin_puk_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GSimpleAsyncResult *simple)
+{
+ GError *error = NULL;
+
+ mm_base_modem_at_command_finish (modem, res, &error);
+ if (error)
+ g_simple_async_result_take_error (simple, error);
+ else
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+common_send_pin_puk (MMSim *self,
+ const gchar *pin,
+ const gchar *puk,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ gchar *command;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ common_send_pin_puk);
+
+ command = (puk ?
+ g_strdup_printf ("+CPIN=\"%s\",\"%s\"", puk, pin) :
+ g_strdup_printf ("+CPIN=\"%s\"", pin));
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self->priv->modem),
+ command,
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)send_pin_puk_ready,
+ result);
+ g_free (command);
+}
+
+static void
+send_puk (MMSim *self,
+ const gchar *puk,
+ const gchar *new_pin,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_send_pin_puk (self, new_pin, puk, callback, user_data);
+}
+
+static void
+send_pin (MMSim *self,
+ const gchar *pin,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_send_pin_puk (self, pin, NULL, callback, user_data);
+}
+
+/*****************************************************************************/
+/* SEND PIN/PUK (common logic) */
+
+typedef struct {
+ MMSim *self;
+ GSimpleAsyncResult *result;
+ GError *save_error;
+ gulong wait_for_unlock_id;
+} SendPinPukContext;
+
+static void
+send_pin_puk_context_complete_and_free (SendPinPukContext *ctx)
+{
+ if (ctx->wait_for_unlock_id)
+ g_signal_handler_disconnect (ctx->self->priv->modem,
+ ctx->wait_for_unlock_id);
+ if (ctx->save_error)
+ g_error_free (ctx->save_error);
+ g_simple_async_result_complete (ctx->result);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->self);
+ g_free (ctx);
+}
+
+static GError *
+error_for_unlock_check (MMModemLock lock)
+{
+ static const MMMobileEquipmentError errors_for_locks [] = {
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, /* MM_MODEM_LOCK_UNKNOWN */
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, /* MM_MODEM_LOCK_NONE */
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN, /* MM_MODEM_LOCK_SIM_PIN */
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN2, /* MM_MODEM_LOCK_SIM_PIN2 */
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK, /* MM_MODEM_LOCK_SIM_PUK */
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK2, /* MM_MODEM_LOCK_SIM_PUK2 */
+ MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PIN, /* MM_MODEM_LOCK_PH_SP_PIN */
+ MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PUK, /* MM_MODEM_LOCK_PH_SP_PUK */
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PIN, /* MM_MODEM_LOCK_PH_NET_PIN */
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PUK, /* MM_MODEM_LOCK_PH_NET_PUK */
+ MM_MOBILE_EQUIPMENT_ERROR_PH_SIM_PIN, /* MM_MODEM_LOCK_PH_SIM_PIN */
+ MM_MOBILE_EQUIPMENT_ERROR_CORP_PIN, /* MM_MODEM_LOCK_PH_CORP_PIN */
+ MM_MOBILE_EQUIPMENT_ERROR_CORP_PUK, /* MM_MODEM_LOCK_PH_CORP_PUK */
+ MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PIN, /* MM_MODEM_LOCK_PH_FSIM_PIN */
+ MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PUK, /* MM_MODEM_LOCK_PH_FSIM_PUK */
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PIN, /* MM_MODEM_LOCK_PH_NETSUB_PIN */
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PUK, /* MM_MODEM_LOCK_PH_NETSUB_PUK */
+ };
+
+ g_assert (lock >= MM_MODEM_LOCK_UNKNOWN);
+ g_assert (lock <= MM_MODEM_LOCK_PH_NETSUB_PUK);
+
+ return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
+ errors_for_locks[lock],
+ "Device is locked: '%s'",
+ mm_modem_lock_get_string (lock));
+}
+
+gboolean
+mm_sim_send_pin_finish (MMSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+gboolean
+mm_sim_send_puk_finish (MMSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+update_lock_info_ready (MMIfaceModem *modem,
+ GAsyncResult *res,
+ SendPinPukContext *ctx)
+{
+ GError *error = NULL;
+ MMModemLock lock;
+
+ lock = mm_iface_modem_update_lock_info_finish (modem, res, &error);
+ /* Even if we may be SIM-PIN2/PUK2 locked, we don't consider this an error
+ * in the PIN/PUK sending */
+ if (lock != MM_MODEM_LOCK_NONE &&
+ lock != MM_MODEM_LOCK_SIM_PIN2 &&
+ lock != MM_MODEM_LOCK_SIM_PUK2) {
+ /* Device is locked. Now:
+ * - If we got an error in the original send-pin action, report it.
+ * - If we got an error in the pin-check action, report it.
+ * - Otherwise, build our own error from the lock code.
+ */
+ if (ctx->save_error) {
+ g_simple_async_result_take_error (ctx->result, ctx->save_error);
+ ctx->save_error = NULL;
+ g_clear_error (&error);
+ } else if (error)
+ g_simple_async_result_take_error (ctx->result, error);
+ else
+ g_simple_async_result_take_error (ctx->result,
+ error_for_unlock_check (lock));
+ send_pin_puk_context_complete_and_free (ctx);
+ return;
+ }
+
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ send_pin_puk_context_complete_and_free (ctx);
+}
+
+static void
+send_pin_ready (MMSim *self,
+ GAsyncResult *res,
+ SendPinPukContext *ctx)
+{
+ MMModemLock known_lock = MM_MODEM_LOCK_UNKNOWN;
+
+ if (!MM_SIM_GET_CLASS (self)->send_pin_finish (self, res, &ctx->save_error)) {
+ if (g_error_matches (ctx->save_error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK))
+ known_lock = MM_MODEM_LOCK_SIM_PUK;
+ }
+
+ /* Once pin/puk has been sent, recheck lock */
+ mm_iface_modem_update_lock_info (
+ MM_IFACE_MODEM (self->priv->modem),
+ known_lock,
+ (GAsyncReadyCallback)update_lock_info_ready,
+ ctx);
+}
+
+static void
+send_puk_ready (MMSim *self,
+ GAsyncResult *res,
+ SendPinPukContext *ctx)
+{
+ MM_SIM_GET_CLASS (self)->send_puk_finish (self, res, &ctx->save_error);
+
+ /* Once pin/puk has been sent, recheck lock */
+ mm_iface_modem_update_lock_info (MM_IFACE_MODEM (self->priv->modem),
+ MM_MODEM_LOCK_UNKNOWN, /* ask */
+ (GAsyncReadyCallback)update_lock_info_ready,
+ ctx);
+}
+
+void
+mm_sim_send_pin (MMSim *self,
+ const gchar *pin,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SendPinPukContext *ctx;
+
+ /* If sending PIN is not implemented, report an error */
+ if (!MM_SIM_GET_CLASS (self)->send_pin ||
+ !MM_SIM_GET_CLASS (self)->send_pin_finish) {
+ g_simple_async_report_error_in_idle (G_OBJECT (self),
+ callback,
+ user_data,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot send PIN: "
+ "operation not supported");
+ return;
+ }
+
+ ctx = g_new0 (SendPinPukContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ mm_sim_send_pin);
+
+ MM_SIM_GET_CLASS (self)->send_pin (self,
+ pin,
+ (GAsyncReadyCallback)send_pin_ready,
+ ctx);
+}
+
+void
+mm_sim_send_puk (MMSim *self,
+ const gchar *puk,
+ const gchar *new_pin,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SendPinPukContext *ctx;
+
+ /* If sending PIN is not implemented, report an error */
+ if (!MM_SIM_GET_CLASS (self)->send_puk ||
+ !MM_SIM_GET_CLASS (self)->send_puk_finish) {
+ g_simple_async_report_error_in_idle (G_OBJECT (self),
+ callback,
+ user_data,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot send PUK: "
+ "operation not supported");
+ return;
+ }
+
+ ctx = g_new0 (SendPinPukContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ mm_sim_send_puk);
+
+ MM_SIM_GET_CLASS (self)->send_puk (self,
+ puk,
+ new_pin,
+ (GAsyncReadyCallback)send_puk_ready,
+ ctx);
+}
+
+/*****************************************************************************/
+/* SEND PIN (DBus call handling) */
+
+typedef struct {
+ MMSim *self;
+ GDBusMethodInvocation *invocation;
+ gchar *pin;
+} HandleSendPinContext;
+
+static void
+handle_send_pin_context_free (HandleSendPinContext *ctx)
+{
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_free (ctx->pin);
+ g_free (ctx);
+}
+
+static void
+handle_send_pin_ready (MMSim *self,
+ GAsyncResult *res,
+ HandleSendPinContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_sim_send_pin_finish (self, res, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else
+ mm_gdbus_sim_complete_send_pin (MM_GDBUS_SIM (self), ctx->invocation);
+
+ handle_send_pin_context_free (ctx);
+}
+
+static void
+handle_send_pin_auth_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ HandleSendPinContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (modem, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_send_pin_context_free (ctx);
+ return;
+ }
+
+ mm_sim_send_pin (ctx->self,
+ ctx->pin,
+ (GAsyncReadyCallback)handle_send_pin_ready,
+ ctx);
+}
+
+static gboolean
+handle_send_pin (MMSim *self,
+ GDBusMethodInvocation *invocation,
+ const gchar *pin)
+{
+ HandleSendPinContext *ctx;
+
+ ctx = g_new0 (HandleSendPinContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->pin = g_strdup (pin);
+
+ mm_base_modem_authorize (self->priv->modem,
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_send_pin_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* SEND PUK (DBus call handling) */
+
+typedef struct {
+ MMSim *self;
+ GDBusMethodInvocation *invocation;
+ gchar *puk;
+ gchar *new_pin;
+} HandleSendPukContext;
+
+static void
+handle_send_puk_context_free (HandleSendPukContext *ctx)
+{
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_free (ctx->puk);
+ g_free (ctx->new_pin);
+ g_free (ctx);
+}
+
+static void
+handle_send_puk_ready (MMSim *self,
+ GAsyncResult *res,
+ HandleSendPukContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_sim_send_puk_finish (self, res, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else
+ mm_gdbus_sim_complete_send_puk (MM_GDBUS_SIM (self), ctx->invocation);
+
+ handle_send_puk_context_free (ctx);
+}
+
+static void
+handle_send_puk_auth_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ HandleSendPukContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (modem, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_send_puk_context_free (ctx);
+ return;
+ }
+
+ mm_sim_send_puk (ctx->self,
+ ctx->puk,
+ ctx->new_pin,
+ (GAsyncReadyCallback)handle_send_puk_ready,
+ ctx);
+}
+
+static gboolean
+handle_send_puk (MMSim *self,
+ GDBusMethodInvocation *invocation,
+ const gchar *puk,
+ const gchar *new_pin)
+{
+ HandleSendPukContext *ctx;
+
+ ctx = g_new0 (HandleSendPukContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->puk = g_strdup (puk);
+ ctx->new_pin = g_strdup (new_pin);
+
+ mm_base_modem_authorize (self->priv->modem,
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_send_puk_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+mm_sim_dbus_export (MMSim *self)
+{
+ GError *error = NULL;
+
+ /* Handle method invocations */
+ g_signal_connect (self,
+ "handle-change-pin",
+ G_CALLBACK (handle_change_pin),
+ NULL);
+ g_signal_connect (self,
+ "handle-enable-pin",
+ G_CALLBACK (handle_enable_pin),
+ NULL);
+ g_signal_connect (self,
+ "handle-send-pin",
+ G_CALLBACK (handle_send_pin),
+ NULL);
+ g_signal_connect (self,
+ "handle-send-puk",
+ G_CALLBACK (handle_send_puk),
+ NULL);
+
+ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self),
+ self->priv->connection,
+ self->priv->path,
+ &error)) {
+ mm_warn ("couldn't export SIM at '%s': '%s'",
+ self->priv->path,
+ error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+mm_sim_dbus_unexport (MMSim *self)
+{
+ /* Only unexport if currently exported */
+ if (g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (self)))
+ g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self));
+}
+
+/*****************************************************************************/
+
+const gchar *
+mm_sim_get_path (MMSim *self)
+{
+ return self->priv->path;
+}
+
+/*****************************************************************************/
+
+#undef STR_REPLY_READY_FN
+#define STR_REPLY_READY_FN(NAME) \
+ static void \
+ NAME##_command_ready (MMBaseModem *modem, \
+ GAsyncResult *res, \
+ GSimpleAsyncResult *operation_result) \
+ { \
+ GError *error = NULL; \
+ const gchar *response; \
+ \
+ response = mm_base_modem_at_command_finish (modem, res, &error); \
+ if (error) \
+ g_simple_async_result_take_error (operation_result, error); \
+ else \
+ g_simple_async_result_set_op_res_gpointer (operation_result, \
+ (gpointer)response, \
+ NULL); \
+ \
+ g_simple_async_result_complete (operation_result); \
+ g_object_unref (operation_result); \
+ }
+
+/*****************************************************************************/
+/* SIM IDENTIFIER */
+
+static gchar *
+parse_iccid (const gchar *response,
+ GError **error)
+{
+ gchar buf[21];
+ gchar swapped[21];
+ const gchar *str;
+ gint sw1;
+ gint sw2;
+ gboolean success = FALSE;
+
+ memset (buf, 0, sizeof (buf));
+ str = mm_strip_tag (response, "+CRSM:");
+ if (sscanf (str, "%d,%d,\"%20c\"", &sw1, &sw2, (char *) &buf) == 3)
+ success = TRUE;
+ else {
+ /* May not include quotes... */
+ if (sscanf (str, "%d,%d,%20c", &sw1, &sw2, (char *) &buf) == 3)
+ success = TRUE;
+ }
+
+ if (!success) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Could not parse the CRSM response");
+ return NULL;
+ }
+
+ if ((sw1 == 0x90 && sw2 == 0x00) ||
+ (sw1 == 0x91) ||
+ (sw1 == 0x92) ||
+ (sw1 == 0x9f)) {
+ gsize len = 0;
+ gint f_pos = -1;
+ gint i;
+
+ /* Make sure the buffer is only digits or 'F' */
+ for (len = 0; len < sizeof (buf) && buf[len]; len++) {
+ if (isdigit (buf[len]))
+ continue;
+ if (buf[len] == 'F' || buf[len] == 'f') {
+ buf[len] = 'F'; /* canonicalize the F */
+ f_pos = len;
+ continue;
+ }
+ if (buf[len] == '\"') {
+ buf[len] = 0;
+ break;
+ }
+
+ /* Invalid character */
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "CRSM ICCID response contained invalid character '%c'",
+ buf[len]);
+ return NULL;
+ }
+
+ /* BCD encoded ICCIDs are 20 digits long */
+ if (len != 20) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Invalid +CRSM ICCID response size (was %zd, expected 20)",
+ len);
+ return NULL;
+ }
+
+ /* Ensure if there's an 'F' that it's second-to-last */
+ if ((f_pos >= 0) && (f_pos != len - 2)) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Invalid +CRSM ICCID length (unexpected F)");
+ return NULL;
+ }
+
+ /* Swap digits in the EFiccid response to get the actual ICCID, each
+ * group of 2 digits is reversed in the +CRSM response. i.e.:
+ *
+ * 21436587 -> 12345678
+ */
+ memset (swapped, 0, sizeof (swapped));
+ for (i = 0; i < 10; i++) {
+ swapped[i * 2] = buf[(i * 2) + 1];
+ swapped[(i * 2) + 1] = buf[i * 2];
+ }
+
+ /* Zero out the F for 19 digit ICCIDs */
+ if (swapped[len - 1] == 'F')
+ swapped[len - 1] = 0;
+
+
+ return g_strdup (swapped);
+ } else {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
+ sw1, sw2);
+ return NULL;
+ }
+}
+
+static gchar *
+load_sim_identifier_finish (MMSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ const gchar *result;
+ gchar *sim_identifier;
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return NULL;
+ result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+
+ sim_identifier = parse_iccid (result, error);
+ if (!sim_identifier)
+ return NULL;
+
+ mm_dbg ("loaded SIM identifier: %s", sim_identifier);
+ return sim_identifier;
+}
+
+STR_REPLY_READY_FN (load_sim_identifier)
+
+static void
+load_sim_identifier (MMSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_dbg ("loading SIM identifier...");
+
+ /* READ BINARY of EFiccid (ICC Identification) ETSI TS 102.221 section 13.2 */
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self->priv->modem),
+ "+CRSM=176,12258,0,0,10",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)load_sim_identifier_command_ready,
+ g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ load_sim_identifier));
+}
+
+/*****************************************************************************/
+/* IMSI */
+
+static gchar *
+parse_imsi (const gchar *response,
+ GError **error)
+{
+ const gchar *s;
+ gint len;
+
+ g_assert (response != NULL);
+
+ for (s = mm_strip_tag (response, "+CIMI"), len = 0;
+ *s;
+ ++s, ++len) {
+ /* IMSI is a number with 15 or less decimal digits. */
+ if (!isdigit (*s) || len > 15) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Invalid +CIMI response '%s'", response ? response : "<null>");
+ return NULL;
+ }
+ }
+
+ return g_strdup (response);
+}
+
+static gchar *
+load_imsi_finish (MMSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ const gchar *result;
+ gchar *imsi;
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return NULL;
+ result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+
+ imsi = parse_imsi (result, error);
+ if (!imsi)
+ return NULL;
+
+ mm_dbg ("loaded IMSI: %s", imsi);
+ return imsi;
+}
+
+STR_REPLY_READY_FN (load_imsi)
+
+static void
+load_imsi (MMSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_dbg ("loading IMSI...");
+
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self->priv->modem),
+ "+CIMI",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)load_imsi_command_ready,
+ g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ load_imsi));
+}
+
+/*****************************************************************************/
+/* Operator ID */
+
+static guint
+parse_mnc_length (const gchar *response,
+ GError **error)
+{
+ gint sw1;
+ gint sw2;
+ gboolean success = FALSE;
+ gchar hex[51];
+
+ memset (hex, 0, sizeof (hex));
+ if (sscanf (response, "+CRSM:%d,%d,\"%50c\"", &sw1, &sw2, (char *) &hex) == 3)
+ success = TRUE;
+ else {
+ /* May not include quotes... */
+ if (sscanf (response, "+CRSM:%d,%d,%50c", &sw1, &sw2, (char *) &hex) == 3)
+ success = TRUE;
+ }
+
+ if (!success) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Could not parse the CRSM response");
+ return 0;
+ }
+
+ if ((sw1 == 0x90 && sw2 == 0x00) ||
+ (sw1 == 0x91) ||
+ (sw1 == 0x92) ||
+ (sw1 == 0x9f)) {
+ gsize buflen = 0;
+ guint32 mnc_len;
+ gchar *bin;
+
+ /* Make sure the buffer is only hex characters */
+ while (buflen < sizeof (hex) && hex[buflen]) {
+ if (!isxdigit (hex[buflen])) {
+ hex[buflen] = 0x0;
+ break;
+ }
+ buflen++;
+ }
+
+ /* Convert hex string to binary */
+ bin = mm_utils_hexstr2bin (hex, &buflen);
+ if (!bin || buflen < 4) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "SIM returned malformed response '%s'",
+ hex);
+ g_free (bin);
+ return 0;
+ }
+
+ /* MNC length is byte 4 of this SIM file */
+ mnc_len = bin[3] & 0xFF;
+ if (mnc_len == 2 || mnc_len == 3) {
+ g_free (bin);
+ return mnc_len;
+ }
+
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "SIM returned invalid MNC length %d (should be either 2 or 3)",
+ mnc_len);
+ g_free (bin);
+ return 0;
+ }
+
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
+ sw1, sw2);
+ return 0;
+}
+
+static gchar *
+load_operator_identifier_finish (MMSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ const gchar *imsi;
+ const gchar *result;
+ guint mnc_length;
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return NULL;
+ result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+
+ imsi = mm_gdbus_sim_get_imsi (MM_GDBUS_SIM (self));
+ if (!imsi) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Cannot load Operator ID without IMSI");
+ return NULL;
+ }
+
+ mnc_length = parse_mnc_length (result, &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return NULL;
+ }
+
+ /* Build Operator ID */
+ return g_strndup (imsi, 3 + mnc_length);
+}
+
+STR_REPLY_READY_FN (load_operator_identifier)
+
+static void
+load_operator_identifier (MMSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_dbg ("loading Operator ID...");
+
+ /* READ BINARY of EFad (Administrative Data) ETSI 51.011 section 10.3.18 */
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self->priv->modem),
+ "+CRSM=176,28589,0,0,4",
+ 10,
+ FALSE,
+ (GAsyncReadyCallback)load_operator_identifier_command_ready,
+ g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ load_operator_identifier));
+}
+
+/*****************************************************************************/
+/* Operator Name (Service Provider Name) */
+
+static gchar *
+parse_spn (const gchar *response,
+ GError **error)
+{
+ gint sw1;
+ gint sw2;
+ gboolean success = FALSE;
+ gchar hex[51];
+
+ memset (hex, 0, sizeof (hex));
+ if (sscanf (response, "+CRSM:%d,%d,\"%50c\"", &sw1, &sw2, (char *) &hex) == 3)
+ success = TRUE;
+ else {
+ /* May not include quotes... */
+ if (sscanf (response, "+CRSM:%d,%d,%50c", &sw1, &sw2, (char *) &hex) == 3)
+ success = TRUE;
+ }
+
+ if (!success) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Could not parse the CRSM response");
+ return NULL;
+ }
+
+ if ((sw1 == 0x90 && sw2 == 0x00) ||
+ (sw1 == 0x91) ||
+ (sw1 == 0x92) ||
+ (sw1 == 0x9f)) {
+ gsize buflen = 0;
+ gchar *bin;
+ gchar *utf8;
+
+ /* Make sure the buffer is only hex characters */
+ while (buflen < sizeof (hex) && hex[buflen]) {
+ if (!isxdigit (hex[buflen])) {
+ hex[buflen] = 0x0;
+ break;
+ }
+ buflen++;
+ }
+
+ /* Convert hex string to binary */
+ bin = mm_utils_hexstr2bin (hex, &buflen);
+ if (!bin) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "SIM returned malformed response '%s'",
+ hex);
+ return NULL;
+ }
+
+ /* Remove the FF filler at the end */
+ while (buflen > 1 && bin[buflen - 1] == (char)0xff)
+ buflen--;
+
+ /* First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */
+ utf8 = (gchar *)mm_charset_gsm_unpacked_to_utf8 ((guint8 *)bin + 1, buflen - 1);
+ g_free (bin);
+ return utf8;
+ }
+
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
+ sw1, sw2);
+ return NULL;
+}
+
+static gchar *
+load_operator_name_finish (MMSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ const gchar *result;
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return NULL;
+ result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+
+ return parse_spn (result, error);
+}
+
+STR_REPLY_READY_FN (load_operator_name)
+
+static void
+load_operator_name (MMSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_dbg ("loading Operator Name...");
+
+ /* READ BINARY of EFspn (Service Provider Name) ETSI 51.011 section 10.3.11 */
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self->priv->modem),
+ "+CRSM=176,28486,0,0,17",
+ 10,
+ FALSE,
+ (GAsyncReadyCallback)load_operator_name_command_ready,
+ g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ load_operator_name));
+}
+
+/*****************************************************************************/
+
+typedef struct _InitAsyncContext InitAsyncContext;
+static void interface_initialization_step (InitAsyncContext *ctx);
+
+typedef enum {
+ INITIALIZATION_STEP_FIRST,
+ INITIALIZATION_STEP_SIM_IDENTIFIER,
+ INITIALIZATION_STEP_IMSI,
+ INITIALIZATION_STEP_OPERATOR_ID,
+ INITIALIZATION_STEP_OPERATOR_NAME,
+ INITIALIZATION_STEP_LAST
+} InitializationStep;
+
+struct _InitAsyncContext {
+ GSimpleAsyncResult *result;
+ GCancellable *cancellable;
+ MMSim *self;
+ InitializationStep step;
+ guint sim_identifier_tries;
+};
+
+static void
+init_async_context_free (InitAsyncContext *ctx)
+{
+ g_object_unref (ctx->self);
+ g_object_unref (ctx->result);
+ if (ctx->cancellable)
+ g_object_unref (ctx->cancellable);
+ g_free (ctx);
+}
+
+MMSim *
+mm_sim_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_sim_export (MM_SIM (sim));
+
+ return MM_SIM (sim);
+}
+
+static gboolean
+initable_init_finish (GAsyncInitable *initable,
+ GAsyncResult *result,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+}
+
+static void
+load_sim_identifier_ready (MMSim *self,
+ GAsyncResult *res,
+ InitAsyncContext *ctx)
+{
+ GError *error = NULL;
+ gchar *simid;
+
+ simid = MM_SIM_GET_CLASS (ctx->self)->load_sim_identifier_finish (self, res, &error);
+ if (!simid) {
+ /* TODO: make the retries gobi-specific? */
+
+ /* Try one more time... Gobi 1K cards may reply to the first
+ * request with '+CRSM: 106,134,""' which is bogus because
+ * subsequent requests work fine.
+ */
+ if (++ctx->sim_identifier_tries < 2) {
+ g_clear_error (&error);
+ interface_initialization_step (ctx);
+ return;
+ }
+
+ mm_warn ("couldn't load SIM identifier: '%s'",
+ error ? error->message : "unknown error");
+ g_clear_error (&error);
+ }
+
+ mm_gdbus_sim_set_sim_identifier (MM_GDBUS_SIM (self), simid);
+ g_free (simid);
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_initialization_step (ctx);
+}
+
+#undef STR_REPLY_READY_FN
+#define STR_REPLY_READY_FN(NAME,DISPLAY) \
+ static void \
+ load_##NAME##_ready (MMSim *self, \
+ GAsyncResult *res, \
+ InitAsyncContext *ctx) \
+ { \
+ GError *error = NULL; \
+ gchar *val; \
+ \
+ val = MM_SIM_GET_CLASS (ctx->self)->load_##NAME##_finish (self, res, &error); \
+ mm_gdbus_sim_set_##NAME (MM_GDBUS_SIM (self), val); \
+ g_free (val); \
+ \
+ if (error) { \
+ mm_warn ("couldn't load %s: '%s'", DISPLAY, error->message); \
+ g_error_free (error); \
+ } \
+ \
+ /* Go on to next step */ \
+ ctx->step++; \
+ interface_initialization_step (ctx); \
+ }
+
+STR_REPLY_READY_FN (imsi, "IMSI")
+STR_REPLY_READY_FN (operator_identifier, "Operator identifier")
+STR_REPLY_READY_FN (operator_name, "Operator name")
+
+static void
+interface_initialization_step (InitAsyncContext *ctx)
+{
+ switch (ctx->step) {
+ case INITIALIZATION_STEP_FIRST:
+ /* Fall down to next step */
+ ctx->step++;
+
+ case INITIALIZATION_STEP_SIM_IDENTIFIER:
+ /* SIM ID is meant to be loaded only once during the whole
+ * lifetime of the modem. Therefore, if we already have them loaded,
+ * don't try to load them again. */
+ if (mm_gdbus_sim_get_sim_identifier (MM_GDBUS_SIM (ctx->self)) == NULL &&
+ MM_SIM_GET_CLASS (ctx->self)->load_sim_identifier &&
+ MM_SIM_GET_CLASS (ctx->self)->load_sim_identifier_finish) {
+ MM_SIM_GET_CLASS (ctx->self)->load_sim_identifier (
+ ctx->self,
+ (GAsyncReadyCallback)load_sim_identifier_ready,
+ ctx);
+ return;
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
+ case INITIALIZATION_STEP_IMSI:
+ /* IMSI is meant to be loaded only once during the whole
+ * lifetime of the modem. Therefore, if we already have them loaded,
+ * don't try to load them again. */
+ if (mm_gdbus_sim_get_imsi (MM_GDBUS_SIM (ctx->self)) == NULL &&
+ MM_SIM_GET_CLASS (ctx->self)->load_imsi &&
+ MM_SIM_GET_CLASS (ctx->self)->load_imsi_finish) {
+ MM_SIM_GET_CLASS (ctx->self)->load_imsi (
+ ctx->self,
+ (GAsyncReadyCallback)load_imsi_ready,
+ ctx);
+ return;
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
+ case INITIALIZATION_STEP_OPERATOR_ID:
+ /* Operator ID is meant to be loaded only once during the whole
+ * lifetime of the modem. Therefore, if we already have them loaded,
+ * don't try to load them again. */
+ if (mm_gdbus_sim_get_operator_identifier (MM_GDBUS_SIM (ctx->self)) == NULL &&
+ MM_SIM_GET_CLASS (ctx->self)->load_operator_identifier &&
+ MM_SIM_GET_CLASS (ctx->self)->load_operator_identifier_finish) {
+ MM_SIM_GET_CLASS (ctx->self)->load_operator_identifier (
+ ctx->self,
+ (GAsyncReadyCallback)load_operator_identifier_ready,
+ ctx);
+ return;
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
+ case INITIALIZATION_STEP_OPERATOR_NAME:
+ /* Operator Name is meant to be loaded only once during the whole
+ * lifetime of the modem. Therefore, if we already have them loaded,
+ * don't try to load them again. */
+ if (mm_gdbus_sim_get_operator_name (MM_GDBUS_SIM (ctx->self)) == NULL &&
+ MM_SIM_GET_CLASS (ctx->self)->load_operator_name &&
+ MM_SIM_GET_CLASS (ctx->self)->load_operator_name_finish) {
+ MM_SIM_GET_CLASS (ctx->self)->load_operator_name (
+ ctx->self,
+ (GAsyncReadyCallback)load_operator_name_ready,
+ ctx);
+ return;
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
+ case INITIALIZATION_STEP_LAST:
+ /* We are done without errors! */
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_simple_async_result_complete_in_idle (ctx->result);
+ init_async_context_free (ctx);
+ return;
+ }
+
+
+ g_assert_not_reached ();
+}
+
+static void
+common_init_async (GAsyncInitable *initable,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+
+{
+ InitAsyncContext *ctx;
+
+ ctx = g_new (InitAsyncContext, 1);
+ ctx->self = g_object_ref (initable);
+ ctx->result = g_simple_async_result_new (G_OBJECT (initable),
+ callback,
+ user_data,
+ common_init_async);
+ ctx->cancellable = (cancellable ?
+ g_object_ref (cancellable) :
+ NULL);
+ ctx->step = INITIALIZATION_STEP_FIRST;
+ ctx->sim_identifier_tries = 0;
+
+ interface_initialization_step (ctx);
+}
+
+static void
+initable_init_async (GAsyncInitable *initable,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_gdbus_sim_set_sim_identifier (MM_GDBUS_SIM (initable), NULL);
+ mm_gdbus_sim_set_imsi (MM_GDBUS_SIM (initable), NULL);
+ mm_gdbus_sim_set_operator_identifier (MM_GDBUS_SIM (initable), NULL);
+ mm_gdbus_sim_set_operator_name (MM_GDBUS_SIM (initable), NULL);
+
+ common_init_async (initable, cancellable, callback, user_data);
+}
+
+void
+mm_sim_new (MMBaseModem *modem,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_async_initable_new_async (MM_TYPE_SIM,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ callback,
+ user_data,
+ MM_SIM_MODEM, modem,
+ NULL);
+}
+
+gboolean
+mm_sim_initialize_finish (MMSim *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ return initable_init_finish (G_ASYNC_INITABLE (self), result, error);
+}
+
+void
+mm_sim_initialize (MMSim *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_init_async (G_ASYNC_INITABLE (self),
+ cancellable,
+ callback,
+ user_data);
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MMSim *self = MM_SIM (object);
+
+ switch (prop_id) {
+ case PROP_PATH:
+ g_free (self->priv->path);
+ self->priv->path = g_value_dup_string (value);
+
+ /* Export when we get a DBus connection AND we have a path */
+ if (self->priv->path &&
+ self->priv->connection)
+ mm_sim_dbus_export (self);
+ break;
+ case PROP_CONNECTION:
+ g_clear_object (&self->priv->connection);
+ self->priv->connection = g_value_dup_object (value);
+
+ /* Export when we get a DBus connection AND we have a path */
+ if (!self->priv->connection)
+ mm_sim_dbus_unexport (self);
+ else if (self->priv->path)
+ mm_sim_dbus_export (self);
+ break;
+ case PROP_MODEM:
+ g_clear_object (&self->priv->modem);
+ self->priv->modem = g_value_dup_object (value);
+ if (self->priv->modem) {
+ /* Bind the modem's connection (which is set when it is exported,
+ * and unset when unexported) to the SIM's connection */
+ g_object_bind_property (self->priv->modem, MM_BASE_MODEM_CONNECTION,
+ self, MM_SIM_CONNECTION,
+ G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+ }
+ 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)
+{
+ MMSim *self = MM_SIM (object);
+
+ switch (prop_id) {
+ case PROP_PATH:
+ g_value_set_string (value, self->priv->path);
+ break;
+ case PROP_CONNECTION:
+ g_value_set_object (value, self->priv->connection);
+ break;
+ case PROP_MODEM:
+ g_value_set_object (value, self->priv->modem);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+mm_sim_init (MMSim *self)
+{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
+ MM_TYPE_SIM,
+ MMSimPrivate);
+}
+
+static void
+finalize (GObject *object)
+{
+ MMSim *self = MM_SIM (object);
+
+ g_free (self->priv->path);
+
+ G_OBJECT_CLASS (mm_sim_parent_class)->finalize (object);
+}
+
+static void
+dispose (GObject *object)
+{
+ MMSim *self = MM_SIM (object);
+
+ if (self->priv->connection) {
+ /* If we arrived here with a valid connection, make sure we unexport
+ * the object */
+ mm_sim_dbus_unexport (self);
+ g_clear_object (&self->priv->connection);
+ }
+
+ g_clear_object (&self->priv->modem);
+
+ G_OBJECT_CLASS (mm_sim_parent_class)->dispose (object);
+}
+
+static void
+async_initable_iface_init (GAsyncInitableIface *iface)
+{
+ iface->init_async = initable_init_async;
+ iface->init_finish = initable_init_finish;
+}
+
+static void
+mm_sim_class_init (MMSimClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMSimPrivate));
+
+ /* Virtual methods */
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ object_class->finalize = finalize;
+ object_class->dispose = dispose;
+
+ klass->load_sim_identifier = load_sim_identifier;
+ klass->load_sim_identifier_finish = load_sim_identifier_finish;
+ klass->load_imsi = load_imsi;
+ klass->load_imsi_finish = load_imsi_finish;
+ klass->load_operator_identifier = load_operator_identifier;
+ klass->load_operator_identifier_finish = load_operator_identifier_finish;
+ klass->load_operator_name = load_operator_name;
+ klass->load_operator_name_finish = load_operator_name_finish;
+ klass->send_pin = send_pin;
+ klass->send_pin_finish = common_send_pin_puk_finish;
+ klass->send_puk = send_puk;
+ klass->send_puk_finish = common_send_pin_puk_finish;
+ klass->enable_pin = enable_pin;
+ klass->enable_pin_finish = enable_pin_finish;
+ klass->change_pin = change_pin;
+ klass->change_pin_finish = change_pin_finish;
+
+ properties[PROP_CONNECTION] =
+ g_param_spec_object (MM_SIM_CONNECTION,
+ "Connection",
+ "GDBus connection to the system bus.",
+ G_TYPE_DBUS_CONNECTION,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_CONNECTION, properties[PROP_CONNECTION]);
+
+ properties[PROP_PATH] =
+ g_param_spec_string (MM_SIM_PATH,
+ "Path",
+ "DBus path of the SIM",
+ NULL,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_PATH, properties[PROP_PATH]);
+
+ properties[PROP_MODEM] =
+ g_param_spec_object (MM_SIM_MODEM,
+ "Modem",
+ "The Modem which owns this SIM",
+ MM_TYPE_BASE_MODEM,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]);
+
+ /* Signals */
+ signals[SIGNAL_PIN_LOCK_ENABLED] =
+ g_signal_new (MM_SIM_PIN_LOCK_ENABLED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMSimClass, pin_lock_enabled),
+ NULL, NULL,
+ mm_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+}