[PATCH v3] Telit plugin: add load_unlock_retries interface

Carlo Lobrano c.lobrano at gmail.com
Wed Dec 16 06:38:58 PST 2015


From: Carlo Lobrano <c.lobrano at gmail.com>

---
 plugins/Makefile.am                               |  18 ++-
 plugins/telit/mm-broadband-modem-telit.c          | 183 ++++++++++++++++++++++
 plugins/telit/mm-modem-helpers-telit.c            |  75 +++++++++
 plugins/telit/mm-modem-helpers-telit.h            |  25 +++
 plugins/telit/tests/test-mm-modem-helpers-telit.c | 104 ++++++++++++
 5 files changed, 404 insertions(+), 1 deletion(-)
 create mode 100644 plugins/telit/mm-modem-helpers-telit.c
 create mode 100644 plugins/telit/mm-modem-helpers-telit.h
 create mode 100644 plugins/telit/tests/test-mm-modem-helpers-telit.c

diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index b4a76fa..59b1bff 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -550,11 +550,27 @@ libmm_plugin_telit_la_SOURCES = \
 	telit/mm-plugin-telit.c \
 	telit/mm-plugin-telit.h \
 	telit/mm-broadband-modem-telit.c \
-	telit/mm-broadband-modem-telit.h
+	telit/mm-broadband-modem-telit.h \
+	telit/mm-modem-helpers-telit.c \
+	telit/mm-modem-helpers-telit.h
 libmm_plugin_telit_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
 libmm_plugin_telit_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
 udevrules_DATA += telit/77-mm-telit-port-types.rules
 
+noinst_PROGRAMS += test-modem-helpers-telit
+test_modem_helpers_telit_SOURCES = \
+	telit/mm-modem-helpers-telit.c \
+	telit/mm-modem-helpers-telit.h \
+	telit/tests/test-mm-modem-helpers-telit.c
+test_modem_helpers_telit_CPPFLAGS = \
+	-I$(top_srcdir)/plugins/telit \
+	$(PLUGIN_COMMON_COMPILER_FLAGS)
+test_modem_helpers_telit_LDADD = \
+       $(top_builddir)/libmm-glib/libmm-glib.la \
+       $(top_builddir)/src/libmodem-helpers.la
+test_modem_helpers_telit_LDADD = $(top_builddir)/libmm-glib/libmm-glib.la
+test_modem_helpers_telit_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
 # MTK
 libmm_plugin_mtk_la_SOURCES = \
 	mtk/mm-plugin-mtk.c \
diff --git a/plugins/telit/mm-broadband-modem-telit.c b/plugins/telit/mm-broadband-modem-telit.c
index 0f77231..4121ac3 100644
--- a/plugins/telit/mm-broadband-modem-telit.c
+++ b/plugins/telit/mm-broadband-modem-telit.c
@@ -31,6 +31,7 @@
 #include "mm-iface-modem.h"
 #include "mm-iface-modem-3gpp.h"
 #include "mm-broadband-modem-telit.h"
+#include "mm-modem-helpers-telit.h"
 
 static void iface_modem_init (MMIfaceModem *iface);
 static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
@@ -40,6 +41,186 @@ G_DEFINE_TYPE_EXTENDED (MMBroadbandModemTelit, mm_broadband_modem_telit, MM_TYPE
                         G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init));
 
 /*****************************************************************************/
