[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