summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@aleksander.es>2018-10-09 10:50:03 +0200
committerAleksander Morgado <aleksander@aleksander.es>2018-10-09 11:08:45 +0200
commitb2520b8d8e37ded25ea12705a2cbec99b45d6246 (patch)
treeea01902d5a413ef5ca28e4706fc9c312957182b7
parent1ac79ccdfd82af5277c6baac7760d3a9588a0bd4 (diff)
downloadModemManager-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.am22
-rw-r--r--plugins/sierra/mm-broadband-bearer-sierra.c122
-rw-r--r--plugins/sierra/mm-modem-helpers-sierra.c84
-rw-r--r--plugins/sierra/mm-modem-helpers-sierra.h26
-rw-r--r--plugins/sierra/tests/test-modem-helpers-sierra.c150
-rw-r--r--src/mm-modem-helpers.c2
-rw-r--r--src/mm-modem-helpers.h2
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);