+/* Load unlock retries (Modem interface) */
+
+#define CSIM_QUERY_PIN_RETRIES_STR  "+CSIM=10,0020000100"
+#define CSIM_QUERY_PUK_RETRIES_STR  "+CSIM=10,002C000100"
+#define CSIM_QUERY_PIN2_RETRIES_STR "+CSIM=10,0020008100"
+#define CSIM_QUERY_PUK2_RETRIES_STR "+CSIM=10,002C008100"
+#define CSIM_QUERY_TIMEOUT 3
+
+typedef enum {
+    LOAD_UNLOCK_RETRIES_STEP_FIRST,
+    LOAD_UNLOCK_RETRIES_STEP_PIN,
+    LOAD_UNLOCK_RETRIES_STEP_PUK,
+    LOAD_UNLOCK_RETRIES_STEP_PIN2,
+    LOAD_UNLOCK_RETRIES_STEP_PUK2,
+    LOAD_UNLOCK_RETRIES_STEP_LAST
+} LoadUnlockRetriesStep;
+
+typedef struct {
+    MMBroadbandModemTelit* self;
+    GSimpleAsyncResult* result;
+    MMUnlockRetries* retries;
+    LoadUnlockRetriesStep step;
+    guint succeded_requests;
+} LoadUnlockRetriesContext;
+
+static void load_unlock_retries_step (LoadUnlockRetriesContext* ctx);
+
+static void
+load_unlock_retries_context_complete_and_free (LoadUnlockRetriesContext* ctx)
+{
+    g_simple_async_result_complete (ctx->result);
+    g_object_unref (ctx->retries);
+    g_object_unref (ctx->result);
+    g_object_unref (ctx->self);
+    g_slice_free (LoadUnlockRetriesContext, ctx);
+}
+
+static MMUnlockRetries *
+modem_load_unlock_retries_finish (MMIfaceModem *self,
+                                  GAsyncResult *res,
+                                  GError **error)
+{
+    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+        return NULL;
+
+    return (MMUnlockRetries*) g_object_ref (g_simple_async_result_get_op_res_gpointer (
+                                                G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+static void
+csim_query_ready (MMBaseModem *self,
+                  GAsyncResult* res,
+                  LoadUnlockRetriesContext* ctx)
+{
+    const gchar *response;
+    gint unlock_retries;
+    GError *error = NULL;
+
+    response = mm_base_modem_at_command_finish (self, res, &error);
+
+    if (!response) {
+        mm_warn ("No respose for step %d: %s", ctx->step, error->message);
+        g_error_free (error);
+        goto next_step;
+    }
+
+    if ( (unlock_retries = parse_csim_response (ctx->step, response, &error)) < 0) {
+        mm_warn ("Parse error in step %d: %s.", ctx->step, error->message);
+        g_error_free (error);
+        goto next_step;
+    }
+
+    ctx->succeded_requests++;
+
+    switch (ctx->step) {
+        case LOAD_UNLOCK_RETRIES_STEP_PIN:
+            mm_dbg ("PIN unlock retries left: %d", unlock_retries);
+            mm_unlock_retries_set (ctx->retries, MM_MODEM_LOCK_SIM_PIN, unlock_retries);
+            break;
+        case LOAD_UNLOCK_RETRIES_STEP_PUK:
+            mm_dbg ("PUK unlock retries left: %d", unlock_retries);
+            mm_unlock_retries_set (ctx->retries, MM_MODEM_LOCK_SIM_PUK, unlock_retries);
+            break;
+        case LOAD_UNLOCK_RETRIES_STEP_PIN2:
+            mm_dbg ("PIN2 unlock retries left: %d", unlock_retries);
+            mm_unlock_retries_set (ctx->retries, MM_MODEM_LOCK_SIM_PIN2, unlock_retries);
+            break;
+        case LOAD_UNLOCK_RETRIES_STEP_PUK2:
+            mm_dbg ("PUK2 unlock retries left: %d", unlock_retries);
+            mm_unlock_retries_set (ctx->retries, MM_MODEM_LOCK_SIM_PUK2, unlock_retries);
+            break;
+        default:
+            break;
+    }
+
+next_step:
+    ctx->step++;
+    load_unlock_retries_step (ctx);
+}
+
+static void
+load_unlock_retries_step (LoadUnlockRetriesContext* ctx)
+{
+    switch (ctx->step) {
+        case LOAD_UNLOCK_RETRIES_STEP_FIRST:
+            /* Fall back on next step */
+            ctx->step++;
+        case LOAD_UNLOCK_RETRIES_STEP_PIN:
+            mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+                                      CSIM_QUERY_PIN_RETRIES_STR,
+                                      CSIM_QUERY_TIMEOUT,
+                                      FALSE,
+                                      (GAsyncReadyCallback) csim_query_ready,
+                                      ctx);
+            break;
+        case LOAD_UNLOCK_RETRIES_STEP_PUK:
+            mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+                                      CSIM_QUERY_PUK_RETRIES_STR,
+                                      CSIM_QUERY_TIMEOUT,
+                                      FALSE,
+                                      (GAsyncReadyCallback) csim_query_ready,
+                                      ctx);
+            break;
+        case LOAD_UNLOCK_RETRIES_STEP_PIN2:
+            mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+                                      CSIM_QUERY_PIN2_RETRIES_STR,
+                                      CSIM_QUERY_TIMEOUT,
+                                      FALSE,
+                                      (GAsyncReadyCallback) csim_query_ready,
+                                      ctx);
+            break;
+        case LOAD_UNLOCK_RETRIES_STEP_PUK2:
+            mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+                                      CSIM_QUERY_PUK2_RETRIES_STR,
+                                      CSIM_QUERY_TIMEOUT,
+                                      FALSE,
+                                      (GAsyncReadyCallback) csim_query_ready,
+                                      ctx);
+            break;
+        case LOAD_UNLOCK_RETRIES_STEP_LAST:
+            if (ctx->succeded_requests == 0) {
+                g_simple_async_result_set_error (ctx->result,
+                                                 MM_CORE_ERROR,
+                                                 MM_CORE_ERROR_FAILED,
+                                                 "Could not get any of the SIM unlock retries values. Look above for warning messages");
+            } else {
+                g_simple_async_result_set_op_res_gpointer (ctx->result,
+                                                           g_object_ref (ctx->retries),
+                                                           (GDestroyNotify)g_object_unref);
+            }
+
+            load_unlock_retries_context_complete_and_free (ctx);
+            break;
+        default:
+            break;
+    }
+}
+
+static void
+modem_load_unlock_retries (MMIfaceModem *self,
+                           GAsyncReadyCallback callback,
+                           gpointer user_data)
+{
+    LoadUnlockRetriesContext* ctx;
+
+    ctx = g_slice_new0 (LoadUnlockRetriesContext);
+    ctx->self = g_object_ref (self);
+    ctx->result = g_simple_async_result_new (G_OBJECT (self),
+                                             callback,
+                                             user_data,
+                                             modem_load_unlock_retries);
+
+    ctx->retries = mm_unlock_retries_new ();
+    ctx->step = 0;
+    ctx->succeded_requests = 0;
+
+    load_unlock_retries_step (ctx);
+}
+
+/*****************************************************************************/
 /* Modem power down (Modem interface) */
 
 static gboolean
