[PATCH c2] huawei: handle disconnection via ^NDISSTAT unsolicited message

Ben Chan benchan at chromium.org
Tue Sep 17 20:45:16 PDT 2013


This patch changes MMBroadbandModemHuawei to use ^NDISSTAT unsolicited
messages to handle network-initiated disconnection. As a ^NDISSTAT
unsolicited message is similar to a ^NDISSTATQRY response, the patch
extends the ^NDISSTATQRY parser code to handle both ^NDISSTAT and
^NDISSTATQRY responses.
---
 plugins/huawei/mm-broadband-bearer-huawei.c      | 18 +++++
 plugins/huawei/mm-broadband-bearer-huawei.h      |  3 +
 plugins/huawei/mm-broadband-modem-huawei.c       | 92 +++++++++++++++++++++---
 plugins/huawei/mm-modem-helpers-huawei.c         | 18 +++--
 plugins/huawei/mm-modem-helpers-huawei.h         |  2 +-
 plugins/huawei/tests/test-modem-helpers-huawei.c | 34 ++++++++-
 6 files changed, 150 insertions(+), 17 deletions(-)

diff --git a/plugins/huawei/mm-broadband-bearer-huawei.c b/plugins/huawei/mm-broadband-bearer-huawei.c
index eec37f3..569ddf1 100644
--- a/plugins/huawei/mm-broadband-bearer-huawei.c
+++ b/plugins/huawei/mm-broadband-bearer-huawei.c
@@ -639,6 +639,24 @@ disconnect_3gpp (MMBroadbandBearer *self,
 
 /*****************************************************************************/
 
+gboolean
+mm_broadband_bearer_huawei_is_connect_pending (MMBroadbandBearerHuawei *self)
+{
+    g_return_val_if_fail (MM_BROADBAND_BEARER_HUAWEI (self), FALSE);
+
+    return !!self->priv->connect_pending;
+}
+
+gboolean
+mm_broadband_bearer_huawei_is_disconnect_pending (MMBroadbandBearerHuawei *self)
+{
+    g_return_val_if_fail (MM_BROADBAND_BEARER_HUAWEI (self), FALSE);
+
+    return !!self->priv->disconnect_pending;
+}
+
+/*****************************************************************************/
+
 MMBearer *
 mm_broadband_bearer_huawei_new_finish (GAsyncResult *res,
                                        GError **error)
diff --git a/plugins/huawei/mm-broadband-bearer-huawei.h b/plugins/huawei/mm-broadband-bearer-huawei.h
index 4c87d9c..3f514fb 100644
--- a/plugins/huawei/mm-broadband-bearer-huawei.h
+++ b/plugins/huawei/mm-broadband-bearer-huawei.h
@@ -56,4 +56,7 @@ void      mm_broadband_bearer_huawei_new        (MMBroadbandModemHuawei *modem,
 MMBearer *mm_broadband_bearer_huawei_new_finish (GAsyncResult *res,
                                                  GError **error);
 
+gboolean mm_broadband_bearer_huawei_is_connect_pending (MMBroadbandBearerHuawei *self);
+gboolean mm_broadband_bearer_huawei_is_disconnect_pending (MMBroadbandBearerHuawei *self);
+
 #endif /* MM_BROADBAND_BEARER_HUAWEI_H */
diff --git a/plugins/huawei/mm-broadband-modem-huawei.c b/plugins/huawei/mm-broadband-modem-huawei.c
index 73323e2..614df7c 100644
--- a/plugins/huawei/mm-broadband-modem-huawei.c
+++ b/plugins/huawei/mm-broadband-modem-huawei.c
@@ -34,6 +34,7 @@
 #include "mm-log.h"
 #include "mm-errors-types.h"
 #include "mm-modem-helpers.h"
+#include "mm-modem-helpers-huawei.h"
 #include "mm-base-modem-at.h"
 #include "mm-iface-modem.h"
 #include "mm-iface-modem-3gpp.h"
@@ -43,6 +44,7 @@
 #include "mm-broadband-modem-huawei.h"
 #include "mm-broadband-bearer-huawei.h"
 #include "mm-broadband-bearer.h"
+#include "mm-bearer-list.h"
 #include "mm-sim-huawei.h"
 
 static void iface_modem_init (MMIfaceModem *iface);
@@ -85,6 +87,7 @@ struct _MMBroadbandModemHuaweiPrivate {
 
     /* Regex for connection status related notifications */
     GRegex *dsflowrpt_regex;
+    GRegex *ndisstat_regex;
 
     /* Regex to ignore */
     GRegex *boot_regex;
@@ -97,7 +100,6 @@ struct _MMBroadbandModemHuaweiPrivate {
     GRegex *srvst_regex;
     GRegex *stin_regex;
     GRegex *hcsq_regex;
-    GRegex *ndisstat_regex;
     GRegex *pdpdeact_regex;
     GRegex *ndisend_regex;
     GRegex *rfswitch_regex;
@@ -1510,6 +1512,77 @@ huawei_status_changed (MMAtSerialPort *port,
     g_free (str);
 }
 
+typedef struct {
+    gboolean ipv4_available;
+    gboolean ipv4_connected;
+    gboolean ipv6_available;
+    gboolean ipv6_connected;
+} NdisstatResult;
+
+static void
+bearer_report_connection_status (MMBearer *bearer,
+                                 NdisstatResult *ndisstat_result)
+{
+    /* When a pending connection / disconnection attempt is in progress, we use
+     * ^NDISSTATQRY? to check the connection status and thus temporarily ignore
+     * ^NDISSTAT unsolicited messages */
+    if (mm_broadband_bearer_huawei_is_connect_pending (MM_BROADBAND_BEARER_HUAWEI (bearer)) ||
+        mm_broadband_bearer_huawei_is_disconnect_pending (MM_BROADBAND_BEARER_HUAWEI (bearer)))
+        return;
+
+    /* We already use ^NDISSTATQRY? to poll the connection status, so we only
+     * handle network-initiated disconnection here. */
+    if (ndisstat_result->ipv4_available && !ndisstat_result->ipv4_connected) {
+        /* TODO: MMBroadbandBearerHuawei does not currently support IPv6.
+         * When it does, we should check the IP family associated with each bearer. */
+        mm_dbg ("Disconnect bearer '%s'", mm_bearer_get_path (bearer));
+        mm_bearer_report_disconnection (bearer);
+    }
+}
+
+static void
+huawei_ndisstat_changed (MMAtSerialPort *port,
+                         GMatchInfo *match_info,
+                         MMBroadbandModemHuawei *self)
+{
+    gchar *str;
+    NdisstatResult ndisstat_result;
+    GError *error = NULL;
+    MMBearerList *list = NULL;
+
+    str = g_match_info_fetch (match_info, 1);
+    if (!mm_huawei_parse_ndisstatqry_response (str,
+                                               &ndisstat_result.ipv4_available,
+                                               &ndisstat_result.ipv4_connected,
+                                               &ndisstat_result.ipv6_available,
+                                               &ndisstat_result.ipv6_connected,
+                                               &error)) {
+        mm_dbg ("Ignore invalid ^NDISSTAT unsolicited message: '%s' (error %s)",
+                str, error->message);
+        g_error_free (error);
+        return;
+    }
+
+    mm_dbg ("NDIS status: IPv4 %s, IPv6 %s",
+            ndisstat_result.ipv4_available ?
+            (ndisstat_result.ipv4_connected ? "connected" : "disconnected") : "not available",
+            ndisstat_result.ipv6_available ?
+            (ndisstat_result.ipv6_connected ? "connected" : "disconnected") : "not available");
+
+    /* If empty bearer list, nothing else to do */
+    g_object_get (self,
+                  MM_IFACE_MODEM_BEARER_LIST, &list,
+                  NULL);
+    if (!list)
+        return;
+
+    mm_bearer_list_foreach (list,
+                            (MMBearerListForeachFunc)bearer_report_connection_status,
+                            &ndisstat_result);
+
+    g_object_unref (list);
+}
+
 static void
 set_3gpp_unsolicited_events_handlers (MMBroadbandModemHuawei *self,
                                       gboolean enable)
@@ -1548,6 +1621,13 @@ set_3gpp_unsolicited_events_handlers (MMBroadbandModemHuawei *self,
             enable ? (MMAtSerialUnsolicitedMsgFn)huawei_status_changed : NULL,
             enable ? self : NULL,
             NULL);
+
+        mm_at_serial_port_add_unsolicited_msg_handler (
+            ports[i],
+            self->priv->ndisstat_regex,
+            enable ? (MMAtSerialUnsolicitedMsgFn)huawei_ndisstat_changed : NULL,
+            enable ? self : NULL,
+            NULL);
     }
 }
 
@@ -2992,10 +3072,6 @@ set_ignored_unsolicited_events_handlers (MMBroadbandModemHuawei *self)
             NULL, NULL, NULL);
         mm_at_serial_port_add_unsolicited_msg_handler (
             ports[i],
-            self->priv->ndisstat_regex,
-            NULL, NULL, NULL);
-        mm_at_serial_port_add_unsolicited_msg_handler (
-            ports[i],
             self->priv->pdpdeact_regex,
             NULL, NULL, NULL);
         mm_at_serial_port_add_unsolicited_msg_handler (
@@ -3063,6 +3139,8 @@ mm_broadband_modem_huawei_init (MMBroadbandModemHuawei *self)
                                           G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
     self->priv->dsflowrpt_regex = g_regex_new ("\\r\\n\\^DSFLOWRPT:(.+)\\r\\n",
                                                G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+    self->priv->ndisstat_regex = g_regex_new ("\\r\\n(\\^NDISSTAT:.+)\\r+\\n",
+                                              G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
     self->priv->boot_regex = g_regex_new ("\\r\\n\\^BOOT:.+\\r\\n",
                                           G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
     self->priv->connect_regex = g_regex_new ("\\r\\n\\^CONNECT .+\\r\\n",
@@ -3083,8 +3161,6 @@ mm_broadband_modem_huawei_init (MMBroadbandModemHuawei *self)
                                           G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
     self->priv->hcsq_regex = g_regex_new ("\\r\\n\\^HCSQ:.+\\r+\\n",
                                           G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
-    self->priv->ndisstat_regex = g_regex_new ("\\r\\n\\^NDISSTAT:.+\\r+\\n",
-                                              G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
     self->priv->pdpdeact_regex = g_regex_new ("\\r\\n\\^PDPDEACT:.+\\r+\\n",
                                               G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
     self->priv->ndisend_regex = g_regex_new ("\\r\\n\\^NDISEND:.+\\r+\\n",
@@ -3109,6 +3185,7 @@ finalize (GObject *object)
     g_regex_unref (self->priv->hrssilvl_regex);
     g_regex_unref (self->priv->mode_regex);
     g_regex_unref (self->priv->dsflowrpt_regex);
+    g_regex_unref (self->priv->ndisstat_regex);
     g_regex_unref (self->priv->boot_regex);
     g_regex_unref (self->priv->connect_regex);
     g_regex_unref (self->priv->csnr_regex);
@@ -3119,7 +3196,6 @@ finalize (GObject *object)
     g_regex_unref (self->priv->srvst_regex);
     g_regex_unref (self->priv->stin_regex);
     g_regex_unref (self->priv->hcsq_regex);
-    g_regex_unref (self->priv->ndisstat_regex);
     g_regex_unref (self->priv->pdpdeact_regex);
     g_regex_unref (self->priv->ndisend_regex);
     g_regex_unref (self->priv->rfswitch_regex);
diff --git a/plugins/huawei/mm-modem-helpers-huawei.c b/plugins/huawei/mm-modem-helpers-huawei.c
index f5abe57..bb977c2 100644
--- a/plugins/huawei/mm-modem-helpers-huawei.c
+++ b/plugins/huawei/mm-modem-helpers-huawei.c
@@ -23,7 +23,7 @@
 #include "mm-modem-helpers-huawei.h"
 
 /*****************************************************************************/
-/* ^NDISSTATQRY response parser */
+/* ^NDISSTAT /  ^NDISSTATQRY response parser */
 
 gboolean
 mm_huawei_parse_ndisstatqry_response (const gchar *response,
@@ -37,8 +37,10 @@ mm_huawei_parse_ndisstatqry_response (const gchar *response,
     GMatchInfo *match_info;
     GError *inner_error = NULL;
 
-    if (!response || !g_str_has_prefix (response, "^NDISSTATQRY:")) {
-        g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing ^NDISSTATQRY prefix");
+    if (!response ||
+        !(g_str_has_prefix (response, "^NDISSTAT:") ||
+          g_str_has_prefix (response, "^NDISSTATQRY:"))) {
+        g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing ^NDISSTAT / ^NDISSTATQRY prefix");
         return FALSE;
     }
 
@@ -46,6 +48,8 @@ mm_huawei_parse_ndisstatqry_response (const gchar *response,
     *ipv6_available = FALSE;
 
     /* The response maybe as:
+     *     ^NDISSTAT: 1,,,IPV4
+     *     ^NDISSTAT: 0,33,,IPV6
      *     ^NDISSTATQRY: 1,,,IPV4
      *     ^NDISSTATQRY: 0,33,,IPV6
      *     OK
@@ -54,8 +58,8 @@ mm_huawei_parse_ndisstatqry_response (const gchar *response,
      *     ^NDISSTATQRY:0,,,"IPV4",0,,,"IPV6"
      *     OK
      */
-    r = g_regex_new ("\\^NDISSTATQRY:\\s*(\\d),([^,]*),([^,]*),([^,\\r\\n]*)(?:\\r\\n)?"
-                     "(?:\\^NDISSTATQRY:)?\\s*,?(\\d)?,?([^,]*)?,?([^,]*)?,?([^,\\r\\n]*)?(?:\\r\\n)?",
+    r = g_regex_new ("\\^NDISSTAT(?:QRY)?:\\s*(\\d),([^,]*),([^,]*),([^,\\r\\n]*)(?:\\r\\n)?"
+                     "(?:\\^NDISSTAT:|\\^NDISSTATQRY:)?\\s*,?(\\d)?,?([^,]*)?,?([^,]*)?,?([^,\\r\\n]*)?(?:\\r\\n)?",
                      G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
                      0, NULL);
     g_assert (r != NULL);
@@ -78,7 +82,7 @@ mm_huawei_parse_ndisstatqry_response (const gchar *response,
                 (connected != 0 && connected != 1)) {
                 inner_error = g_error_new (MM_CORE_ERROR,
                                            MM_CORE_ERROR_FAILED,
-                                           "Couldn't parse ^NDISSTATQRY fields");
+                                           "Couldn't parse ^NDISSTAT / ^NDISSTATQRY fields");
             } else if (g_ascii_strcasecmp (ip_type_str, "IPV4") == 0) {
                 *ipv4_available = TRUE;
                 *ipv4_connected = (gboolean)connected;
@@ -98,7 +102,7 @@ mm_huawei_parse_ndisstatqry_response (const gchar *response,
     if (!ipv4_available && !ipv6_available) {
         inner_error = g_error_new (MM_CORE_ERROR,
                                    MM_CORE_ERROR_FAILED,
-                                   "Couldn't find IPv4 or IPv6 info in ^NDISSTATQRY response");
+                                   "Couldn't find IPv4 or IPv6 info in ^NDISSTAT / ^NDISSTATQRY response");
     }
 
     if (inner_error) {
diff --git a/plugins/huawei/mm-modem-helpers-huawei.h b/plugins/huawei/mm-modem-helpers-huawei.h
index cc32087..4d15e8a 100644
--- a/plugins/huawei/mm-modem-helpers-huawei.h
+++ b/plugins/huawei/mm-modem-helpers-huawei.h
@@ -19,7 +19,7 @@
 
 #include "glib.h"
 
-/* ^NDISSTATQRY response parser */
+/* ^NDISSTAT / ^NDISSTATQRY response parser */
 gboolean mm_huawei_parse_ndisstatqry_response (const gchar *response,
                                                gboolean *ipv4_available,
                                                gboolean *ipv4_connected,
diff --git a/plugins/huawei/tests/test-modem-helpers-huawei.c b/plugins/huawei/tests/test-modem-helpers-huawei.c
index ea4cd95..8d99215 100644
--- a/plugins/huawei/tests/test-modem-helpers-huawei.c
+++ b/plugins/huawei/tests/test-modem-helpers-huawei.c
@@ -20,7 +20,7 @@
 #include "mm-modem-helpers-huawei.h"
 
 /*****************************************************************************/
-/* Test ^NDISSTATQRY responses */
+/* Test ^NDISSTAT / ^NDISSTATQRY responses */
 
 typedef struct {
     const gchar *str;
@@ -31,6 +31,38 @@ typedef struct {
 } NdisstatqryTest;
 
 static const NdisstatqryTest ndisstatqry_tests[] = {
+    { "^NDISSTAT: 1,,,IPV4\r\n", TRUE,  TRUE,  FALSE, FALSE },
+    { "^NDISSTAT: 0,,,IPV4\r\n", TRUE,  FALSE, FALSE, FALSE },
+    { "^NDISSTAT: 1,,,IPV6\r\n", FALSE, FALSE, TRUE,  TRUE  },
+    { "^NDISSTAT: 0,,,IPV6\r\n", FALSE, FALSE, TRUE,  FALSE },
+    { "^NDISSTAT: 1,,,IPV4\r\n"
+      "^NDISSTAT: 1,,,IPV6\r\n", TRUE,  TRUE,  TRUE,  TRUE  },
+    { "^NDISSTAT: 1,,,IPV4\r\n"
+      "^NDISSTAT: 0,,,IPV6\r\n", TRUE,  TRUE,  TRUE,  FALSE },
+    { "^NDISSTAT: 0,,,IPV4\r\n"
+      "^NDISSTAT: 1,,,IPV6\r\n", TRUE,  FALSE, TRUE,  TRUE  },
+    { "^NDISSTAT: 0,,,IPV4\r\n"
+      "^NDISSTAT: 0,,,IPV6\r\n", TRUE,  FALSE, TRUE,  FALSE },
+    { "^NDISSTAT: 1,,,IPV4",     TRUE,  TRUE,  FALSE, FALSE },
+    { "^NDISSTAT: 0,,,IPV4",     TRUE,  FALSE, FALSE, FALSE },
+    { "^NDISSTAT: 1,,,IPV6",     FALSE, FALSE, TRUE,  TRUE  },
+    { "^NDISSTAT: 0,,,IPV6",     FALSE, FALSE, TRUE,  FALSE },
+    { "^NDISSTAT: 1,,,IPV4\r\n"
+      "^NDISSTAT: 1,,,IPV6",     TRUE,  TRUE,  TRUE,  TRUE  },
+    { "^NDISSTAT: 1,,,IPV4\r\n"
+      "^NDISSTAT: 0,,,IPV6",     TRUE,  TRUE,  TRUE,  FALSE },
+    { "^NDISSTAT: 0,,,IPV4\r\n"
+      "^NDISSTAT: 1,,,IPV6",     TRUE,  FALSE, TRUE,  TRUE  },
+    { "^NDISSTAT: 0,,,IPV4\r\n"
+      "^NDISSTAT: 0,,,IPV6",     TRUE,  FALSE, TRUE,  FALSE },
+    { "^NDISSTAT: 1,,,\"IPV4\",1,,,\"IPV6\"",     TRUE,  TRUE,  TRUE,  TRUE  },
+    { "^NDISSTAT: 1,,,\"IPV4\",0,,,\"IPV6\"",     TRUE,  TRUE,  TRUE,  FALSE },
+    { "^NDISSTAT: 0,,,\"IPV4\",1,,,\"IPV6\"",     TRUE,  FALSE, TRUE,  TRUE  },
+    { "^NDISSTAT: 0,,,\"IPV4\",0,,,\"IPV6\"",     TRUE,  FALSE, TRUE,  FALSE },
+    { "^NDISSTAT: 1,,,\"IPV4\",1,,,\"IPV6\"\r\n", TRUE,  TRUE,  TRUE,  TRUE  },
+    { "^NDISSTAT: 1,,,\"IPV4\",0,,,\"IPV6\"\r\n", TRUE,  TRUE,  TRUE,  FALSE },
+    { "^NDISSTAT: 0,,,\"IPV4\",1,,,\"IPV6\"\r\n", TRUE,  FALSE, TRUE,  TRUE  },
+    { "^NDISSTAT: 0,,,\"IPV4\",0,,,\"IPV6\"\r\n", TRUE,  FALSE, TRUE,  FALSE },
     { "^NDISSTATQRY: 1,,,IPV4\r\n", TRUE,  TRUE,  FALSE, FALSE },
     { "^NDISSTATQRY: 0,,,IPV4\r\n", TRUE,  FALSE, FALSE, FALSE },
     { "^NDISSTATQRY: 1,,,IPV6\r\n", FALSE, FALSE, TRUE,  TRUE  },
-- 
1.8.4



More information about the ModemManager-devel mailing list