diff options
author | Prathmesh Prabhu <pprabhu@chromium.org> | 2013-10-09 10:06:27 -0700 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2013-10-10 08:29:56 +0200 |
commit | 09ea458f78f417df0b5e7ff86dafc1b5ab326d5a (patch) | |
tree | 32ed29e24e1e117ee833d8f2e10a08faccd45e60 | |
parent | 92a2953e938e7687d8f2a534b38b26647517c99a (diff) |
altair-lte: obtain subscription state of SIM from registration failure code
This patch uses the extended error code after a registration failure to
determine if the SIM in the relevant modem is associated with a non-provisioned
account. It uses the standard AT+CEER command, but the implementation is
restricted to the altair plugin.
-rw-r--r-- | plugins/Makefile.am | 13 | ||||
-rw-r--r-- | plugins/altair/mm-broadband-modem-altair-lte.c | 134 | ||||
-rw-r--r-- | plugins/altair/mm-modem-helpers-altair-lte.c | 68 | ||||
-rw-r--r-- | plugins/altair/mm-modem-helpers-altair-lte.h | 26 | ||||
-rw-r--r-- | plugins/altair/tests/test-modem-helpers-altair-lte.c | 75 |
5 files changed, 313 insertions, 3 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am index d10c9292..d775d5a4 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -348,6 +348,8 @@ libmm_plugin_novatel_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) # Altair LTE modem libmm_plugin_altair_lte_la_SOURCES = \ + altair/mm-modem-helpers-altair-lte.c \ + altair/mm-modem-helpers-altair-lte.h \ altair/mm-plugin-altair-lte.c \ altair/mm-plugin-altair-lte.h \ altair/mm-broadband-modem-altair-lte.c \ @@ -357,6 +359,17 @@ libmm_plugin_altair_lte_la_SOURCES = \ libmm_plugin_altair_lte_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) libmm_plugin_altair_lte_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) +noinst_PROGRAMS += test-modem-helpers-altair-lte +test_modem_helpers_altair_lte_SOURCES = \ + altair/mm-modem-helpers-altair-lte.c \ + altair/mm-modem-helpers-altair-lte.h \ + altair/tests/test-modem-helpers-altair-lte.c +test_modem_helpers_altair_lte_CPPFLAGS = \ + -I$(top_srcdir)/plugins/altair \ + $(PLUGIN_COMMON_COMPILER_FLAGS) +test_modem_helpers_altair_lte_LDFLAGS = $(top_builddir)/libmm-glib/libmm-glib.la + + # VIA modem libmm_plugin_via_la_SOURCES = \ via/mm-plugin-via.c \ diff --git a/plugins/altair/mm-broadband-modem-altair-lte.c b/plugins/altair/mm-broadband-modem-altair-lte.c index 3303cbb5..64d4c51e 100644 --- a/plugins/altair/mm-broadband-modem-altair-lte.c +++ b/plugins/altair/mm-broadband-modem-altair-lte.c @@ -24,6 +24,9 @@ #include <ctype.h> #include "ModemManager.h" +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + #include "mm-base-modem-at.h" #include "mm-broadband-bearer-altair-lte.h" #include "mm-broadband-modem-altair-lte.h" @@ -34,6 +37,7 @@ #include "mm-iface-modem-messaging.h" #include "mm-log.h" #include "mm-modem-helpers.h" +#include "mm-modem-helpers-altair-lte.h" #include "mm-serial-parsers.h" #include "mm-bearer-list.h" @@ -465,6 +469,131 @@ reset (MMIfaceModem *self, } /*****************************************************************************/ +/* Run registration checks (3GPP interface) */ + +static gboolean +modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), + error); +} + +static void +run_registration_checks_subscription_state_ready (MMIfaceModem3gpp *self, + GAsyncResult *res, + GSimpleAsyncResult *operation_result) +{ + GError *error = NULL; + const gchar *at_response; + gchar *ceer_response; + + /* If the AT+CEER command fails, or we fail to obtain a valid result, we + * ignore the error. This allows the registration attempt to continue. + * So, the async response from this function is *always* True. + */ + g_simple_async_result_set_op_res_gboolean (operation_result, TRUE); + + at_response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (!at_response) { + g_assert (error); + mm_warn ("AT+CEER failed: %s", error->message); + g_error_free (error); + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); + return; + } + + ceer_response = mm_altair_parse_ceer_response (at_response, &error); + if (!ceer_response) { + g_assert (error); + mm_warn ("Failed to parse AT+CEER response: %s", error->message); + g_error_free (error); + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); + return; + } + + if (g_strcmp0 ("EPS_AND_NON_EPS_SERVICES_NOT_ALLOWED", ceer_response) == 0) { + mm_dbg ("Registration failed due to unprovisioned SIM."); + mm_iface_modem_3gpp_update_subscription_state (self, MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNPROVISIONED); + } else { + mm_dbg ("Failed to find a better reason for registration failure."); + } + + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); + g_free (ceer_response); +} + +static void +run_registration_checks_ready (MMIfaceModem3gpp *self, + GAsyncResult *res, + GSimpleAsyncResult *operation_result) +{ + GError *error = NULL; + gboolean success; + MMModem3gppRegistrationState registration_state; + + g_assert (iface_modem_3gpp_parent->run_registration_checks_finish); + success = iface_modem_3gpp_parent->run_registration_checks_finish (self, res, &error); + if (!success) { + g_assert (error); + g_simple_async_result_take_error (operation_result, error); + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); + return; + } + + g_object_get (self, + MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, ®istration_state, + NULL); + + if (registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || + registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) { + mm_dbg ("Registration succeeded: Marking the SIM as provisioned."); + mm_iface_modem_3gpp_update_subscription_state (self, MM_MODEM_3GPP_SUBSCRIPTION_STATE_PROVISIONED); + g_simple_async_result_set_op_res_gboolean (operation_result, TRUE); + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); + return; + } + + mm_dbg ("Registration not successful yet. Checking if SIM is unprovisioned."); + mm_base_modem_at_command (MM_BASE_MODEM (self), + "+CEER", + 6, + FALSE, + (GAsyncReadyCallback) run_registration_checks_subscription_state_ready, + operation_result); +} + +static void +modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self, + gboolean cs_supported, + gboolean ps_supported, + gboolean eps_supported, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *operation_result; + + operation_result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + modem_3gpp_run_registration_checks); + + g_assert (iface_modem_3gpp_parent->run_registration_checks); + iface_modem_3gpp_parent->run_registration_checks (self, + cs_supported, + ps_supported, + eps_supported, + (GAsyncReadyCallback) run_registration_checks_ready, + operation_result); +} + +/*****************************************************************************/ /* Register in network (3GPP interface) */ static void @@ -989,7 +1118,6 @@ iface_modem_init (MMIfaceModem *iface) iface->setup_charset_finish = NULL; iface->setup_flow_control = NULL; iface->setup_flow_control_finish = NULL; - } static void @@ -1003,7 +1131,6 @@ iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface) static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { - iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events; @@ -1017,6 +1144,8 @@ iface_modem_3gpp_init (MMIfaceModem3gpp *iface) iface->register_in_network = modem_3gpp_register_in_network; iface->register_in_network_finish = modem_3gpp_register_in_network_finish; + iface->run_registration_checks = modem_3gpp_run_registration_checks; + iface->run_registration_checks_finish = modem_3gpp_run_registration_checks_finish; /* Scanning is not currently supported */ iface->scan_networks = NULL; @@ -1027,7 +1156,6 @@ iface_modem_3gpp_init (MMIfaceModem3gpp *iface) iface->load_operator_code_finish = modem_3gpp_load_operator_code_finish; iface->load_operator_name = modem_3gpp_load_operator_name; iface->load_operator_name_finish = modem_3gpp_load_operator_name_finish; - } static void diff --git a/plugins/altair/mm-modem-helpers-altair-lte.c b/plugins/altair/mm-modem-helpers-altair-lte.c new file mode 100644 index 00000000..9e5b421e --- /dev/null +++ b/plugins/altair/mm-modem-helpers-altair-lte.c @@ -0,0 +1,68 @@ +/* -*- 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) 2013 Google Inc. + * + */ + +#include <string.h> + +#include <ModemManager.h> +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-modem-helpers-altair-lte.h" + +/*****************************************************************************/ +/* +CEER response parser */ + +gchar * +mm_altair_parse_ceer_response (const gchar *response, + GError **error) +{ + GRegex *r; + GMatchInfo *match_info = NULL; + gchar *ceer_response = NULL; + + + /* First accept an empty response as the no error case. Sometimes, the only + * respone to the AT+CEER query is an OK. + */ + if (g_strcmp0 ("", response) == 0) { + return g_strdup (""); + } + + /* The response we are interested in looks so: + * +CEER: EPS_AND_NON_EPS_SERVICES_NOT_ALLOWED + */ + r = g_regex_new ("\\+CEER:\\s*(\\w*)?", + G_REGEX_RAW, + 0, NULL); + g_assert (r != NULL); + + if (!g_regex_match (r, response, 0, &match_info)) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse +CEER response"); + g_match_info_free (match_info); + g_regex_unref (r); + return NULL; + } + + if (g_match_info_matches (match_info)) { + ceer_response = mm_get_string_unquoted_from_match_info (match_info, 1); + if (!ceer_response) + ceer_response = g_strdup (""); + } + + g_match_info_free (match_info); + g_regex_unref (r); + return ceer_response; +} diff --git a/plugins/altair/mm-modem-helpers-altair-lte.h b/plugins/altair/mm-modem-helpers-altair-lte.h new file mode 100644 index 00000000..dbd641cd --- /dev/null +++ b/plugins/altair/mm-modem-helpers-altair-lte.h @@ -0,0 +1,26 @@ +/* -*- 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) 2013 Google Inc. + * + */ + +#ifndef MM_MODEM_HELPERS_ALTAIR_H +#define MM_MODEM_HELPERS_ALTAIR_H + +#include <glib.h> + +/* +CEER response parser */ +gchar *mm_altair_parse_ceer_response (const gchar *response, + GError **error); + +#endif /* MM_MODEM_HELPERS_ALTAIR_H */ diff --git a/plugins/altair/tests/test-modem-helpers-altair-lte.c b/plugins/altair/tests/test-modem-helpers-altair-lte.c new file mode 100644 index 00000000..665b928f --- /dev/null +++ b/plugins/altair/tests/test-modem-helpers-altair-lte.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) 2013 Google Inc. + * + */ + +#include <stdarg.h> +#include <stdio.h> +#include <glib.h> +#include <glib-object.h> +#include <locale.h> + +#include "mm-modem-helpers-altair-lte.h" + +/*****************************************************************************/ +/* Test +CEER responses */ + +typedef struct { + const gchar *str; + const gchar *result; +} CeerTest; + +static const CeerTest ceer_tests[] = { + { "", "" }, /* Special case, sometimes the response is empty, treat it as a valid response. */ + { "+CEER:", "" }, + { "+CEER: EPS_AND_NON_EPS_SERVICES_NOT_ALLOWED", "EPS_AND_NON_EPS_SERVICES_NOT_ALLOWED" }, + { "+CEER: NO_SUITABLE_CELLS_IN_TRACKING_AREA", "NO_SUITABLE_CELLS_IN_TRACKING_AREA" }, + { "WRONG RESPONSE", NULL }, + { NULL, NULL } +}; + +static void +test_ceer (void) +{ + guint i; + + for (i = 0; ceer_tests[i].str; ++i) { + GError *error = NULL; + gchar *result; + + result = mm_altair_parse_ceer_response (ceer_tests[i].str, &error); + if (ceer_tests[i].result) { + g_assert_cmpstr (ceer_tests[i].result, ==, result); + g_assert_no_error (error); + g_free (result); + } + else { + g_assert (result == NULL); + g_assert (error != NULL); + g_error_free (error); + } + } +} + +int main (int argc, char **argv) +{ + setlocale (LC_ALL, ""); + + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/MM/altair/ceer", test_ceer); + + return g_test_run (); +} |