[PATCH 2/3] sim-qmi: implement SIM verify/unblock/change/enable with UIM service

Aleksander Morgado aleksander at aleksander.es
Sat Jan 30 20:14:24 PST 2016


Newer modems like the MC7455 don't implement the "DMS UIM" commands in the
DMS service, and therefore these modems need to use the UIM service directly.

We include a new flag to detect whether any of the DMS UIM commands is flagged
as invalid, and if so, we'll fallback to the UIM specific implementations for
all.

libqmi version bump to 1.13.7, which includes the new required methods.
---
 configure.ac     |   2 +-
 src/mm-sim-qmi.c | 552 +++++++++++++++++++++++++++++++++++++++++++++++++------
 src/mm-sim-qmi.h |   2 +
 3 files changed, 500 insertions(+), 56 deletions(-)

diff --git a/configure.ac b/configure.ac
index 26711ae..91571ef 100644
--- a/configure.ac
+++ b/configure.ac
@@ -289,7 +289,7 @@ dnl-----------------------------------------------------------------------------
 dnl QMI support (enabled by default)
 dnl
 
-LIBQMI_VERSION=1.13.6
+LIBQMI_VERSION=1.13.7
 
 AC_ARG_WITH(qmi, AS_HELP_STRING([--without-qmi], [Build without QMI support]), [], [with_qmi=yes])
 AM_CONDITIONAL(WITH_QMI, test "x$with_qmi" = "xyes")
diff --git a/src/mm-sim-qmi.c b/src/mm-sim-qmi.c
index 6254cd3..ea9f2a7 100644
--- a/src/mm-sim-qmi.c
+++ b/src/mm-sim-qmi.c
@@ -11,6 +11,7 @@
  * GNU General Public License for more details:
  *
  * Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2016 Aleksander Morgado <aleksander at aleksander.es>
  */
 
 #include <config.h>
@@ -29,6 +30,10 @@
 
 G_DEFINE_TYPE (MMSimQmi, mm_sim_qmi, MM_TYPE_BASE_SIM)
 
+struct _MMSimQmiPrivate {
+    gboolean dms_uim_deprecated;
+};
+
 /*****************************************************************************/
 
 static gboolean
@@ -50,9 +55,11 @@ ensure_qmi_client (GTask       *task,
     g_object_unref (modem);
 
     if (!port) {
-        g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
-                                 "Couldn't peek QMI port");
-        g_object_unref (task);
+        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;
     }
 
@@ -60,10 +67,12 @@ ensure_qmi_client (GTask       *task,
                                       service,
                                       MM_PORT_QMI_FLAG_DEFAULT);
     if (!client) {
-        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);
+        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;
     }
 
@@ -402,18 +411,87 @@ send_pin_finish (MMBaseSim     *self,
 }
 
 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)) {
+        /* DMS UIM deprecated? */
+        if (g_error_matches (error,
+                             QMI_PROTOCOL_ERROR,
+                             QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND)) {
+            g_error_free (error);
+            qmi_message_dms_uim_verify_pin_output_unref (output);
+            /* Flag as deprecated and try with UIM */
+            self->priv->dms_uim_deprecated = TRUE;
+            uim_verify_pin (self, task);
+            return;
+        }
         g_prefix_error (&error, "Couldn't verify PIN: ");
         g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
     } else
@@ -425,40 +503,74 @@ dms_uim_verify_pin_ready (QmiClientDms *client,
 }
 
 static void