@@ -336,6 +517,8 @@ mm_broadband_modem_telit_init (MMBroadbandModemTelit *self)
 static void
 iface_modem_init (MMIfaceModem *iface)
 {
+    iface->load_unlock_retries_finish = modem_load_unlock_retries_finish;
+    iface->load_unlock_retries = modem_load_unlock_retries;
     iface->reset = modem_reset;
     iface->reset_finish = modem_reset_finish;
     iface->modem_power_down = modem_power_down;
diff --git a/plugins/telit/mm-modem-helpers-telit.c b/plugins/telit/mm-modem-helpers-telit.c
new file mode 100644
index 0000000..1ee046f
--- /dev/null
+++ b/plugins/telit/mm-modem-helpers-telit.c
@@ -0,0 +1,75 @@
+/* -*- 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) 2015 Telit.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MMCLI
+#include <libmm-glib.h>
+
+#include "mm-log.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-telit.h"
+
+/*****************************************************************************/
+/* +CSIM response parser */
+
+gint parse_csim_response (const guint step, const gchar* response, GError** error)
+{
+    GRegex *r = NULL;
+    GMatchInfo *match_info = NULL;
+    gchar* retries_hex_str;
+    guint retries;
+
+    r = g_regex_new ("\\+CSIM:\\s*[0-9]+,\\s*.*63C(.*)\"", G_REGEX_RAW, 0, NULL);
+
+    if (!g_regex_match (r, response, 0, &match_info)) {
+        g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+                     "Could not parse reponse '%s'", response);
+        g_match_info_free (match_info);
+        g_regex_unref (r);
+        return -1;
+    }
+
+    if (!g_match_info_matches (match_info)) {
+        g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+                     "Could not find matches in response '%s'", response);
+        g_match_info_free (match_info);
+        g_regex_unref (r);
+        return -1;
+    }
+
+    retries_hex_str = mm_get_string_unquoted_from_match_info (match_info, 1);
+    g_assert (NULL != retries_hex_str);
+
+    /* convert hex value to uint */
+    if (sscanf (retries_hex_str, "%x", &retries) != 1) {
+         g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+                     "Could not get retry value from match '%s'",
+                     retries_hex_str);
+        g_match_info_free (match_info);
+        g_regex_unref (r);
+        return -1;
+    }
+
+    g_free (retries_hex_str);
+    g_match_info_free (match_info);
+    g_regex_unref (r);
+
+    return retries;
+}
diff --git a/plugins/telit/mm-modem-helpers-telit.h b/plugins/telit/mm-modem-helpers-telit.h
new file mode 100644
index 0000000..ac91d20
--- /dev/null
+++ b/plugins/telit/mm-modem-helpers-telit.h
@@ -0,0 +1,25 @@
+/* -*- 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) 2015 Telit.
+ *
+ */
+#ifndef MM_MODEM_HELPERS_TELIT_H
+#define MM_MODEM_HELPERS_TELIT_H
+
+#include <glib.h>
+
+/* +CSIM response parser */
+gint parse_csim_response (const guint step, const gchar* response, GError** error);
+
+#endif  /* MM_MODEM_HELPERS_TELIT_H */
+
diff --git a/plugins/telit/tests/test-mm-modem-helpers-telit.c b/plugins/telit/tests/test-mm-modem-helpers-telit.c
new file mode 100644
index 0000000..a6a44c4
--- /dev/null
+++ b/plugins/telit/tests/test-mm-modem-helpers-telit.c
@@ -0,0 +1,104 @@
+/* -*- 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) 2015 Telit
+ *
+ */
+#include <stdio.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <locale.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-telit.h"
+
+typedef struct {
+    gchar* response;
+    gint result;
+} CSIMResponseTest;
+
+static CSIMResponseTest valid_csim_response_test_list [] = {
+    /* The parser expects that 2nd arg contains
+     * substring "63Cx" where x is an HEX string
+     * representing the retry value */
+    {"+CSIM:8,\"xxxx63C1\"", 1},
+    {"+CSIM:8,\"xxxx63CA\"", 10},
+    {"+CSIM:8,\"xxxx63CF\"", 15},
+    /* The parser accepts spaces */
+    {"+CSIM:8,\"xxxx63C1\"", 1},
+    {"+CSIM:8, \"xxxx63C1\"", 1},
+    {"+CSIM: 8, \"xxxx63C1\"", 1},
+    {"+CSIM:  8, \"xxxx63C1\"", 1},
+    /* the parser expects an int as first argument (2nd arg's length),
+     * but does not check if it is correct */
+    {"+CSIM: 10, \"63CF\"", 15},
+    /* the parser expect a quotation mark at the end
+     * of the response, but not at the begin */
+    {"+CSIM: 10, 63CF\"", 15},
+    { NULL, -1}
+};
+
+static CSIMResponseTest invalid_csim_response_test_list [] = {
+    /* Test missing final quotation mark */
+    {"+CSIM: 8, xxxx63CF", -1},
+    /* Negative test for substring "63Cx" */
+    {"+CSIM: 4, xxxx73CF\"", -1},
+    /* Test missing first argument */
+    {"+CSIM:xxxx63CF\"", -1},
+    { NULL, -1}
+};
+
+static void
+test_parse_csim_response (void)
+{
+    const gint step = 1;
+    guint i;
+    gint res;
+    GError* error = NULL;
+
+    /* Test valid responses */
+    for (i = 0; valid_csim_response_test_list[i].response != NULL; i++) {
+        res = parse_csim_response (step, valid_csim_response_test_list[i].response, &error);
+
+        g_assert_no_error (error);
+        g_assert_cmpint (res, ==, valid_csim_response_test_list[i].result);
+    }
+
+    /* Test invalid responses */
+    for (i = 0; invalid_csim_response_test_list[i].response != NULL; i++) {
+        res = parse_csim_response (step, invalid_csim_response_test_list[i].response, &error);
+
+        g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED);
+        g_assert_cmpint (res, ==, invalid_csim_response_test_list[i].result);
+
+        if (NULL != error) {
+            g_error_free (error);
+            error = NULL;
+        }
+    }
+}
+
+int main (int argc, char **argv)
+{
+    setlocale (LC_ALL, "");
+
+    g_type_init ();
+    g_test_init (&argc, &argv, NULL);
+
+    g_test_add_func ("/MM/telit/csim", test_parse_csim_response);
+    return g_test_run ();
+}
-- 
2.1.4



More information about the ModemManager-devel mailing list