diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2018-10-09 10:50:03 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2018-10-09 11:08:45 +0200 |
commit | b2520b8d8e37ded25ea12705a2cbec99b45d6246 (patch) | |
tree | ea01902d5a413ef5ca28e4706fc9c312957182b7 | |
parent | 1ac79ccdfd82af5277c6baac7760d3a9588a0bd4 (diff) | |
download | ModemManager-aleksander/sierra-scact-monitor.tar.gz |
sierra-legacy: implement connection status monitoring with !SCACT?aleksander/sierra-scact-monitor
Devices like the Netgear AC313U require explicit context monitoring,
otherwise the device may end up disconnected internally and MM would
still think that the connection is ongoing.
-rw-r--r-- | plugins/Makefile.am | 22 | ||||
-rw-r--r-- | plugins/sierra/mm-broadband-bearer-sierra.c | 122 | ||||
-rw-r--r-- | plugins/sierra/mm-modem-helpers-sierra.c | 84 | ||||
-rw-r--r-- | plugins/sierra/mm-modem-helpers-sierra.h | 26 | ||||
-rw-r--r-- | plugins/sierra/tests/test-modem-helpers-sierra.c | 150 | ||||
-rw-r--r-- | src/mm-modem-helpers.c | 2 | ||||
-rw-r--r-- | src/mm-modem-helpers.h | 2 |
7 files changed, 405 insertions, 3 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 52513a788..350dca1fb 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -180,6 +180,25 @@ MBM_COMMON_LIBADD_FLAGS = $(builddir)/libmm-utils-mbm.la # common sierra support ################################################################################ +noinst_LTLIBRARIES += libhelpers-sierra.la +libhelpers_sierra_la_SOURCES = \ + sierra/mm-modem-helpers-sierra.c \ + sierra/mm-modem-helpers-sierra.h \ + $(NULL) + +noinst_PROGRAMS += test-modem-helpers-sierra +test_modem_helpers_sierra_SOURCES = \ + sierra/tests/test-modem-helpers-sierra.c \ + $(NULL) +test_modem_helpers_sierra_CPPFLAGS = \ + -I$(top_srcdir)/plugins/sierra \ + $(NULL) +test_modem_helpers_sierra_LDADD = \ + $(builddir)/libhelpers-sierra.la \ + $(top_builddir)/src/libhelpers.la \ + $(top_builddir)/libmm-glib/libmm-glib.la \ + $(NULL) + noinst_LTLIBRARIES += libmm-utils-sierra.la libmm_utils_sierra_la_SOURCES = \ sierra/mm-common-sierra.c \ @@ -191,6 +210,9 @@ libmm_utils_sierra_la_SOURCES = \ sierra/mm-broadband-modem-sierra.c \ sierra/mm-broadband-modem-sierra.h \ $(NULL) +libmm_utils_sierra_la_LIBADD = \ + $(builddir)/libhelpers-sierra.la \ + $(NULL) SIERRA_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/sierra SIERRA_COMMON_LIBADD_FLAGS = $(builddir)/libmm-utils-sierra.la diff --git a/plugins/sierra/mm-broadband-bearer-sierra.c b/plugins/sierra/mm-broadband-bearer-sierra.c index 895842905..1943d1acb 100644 --- a/plugins/sierra/mm-broadband-bearer-sierra.c +++ b/plugins/sierra/mm-broadband-bearer-sierra.c @@ -31,6 +31,7 @@ #include "mm-broadband-bearer-sierra.h" #include "mm-log.h" #include "mm-modem-helpers.h" +#include "mm-modem-helpers-sierra.h" G_DEFINE_TYPE (MMBroadbandBearerSierra, mm_broadband_bearer_sierra, MM_TYPE_BROADBAND_BEARER); @@ -45,6 +46,123 @@ enum { }; /*****************************************************************************/ +/* Connection status monitoring */ + +static MMBearerConnectionStatus +load_connection_status_finish (MMBaseBearer *bearer, + GAsyncResult *res, + GError **error) +{ + GError *inner_error = NULL; + gssize value; + + value = g_task_propagate_int (G_TASK (res), &inner_error); + if (inner_error) { + g_propagate_error (error, inner_error); + return MM_BEARER_CONNECTION_STATUS_UNKNOWN; + } + return (MMBearerConnectionStatus)value; +} + +static void +scact_periodic_query_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + MMBroadbandBearer *self; + const gchar *response; + GError *error = NULL; + GList *pdp_active_list = NULL; + GList *l; + MMBearerConnectionStatus status = MM_BEARER_CONNECTION_STATUS_UNKNOWN; + guint cid; + + self = MM_BROADBAND_BEARER (g_task_get_source_object (task)); + cid = GPOINTER_TO_UINT (g_task_get_task_data (task)); + + response = mm_base_modem_at_command_finish (modem, res, &error); + if (response) + pdp_active_list = mm_sierra_parse_scact_read_response (response, &error); + + if (error) { + g_assert (!pdp_active_list); + g_prefix_error (&error, "Couldn't check current list of active PDP contexts: "); + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + for (l = pdp_active_list; l; l = g_list_next (l)) { + MM3gppPdpContextActive *pdp_active; + + /* We look for the specific CID */ + pdp_active = (MM3gppPdpContextActive *)(l->data); + if (pdp_active->cid == cid) { + status = (pdp_active->active ? MM_BEARER_CONNECTION_STATUS_CONNECTED : MM_BEARER_CONNECTION_STATUS_DISCONNECTED); + break; + } + } + mm_3gpp_pdp_context_active_list_free (pdp_active_list); + + /* PDP context not found? This shouldn't happen, error out */ + if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "PDP context not found in the known contexts list"); + else + g_task_return_int (task, (gssize) status); + g_object_unref (task); +} + +static void +load_connection_status (MMBaseBearer *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + MMBaseModem *modem = NULL; + MMPortSerialAt *port; + guint cid; + + task = g_task_new (self, NULL, callback, user_data); + + g_object_get (MM_BASE_BEARER (self), + MM_BASE_BEARER_MODEM, &modem, + NULL); + + /* If CID not defined, error out */ + cid = mm_broadband_bearer_get_3gpp_cid (MM_BROADBAND_BEARER (self)); + if (!cid) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't load connection status: cid not defined"); + g_object_unref (task); + goto out; + } + g_task_set_task_data (task, GUINT_TO_POINTER (cid), NULL); + + /* If no control port available, error out */ + port = mm_base_modem_peek_best_at_port (modem, NULL); + if (!port) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, + "Couldn't load connection status: no control port available"); + g_object_unref (task); + goto out; + } + + mm_base_modem_at_command_full (MM_BASE_MODEM (modem), + port, + "!SCACT?", + 3, + FALSE, /* allow cached */ + FALSE, /* raw */ + NULL, /* cancellable */ + (GAsyncReadyCallback) scact_periodic_query_ready, + task); + +out: + g_clear_object (&modem); +} + +/*****************************************************************************/ /* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */ typedef enum { @@ -539,8 +657,8 @@ mm_broadband_bearer_sierra_class_init (MMBroadbandBearerSierraClass *klass) object_class->set_property = set_property; object_class->get_property = get_property; - base_bearer_class->load_connection_status = NULL; - base_bearer_class->load_connection_status_finish = NULL; + base_bearer_class->load_connection_status = load_connection_status; + base_bearer_class->load_connection_status_finish = load_connection_status_finish; broadband_bearer_class->dial_3gpp = dial_3gpp; broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish; diff --git a/plugins/sierra/mm-modem-helpers-sierra.c b/plugins/sierra/mm-modem-helpers-sierra.c new file mode 100644 index 000000000..14324614f --- /dev/null +++ b/plugins/sierra/mm-modem-helpers-sierra.c @@ -0,0 +1,84 @@ +/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es> + */ + +#include <glib.h> +#include <string.h> + +#include "mm-log.h" +#include "mm-modem-helpers.h" +#include "mm-modem-helpers-sierra.h" + +GList * +mm_sierra_parse_scact_read_response (const gchar *reply, + GError **error) +{ + GError *inner_error = NULL; + GRegex *r; + GMatchInfo *match_info; + GList *list; + + if (!reply || !reply[0]) + /* Nothing configured, all done */ + return NULL; + + list = NULL; + r = g_regex_new ("!SCACT:\\s*(\\d+),(\\d+)", + G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, &inner_error); + g_assert (r); + + g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, &inner_error); + while (!inner_error && g_match_info_matches (match_info)) { + MM3gppPdpContextActive *pdp_active; + guint cid = 0; + guint aux = 0; + + if (!mm_get_uint_from_match_info (match_info, 1, &cid)) { + inner_error = g_error_new (MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't parse CID from reply: '%s'", + reply); + break; + } + if (!mm_get_uint_from_match_info (match_info, 2, &aux) || (aux != 0 && aux != 1)) { + inner_error = g_error_new (MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't parse context status from reply: '%s'", + reply); + break; + } + + pdp_active = g_slice_new0 (MM3gppPdpContextActive); + pdp_active->cid = cid; + pdp_active->active = (gboolean) aux; + list = g_list_prepend (list, pdp_active); + + g_match_info_next (match_info, &inner_error); + } + + if (match_info) + g_match_info_free (match_info); + g_regex_unref (r); + + if (inner_error) { + mm_3gpp_pdp_context_active_list_free (list); + g_propagate_error (error, inner_error); + g_prefix_error (error, "Couldn't properly parse list of active/inactive PDP contexts. "); + return NULL; + } + + list = g_list_sort (list, (GCompareFunc) mm_3gpp_pdp_context_active_cmp); + + return list; +} diff --git a/plugins/sierra/mm-modem-helpers-sierra.h b/plugins/sierra/mm-modem-helpers-sierra.h new file mode 100644 index 000000000..f5c6cd2c6 --- /dev/null +++ b/plugins/sierra/mm-modem-helpers-sierra.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) 2018 Aleksander Morgado <aleksander@aleksander.es> + */ + +#ifndef MM_MODEM_HELPERS_SIERRA_H +#define MM_MODEM_HELPERS_SIERRA_H + +#include <glib.h> +#include <ModemManager.h> + +/* MM3gppPdpContextActive list */ +GList *mm_sierra_parse_scact_read_response (const gchar *reply, + GError **error); + +#endif /* MM_MODEM_HELPERS_SIERRA_H */ diff --git a/plugins/sierra/tests/test-modem-helpers-sierra.c b/plugins/sierra/tests/test-modem-helpers-sierra.c new file mode 100644 index 000000000..a53f27e14 --- /dev/null +++ b/plugins/sierra/tests/test-modem-helpers-sierra.c @@ -0,0 +1,150 @@ +/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es> + */ + +#include <glib.h> +#include <glib-object.h> +#include <locale.h> +#include <arpa/inet.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-sierra.h" + +/*****************************************************************************/ +/* Test !SCACT? responses */ + +static void +test_scact_read_results (const gchar *desc, + const gchar *reply, + MM3gppPdpContextActive *expected_results, + guint32 expected_results_len) +{ + GList *l; + GError *error = NULL; + GList *results; + + g_debug ("\nTesting %s !SCACT response...\n", desc); + + results = mm_sierra_parse_scact_read_response (reply, &error); + g_assert_no_error (error); + if (expected_results_len) { + g_assert (results); + g_assert_cmpuint (g_list_length (results), ==, expected_results_len); + } + + for (l = results; l; l = g_list_next (l)) { + MM3gppPdpContextActive *pdp = l->data; + gboolean found = FALSE; + guint i; + + for (i = 0; !found && i < expected_results_len; i++) { + MM3gppPdpContextActive *expected; + + expected = &expected_results[i]; + if (pdp->cid == expected->cid) { + found = TRUE; + g_assert_cmpuint (pdp->active, ==, expected->active); + } + } + + g_assert (found == TRUE); + } + + mm_3gpp_pdp_context_active_list_free (results); +} + +static void +test_scact_read_response_none (void) +{ + test_scact_read_results ("none", "", NULL, 0); +} + +static void +test_scact_read_response_single_inactive (void) +{ + const gchar *reply = "!SCACT: 1,0\r\n"; + static MM3gppPdpContextActive expected[] = { + { 1, FALSE }, + }; + + test_scact_read_results ("single inactive", reply, &expected[0], G_N_ELEMENTS (expected)); +} + +static void +test_scact_read_response_single_active (void) +{ + const gchar *reply = "!SCACT: 1,1\r\n"; + static MM3gppPdpContextActive expected[] = { + { 1, TRUE }, + }; + + test_scact_read_results ("single active", reply, &expected[0], G_N_ELEMENTS (expected)); +} + +static void +test_scact_read_response_multiple (void) +{ + const gchar *reply = + "!SCACT: 1,0\r\n" + "!SCACT: 4,1\r\n" + "!SCACT: 5,0\r\n"; + static MM3gppPdpContextActive expected[] = { + { 1, FALSE }, + { 4, TRUE }, + { 5, FALSE }, + }; + + test_scact_read_results ("multiple", reply, &expected[0], G_N_ELEMENTS (expected)); +} + +/*****************************************************************************/ + +void +_mm_log (const char *loc, + const char *func, + guint32 level, + const char *fmt, + ...) +{ +#if defined ENABLE_TEST_MESSAGE_TRACES + /* Dummy log function */ + va_list args; + gchar *msg; + + va_start (args, fmt); + msg = g_strdup_vprintf (fmt, args); + va_end (args); + g_print ("%s\n", msg); + g_free (msg); +#endif +} + +int main (int argc, char **argv) +{ + setlocale (LC_ALL, ""); + + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/MM/sierra/scact/read/none", test_scact_read_response_none); + g_test_add_func ("/MM/sierra/scact/read/single-inactive", test_scact_read_response_single_inactive); + g_test_add_func ("/MM/sierra/scact/read/single-active", test_scact_read_response_single_active); + g_test_add_func ("/MM/sierra/scact/read/multiple", test_scact_read_response_multiple); + + return g_test_run (); +} diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c index a1c7e9a26..296b9f3a0 100644 --- a/src/mm-modem-helpers.c +++ b/src/mm-modem-helpers.c @@ -1554,7 +1554,7 @@ mm_3gpp_pdp_context_active_list_free (GList *pdp_active_list) g_list_free_full (pdp_active_list, (GDestroyNotify) mm_3gpp_pdp_context_active_free); } -static gint +gint mm_3gpp_pdp_context_active_cmp (MM3gppPdpContextActive *a, MM3gppPdpContextActive *b) { diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h index 30855f279..a2ee82541 100644 --- a/src/mm-modem-helpers.h +++ b/src/mm-modem-helpers.h @@ -181,6 +181,8 @@ typedef struct { gboolean active; } MM3gppPdpContextActive; void mm_3gpp_pdp_context_active_list_free (GList *pdp_active_list); +gint mm_3gpp_pdp_context_active_cmp (MM3gppPdpContextActive *a, + MM3gppPdpContextActive *b); GList *mm_3gpp_parse_cgact_read_response (const gchar *reply, GError **error); |