-send_pin (MMBaseSim *self,
-          const gchar *pin,
-          GAsyncReadyCallback callback,
-          gpointer user_data)
+dms_uim_verify_pin (MMSimQmi *self,
+                    GTask    *task)
 {
-    GTask *task;
     QmiMessageDmsUimVerifyPinInput *input;
     QmiClient *client = NULL;
 
-    task = g_task_new (self, NULL, callback, user_data);
-    if (!ensure_qmi_client (task,
+    if (!ensure_qmi_client (NULL,
                             MM_SIM_QMI (self),
-                            QMI_SERVICE_DMS, &client))
+                            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,
-        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,
+                                   (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,
@@ -468,18 +580,91 @@ send_puk_finish (MMBaseSim     *self,
 }
 
 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)) {
+        /* DMS UIM deprecated? */
+        if (g_error_matches (error,
+                             QMI_PROTOCOL_ERROR,
+                             QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND)) {
+            g_error_free (error);
+            qmi_message_dms_uim_unblock_pin_output_unref (output);
+            /* Flag as deprecated and try with UIM */
+            self->priv->dms_uim_deprecated = TRUE;
+            uim_unblock_pin (self, task);
+            return;
+        }
         g_prefix_error (&error, "Couldn't unblock PIN: ");
         g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
     } else
@@ -491,29 +676,30 @@ dms_uim_unblock_pin_ready (QmiClientDms *client,
 }
 
 static void
-send_puk (MMBaseSim           *self,
-          const gchar         *puk,
-          const gchar         *new_pin,
-          GAsyncReadyCallback  callback,
-          gpointer             user_data)
+dms_uim_unblock_pin (MMSimQmi *self,
+                     GTask    *task)
 {
-    GTask *task;
     QmiMessageDmsUimUnblockPinInput *input;
     QmiClient *client = NULL;
+    UnblockPinContext *ctx;
 
-    task = g_task_new (self, NULL, callback, user_data);
-    if (!ensure_qmi_client (task,
+    if (!ensure_qmi_client (NULL,
                             MM_SIM_QMI (self),
-                            QMI_SERVICE_DMS, &client))
+                            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);
 
-    mm_dbg ("Sending PUK...");
     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,
-        puk,
-        new_pin,
+        ctx->puk,
+        ctx->new_pin,
         NULL);
     qmi_client_dms_uim_unblock_pin (QMI_CLIENT_DMS (client),
                                     input,
@@ -524,9 +710,48 @@ send_puk (MMBaseSim           *self,
     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,
@@ -536,18 +761,91 @@ change_pin_finish (MMBaseSim     *self,
 }
 
 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)) {
+        /* DMS UIM deprecated? */
+        if (g_error_matches (error,
+                             QMI_PROTOCOL_ERROR,
+                             QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND)) {
+            g_error_free (error);
+            qmi_message_dms_uim_change_pin_output_unref (output);
+            /* Flag as deprecated and try with UIM */
+            self->priv->dms_uim_deprecated = TRUE;
+            uim_change_pin (self, task);
+            return;
+        }
         g_prefix_error (&error, "Couldn't change PIN: ");
         g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
     } else
@@ -559,42 +857,81 @@ dms_uim_change_pin_ready (QmiClientDms *client,
 }
 
 static void
-change_pin (MMBaseSim           *self,
-            const gchar         *old_pin,
-            const gchar         *new_pin,
-            GAsyncReadyCallback  callback,
-            gpointer             user_data)
+dms_uim_change_pin (MMSimQmi *self,
+                    GTask    *task)
 {
-    GTask *task;
     QmiMessageDmsUimChangePinInput *input;
     QmiClient *client = NULL;
+    ChangePinContext *ctx;
 
-    task = g_task_new (self, NULL, callback, user_data);
-    if (!ensure_qmi_client (task,
+    if (!ensure_qmi_client (NULL,
                             MM_SIM_QMI (self),
-                            QMI_SERVICE_DMS, &client))
+                            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);
 
-    mm_dbg ("Changing PIN...");
     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,
-        old_pin,
-        new_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,
+                                   (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,
@@ -604,18 +941,91 @@ enable_pin_finish (MMBaseSim *self,
 }
 
 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)) {
+        /* DMS UIM deprecated? */
+        if (g_error_matches (error,
+                             QMI_PROTOCOL_ERROR,
+                             QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND)) {
+            g_error_free (error);
+            qmi_message_dms_uim_set_pin_protection_output_unref (output);
+            /* Flag as deprecated and try with UIM */
+            self->priv->dms_uim_deprecated = TRUE;
+            uim_enable_pin (self, task);
+            return;
+        }
         g_prefix_error (&error, "Couldn't enable PIN: ");
         g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
     } else
@@ -627,31 +1037,30 @@ dms_uim_set_pin_protection_ready (QmiClientDms *client,
 }
 
 static void
-enable_pin (MMBaseSim           *self,
-            const gchar         *pin,
-            gboolean             enabled,
-            GAsyncReadyCallback  callback,
-            gpointer             user_data)
+dms_uim_enable_pin (MMSimQmi *self,
+                    GTask    *task)
 {
-    GTask *task;
     QmiMessageDmsUimSetPinProtectionInput *input;
     QmiClient *client = NULL;
+    EnablePinContext *ctx;
 
-    task = g_task_new (self, NULL, callback, user_data);
-    if (!ensure_qmi_client (task,
+    if (!ensure_qmi_client (NULL,
                             MM_SIM_QMI (self),
-                            QMI_SERVICE_DMS, &client))
+                            QMI_SERVICE_DMS, &client)) {
+        /* Very unlikely that this will ever happen, but anyway, try with
+         * UIM service instead */
+        uim_enable_pin (self, task);
         return;
+    }
 
-    mm_dbg ("%s PIN...",
-            enabled ? "Enabling" : "Disabling");
+    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,
-        enabled,
-        pin,
+        ctx->enabled,
+        ctx->pin,
         NULL);
     qmi_client_dms_uim_set_pin_protection (QMI_CLIENT_DMS (client),
                                            input,
@@ -662,6 +1071,32 @@ enable_pin (MMBaseSim           *self,
     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 *
@@ -702,13 +1137,20 @@ mm_sim_qmi_new (MMBaseModem *modem,
 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
 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));
+
     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;
diff --git a/src/mm-sim-qmi.h b/src/mm-sim-qmi.h
index 070f98f..c0b7621 100644
--- a/src/mm-sim-qmi.h
+++ b/src/mm-sim-qmi.h
@@ -30,9 +30,11 @@
 
 typedef struct _MMSimQmi MMSimQmi;
 typedef struct _MMSimQmiClass MMSimQmiClass;
+typedef struct _MMSimQmiPrivate MMSimQmiPrivate;
 
 struct _MMSimQmi {
     MMBaseSim parent;
+    MMSimQmiPrivate *priv;
 };
 
 struct _MMSimQmiClass {
-- 
2.7.0



More information about the ModemManager-devel mailing list