diff options
-rw-r--r-- | plugins/Makefile.am | 18 | ||||
-rw-r--r-- | plugins/telit/mm-broadband-modem-telit.c | 183 | ||||
-rw-r--r-- | plugins/telit/mm-modem-helpers-telit.c | 75 | ||||
-rw-r--r-- | plugins/telit/mm-modem-helpers-telit.h | 25 | ||||
-rw-r--r-- | plugins/telit/tests/test-mm-modem-helpers-telit.c | 104 |
5 files changed, 404 insertions, 1 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am index b4a76fa2..59b1bffb 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 0f772314..4121ac33 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 00000000..1ee046f0 --- /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 00000000..ac91d20d --- /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 00000000..a6a44c45 --- /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 (); +} |