[PATCHv2] mbm: query supported modes to the modem with +CFUN=?

Aleksander Morgado aleksander at aleksander.es
Mon Dec 21 08:36:44 PST 2015


We were trying to load the generic modes supported reported by either *CNTI=2 or
AT+WS46=?, so that then we could filter out the MBM-specific modes unsupported.

But, this may not be ideal, as both these two commands may fail:

    [mm-broadband-modem.c:1612] modem_load_supported_modes(): loading supported modes...
    [mm-port-serial.c:1237] mm_port_serial_open(): (ttyACM1) device open count is 3 (open)
    [mm-port-serial.c:1294] _close_internal(): (ttyACM1) device open count is 2 (close)
    [mm-port-serial-at.c:440] debug_log(): (ttyACM1): --> 'AT*CNTI=2<CR>'
    [mm-port-serial-at.c:440] debug_log(): (ttyACM1): <-- '<CR><LF>ERROR<CR><LF>'
    [mm-serial-parsers.c:364] mm_serial_parser_v1_parse(): Got failure code 100: Unknown error
    [mm-broadband-modem.c:1546] supported_modes_cnti_ready(): Generic query of supported 3GPP networks with *CNTI failed: 'Unknown error'
    [mm-port-serial.c:1237] mm_port_serial_open(): (ttyACM1) device open count is 3 (open)
    [mm-port-serial.c:1294] _close_internal(): (ttyACM1) device open count is 2 (close)
    [mm-port-serial-at.c:440] debug_log(): (ttyACM1): --> 'AT+WS46=?<CR>'
    [mm-port-serial-at.c:440] debug_log(): (ttyACM1): <-- '<CR><LF>ERROR<CR><LF>'
    [mm-serial-parsers.c:364] mm_serial_parser_v1_parse(): Got failure code 100: Unknown error
    [mm-broadband-modem.c:1494] supported_modes_ws46_test_ready(): Generic query of supported 3GPP networks with WS46=? failed: 'Unknown error'
    [mm-iface-modem.c:3974] load_supported_modes_ready(): couldn't load Supported Modes: 'Couldn't retrieve supported modes'

Instead, we'll ask the modem for the list of modes supported, and return that
directly.
---
 plugins/mbm/mm-broadband-modem-mbm.c       |  93 ++++++++++-----------
 plugins/mbm/mm-modem-helpers-mbm.c         | 100 ++++++++++++++++++++++-
 plugins/mbm/mm-modem-helpers-mbm.h         |  14 ++++
 plugins/mbm/tests/test-modem-helpers-mbm.c |  62 +++++++++++++-
 src/mm-modem-helpers.c                     | 126 +++++++++++++++--------------
 src/mm-modem-helpers.h                     |   2 +
 6 files changed, 281 insertions(+), 116 deletions(-)

diff --git a/plugins/mbm/mm-broadband-modem-mbm.c b/plugins/mbm/mm-broadband-modem-mbm.c
index fa291d6..dc480f8 100644
--- a/plugins/mbm/mm-broadband-modem-mbm.c
+++ b/plugins/mbm/mm-broadband-modem-mbm.c
@@ -35,6 +35,7 @@
 #include "mm-bearer-list.h"
 #include "mm-errors-types.h"
 #include "mm-modem-helpers.h"
+#include "mm-modem-helpers-mbm.h"
 #include "mm-broadband-modem-mbm.h"
 #include "mm-broadband-bearer-mbm.h"
 #include "mm-sim-mbm.h"
@@ -59,12 +60,6 @@ G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbm, mm_broadband_modem_mbm, MM_TYPE_BRO
                         G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
                         G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init))

-#define MBM_NETWORK_MODE_OFFLINE   0
-#define MBM_NETWORK_MODE_ANY       1
-#define MBM_NETWORK_MODE_LOW_POWER 4
-#define MBM_NETWORK_MODE_2G        5
-#define MBM_NETWORK_MODE_3G        6
-
 #define MBM_E2NAP_DISCONNECTED 0
 #define MBM_E2NAP_CONNECTED    1
 #define MBM_E2NAP_CONNECTING   2
@@ -204,59 +199,55 @@ modem_after_sim_unlock (MMIfaceModem *self,
 /* Load supported modes (Modem interface) */

 static GArray *
-load_supported_modes_finish (MMIfaceModem *self,
+load_supported_modes_finish (MMIfaceModem *_self,
                              GAsyncResult *res,
                              GError **error)
 {
-    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
-        return NULL;
-
-    return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
-}
-
-static void
-parent_load_supported_modes_ready (MMIfaceModem *self,
-                                   GAsyncResult *res,
-                                   GSimpleAsyncResult *simple)
-{
-    GError *error = NULL;
-    GArray *all;
+    MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self);
+    const gchar *response;
+    guint32 mask =  0;
     GArray *combinations;
-    GArray *filtered;
     MMModemModeCombination mode;

-    all = iface_modem_parent->load_supported_modes_finish (self, res, &error);
-    if (!all) {
-        g_simple_async_result_take_error (simple, error);
-        g_simple_async_result_complete (simple);
-        g_object_unref (simple);
-        return;
-    }
+    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+    if (!response)
+        return FALSE;
+
+    if (!mm_mbm_parse_cfun_test (response, &mask, error))
+        return FALSE;

     /* Build list of combinations */
     combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 3);

     /* 2G only */
-    mode.allowed = MM_MODEM_MODE_2G;
-    mode.preferred = MM_MODEM_MODE_NONE;
-    g_array_append_val (combinations, mode);
+    if (mask & (1 << MBM_NETWORK_MODE_2G)) {
+        mode.allowed = MM_MODEM_MODE_2G;
+        mode.preferred = MM_MODEM_MODE_NONE;
+        g_array_append_val (combinations, mode);
+    }
+
     /* 3G only */
-    mode.allowed = MM_MODEM_MODE_3G;
-    mode.preferred = MM_MODEM_MODE_NONE;
-    g_array_append_val (combinations, mode);
+    if (mask & (1 << MBM_NETWORK_MODE_3G)) {
+        mode.allowed = MM_MODEM_MODE_3G;
+        mode.preferred = MM_MODEM_MODE_NONE;
+        g_array_append_val (combinations, mode);
+    }
+
     /* 2G and 3G */
-    mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
-    mode.preferred = MM_MODEM_MODE_NONE;
-    g_array_append_val (combinations, mode);
+    if (mask & (1 << MBM_NETWORK_MODE_ANY)) {
+        mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+        mode.preferred = MM_MODEM_MODE_NONE;
+        g_array_append_val (combinations, mode);
+    }

-    /* Filter out those unsupported modes */
-    filtered = mm_filter_supported_modes (all, combinations);
-    g_array_unref (all);
-    g_array_unref (combinations);
+    if (combinations->len == 0) {
+        g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+                             "Couldn't load any supported mode");
+        g_array_unref (combinations);
+        return NULL;
+    }

-    g_simple_async_result_set_op_res_gpointer (simple, filtered, (GDestroyNotify) g_array_unref);
-    g_simple_async_result_complete (simple);
-    g_object_unref (simple);
+    return combinations;
 }

 static void
@@ -264,14 +255,12 @@ load_supported_modes (MMIfaceModem *self,
                       GAsyncReadyCallback callback,
                       gpointer user_data)
 {
-    /* Run parent's loading */
-    iface_modem_parent->load_supported_modes (
-        MM_IFACE_MODEM (self),
-        (GAsyncReadyCallback)parent_load_supported_modes_ready,
-        g_simple_async_result_new (G_OBJECT (self),
-                                   callback,
-                                   user_data,
-                                   load_supported_modes));
+    mm_base_modem_at_command (MM_BASE_MODEM (self),
+                              "+CFUN=?",
+                              3,
+                              FALSE,
+                              callback,
+                              user_data);
 }

 /*****************************************************************************/
diff --git a/plugins/mbm/mm-modem-helpers-mbm.c b/plugins/mbm/mm-modem-helpers-mbm.c
index 42653d8..55557f2 100644
--- a/plugins/mbm/mm-modem-helpers-mbm.c
+++ b/plugins/mbm/mm-modem-helpers-mbm.c
@@ -89,7 +89,7 @@ mm_mbm_parse_e2ipcfg_response (const gchar *response,
     }

     /* *E2IPCFG: (1,<IP>)(2,<gateway>)(3,<DNS>)(3,<DNS>)
-     *
+     *
      * *E2IPCFG: (1,"46.157.32.246")(2,"46.157.32.243")(3,"193.213.112.4")(3,"130.67.15.198")
      * *E2IPCFG: (1,"fe80:0000:0000:0000:0000:0000:e537:1801")(3,"2001:4600:0004:0fff:0000:0000:0000:0054")(3,"2001:4600:0004:1fff:0000:0000:0000:0054")
      * *E2IPCFG: (1,"fe80:0000:0000:0000:0000:0027:b7fe:9401")(3,"fd00:976a:0000:0000:0000:0000:0000:0009")
@@ -164,3 +164,101 @@ done:
     return !!*ip_config;
 }

+/*****************************************************************************/
+
+#define CFUN_TAG "+CFUN:"
+
+static void
+add_supported_mode (guint32 *mask,
+                    guint mode)
+{
+    g_assert (mask);
+
+    if (mode >= 32)
+        g_warning ("Ignored unexpected mode in +CFUN match: %d", mode);
+    else
+        *mask |= (1 << mode);
+}
+
+gboolean
+mm_mbm_parse_cfun_test (const gchar *response,
+                        guint32 *supported_mask,
+                        GError **error)
+{
+    gchar **groups;
+    guint32 mask = 0;
+
+    g_assert (supported_mask);
+
+    if (!response || !g_str_has_prefix (response, CFUN_TAG)) {
+        g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+                             "Missing " CFUN_TAG " prefix");
+        return FALSE;
+    }
+
+    /*
+     * AT+CFUN=?
+     * +CFUN: (0,1,4-6),(0,1)
+     * OK
+     */
+
+    /* Strip tag from response */
+    response = mm_strip_tag (response, CFUN_TAG);
+
+    /* Split response in (groups) */
+    groups = mm_split_string_groups (response);
+
+    /* First group is the one listing supported modes */
+    if (groups && groups[0]) {
+        gchar **supported_modes;
+
+        supported_modes = g_strsplit_set (groups[0], ", ", -1);
+        if (supported_modes) {
+            guint i;
+
+            for (i = 0; supported_modes[i]; i++) {
+                gchar *separator;
+                guint mode;
+
+                if (!supported_modes[i][0])
+                    continue;
+
+                /* Check if this is a range that's being given to us */
+                separator = strchr (supported_modes[i], '-');
+                if (separator) {
+                    gchar *first_str;
+                    gchar *last_str;
+                    guint first;
+                    guint last;
+
+                    *separator = '\0';
+                    first_str = supported_modes[i];
+                    last_str = separator + 1;
+
+                    if (!mm_get_uint_from_str (first_str, &first))
+                        g_warning ("Couldn't match range start: '%s'", first_str);
+                    else if (!mm_get_uint_from_str (last_str, &last))
+                        g_warning ("Couldn't match range stop: '%s'", last_str);
+                    else if (first >= last)
+                        g_warning ("Couldn't match range: wrong first '%s' and last '%s' items", first_str, last_str);
+                    else {
+                        for (mode = first; mode <= last; mode++)
+                            add_supported_mode (&mask, mode);
+                    }
+                } else {
+                    if (!mm_get_uint_from_str (supported_modes[i], &mode))
+                        g_warning ("Couldn't match mode: '%s'", supported_modes[i]);
+                    else
+                        add_supported_mode (&mask, mode);
+                }
+            }
+
+            g_strfreev (supported_modes);
+        }
+    }
+    g_strfreev (groups);
+
+    if (mask)
+        *supported_mask = mask;
+    return !!mask;
+}
diff --git a/plugins/mbm/mm-modem-helpers-mbm.h b/plugins/mbm/mm-modem-helpers-mbm.h
index ef15845..7001cd6 100644
--- a/plugins/mbm/mm-modem-helpers-mbm.h
+++ b/plugins/mbm/mm-modem-helpers-mbm.h
@@ -24,4 +24,18 @@ gboolean mm_mbm_parse_e2ipcfg_response (const gchar *response,
                                         MMBearerIpConfig **out_ip6_config,
                                         GError **error);

+typedef enum {
+    MBM_NETWORK_MODE_OFFLINE   = 0,
+    MBM_NETWORK_MODE_ANY       = 1,
+    MBM_NETWORK_MODE_LOW_POWER = 4,
+    MBM_NETWORK_MODE_2G        = 5,
+    MBM_NETWORK_MODE_3G        = 6,
+} MbmNetworkMode;
+
+/* AT+CFUN=? test parser
+ * Returns a bitmask, bit index set for the supported modes reported */
+gboolean mm_mbm_parse_cfun_test (const gchar *response,
+                                 guint32 *supported_mask,
+                                 GError **error);
+
 #endif  /* MM_MODEM_HELPERS_MBM_H */
diff --git a/plugins/mbm/tests/test-modem-helpers-mbm.c b/plugins/mbm/tests/test-modem-helpers-mbm.c
index 0c48894..79a5dbe 100644
--- a/plugins/mbm/tests/test-modem-helpers-mbm.c
+++ b/plugins/mbm/tests/test-modem-helpers-mbm.c
@@ -28,6 +28,8 @@
 #include "mm-modem-helpers.h"
 #include "mm-modem-helpers-mbm.h"

+#define ENABLE_TEST_MESSAGE_TRACES
+
 /*****************************************************************************/
 /* Test *E2IPCFG responses */

@@ -47,7 +49,7 @@ typedef struct {
 } E2ipcfgTest;

 static const E2ipcfgTest tests[] = {
-    { "*E2IPCFG: (1,\"46.157.32.246\")(2,\"46.157.32.243\")(3,\"193.213.112.4\")(3,\"130.67.15.198\")\r\n",
+    { "*E2IPCFG: (1,\"46.157.32.246\")(2,\"46.157.32.243\")(3,\"193.213.112.4\")(3,\"130.67.15.198\")\r\n",
         "46.157.32.246", "46.157.32.243", "193.213.112.4", "130.67.15.198",
         NULL, NULL },

@@ -130,6 +132,63 @@ test_e2ipcfg (void)
 }

 /*****************************************************************************/
+/* Test +CFUN test responses */
+
+#define MAX_MODES 32
+
+typedef struct {
+    const gchar *str;
+    guint32 expected_mask;
+} CfunTest;
+
+static const CfunTest cfun_tests[] = {
+    {
+        "+CFUN: (0,1,4-6),(1-0)\r\n",
+        ((1 << MBM_NETWORK_MODE_OFFLINE)   |
+         (1 << MBM_NETWORK_MODE_ANY)       |
+         (1 << MBM_NETWORK_MODE_LOW_POWER) |
+         (1 << MBM_NETWORK_MODE_2G)        |
+         (1 << MBM_NETWORK_MODE_3G))
+    },
+    {
+        "+CFUN: (0,1,4-6)\r\n",
+        ((1 << MBM_NETWORK_MODE_OFFLINE)   |
+         (1 << MBM_NETWORK_MODE_ANY)       |
+         (1 << MBM_NETWORK_MODE_LOW_POWER) |
+         (1 << MBM_NETWORK_MODE_2G)        |
+         (1 << MBM_NETWORK_MODE_3G))
+    },
+    {
+        "+CFUN: (0,1,4)\r\n",
+        ((1 << MBM_NETWORK_MODE_OFFLINE)   |
+         (1 << MBM_NETWORK_MODE_ANY)       |
+         (1 << MBM_NETWORK_MODE_LOW_POWER))
+    },
+    {
+        "+CFUN: (0,1)\r\n",
+        ((1 << MBM_NETWORK_MODE_OFFLINE)   |
+         (1 << MBM_NETWORK_MODE_ANY))
+    },
+};
+
+static void
+test_cfun (void)
+{
+    guint i;
+
+    for (i = 0; i < G_N_ELEMENTS (cfun_tests); i++) {
+        guint32 mask;
+        gboolean success;
+        GError *error = NULL;
+
+        success = mm_mbm_parse_cfun_test (cfun_tests[i].str, &mask, &error);
+        g_assert_no_error (error);
+        g_assert (success);
+        g_assert_cmpuint (mask, ==, cfun_tests[i].expected_mask);
+    }
+}
+
+/*****************************************************************************/

 void
 _mm_log (const char *loc,
@@ -159,6 +218,7 @@ int main (int argc, char **argv)
     g_test_init (&argc, &argv, NULL);

     g_test_add_func ("/MM/mbm/e2ipcfg", test_e2ipcfg);
+    g_test_add_func ("/MM/mbm/cfun", test_cfun);

     return g_test_run ();
 }
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index 19fe81e..17ad8d2 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -68,6 +68,69 @@ mm_strip_tag (const gchar *str, const gchar *cmd)

 /*****************************************************************************/

+gchar **
+mm_split_string_groups (const gchar *str)
+{
+    GPtrArray *array;
+    const gchar *start;
+    const gchar *next;
+
+    array = g_ptr_array_new ();
+
+    /*
+     * Manually parse splitting groups. Groups may be single elements, or otherwise
+     * lists given between parenthesis, e.g.:
+     *
+     *    ("SM","ME"),("SM","ME"),("SM","ME")
+     *    "SM","SM","SM"
+     *    "SM",("SM","ME"),("SM","ME")
+     */
+
+    /* Iterate string splitting groups */
+    for (start = str; start; start = next) {
+        gchar *item;
+        gssize len = -1;
+
+        /* skip leading whitespaces */
+        while (*start == ' ')
+            start++;
+
+        if (*start == '(') {
+            start++;
+            next = strchr (start, ')');
+            if (next) {
+                len = next - start;
+                next = strchr (next, ',');
+                if (next)
+                    next++;
+            }
+        } else {
+            next = strchr (start, ',');
+            if (next) {
+                len = next - start;
+                next++;
+            }
+        }
+
+        if (len < 0)
+            item = g_strdup (start);
+        else
+            item = g_strndup (start, len);
+
+        g_ptr_array_add (array, item);
+    }
+
+    if (array->len > 0) {
+        g_ptr_array_add (array, NULL);
+        return (gchar **) g_ptr_array_free (array, FALSE);
+    }
+
+    g_ptr_array_unref (array);
+    return NULL;
+}
+
+/*****************************************************************************/
+
 guint
 mm_count_bits_set (gulong number)
 {
@@ -1290,67 +1353,6 @@ storage_from_str (const gchar *str)
     return MM_SMS_STORAGE_UNKNOWN;
 }

-static gchar **
-helper_split_groups (const gchar *str)
-{
-    GPtrArray *array;
-    const gchar *start;
-    const gchar *next;
-
-    array = g_ptr_array_new ();
-
-    /*
-     * Manually parse splitting groups. Groups may be single elements, or otherwise
-     * lists given between parenthesis, e.g.:
-     *
-     *    ("SM","ME"),("SM","ME"),("SM","ME")
-     *    "SM","SM","SM"
-     *    "SM",("SM","ME"),("SM","ME")
-     */
-
-    /* Iterate string splitting groups */
-    for (start = str; start; start = next) {
-        gchar *item;
-        gssize len = -1;
-
-        /* skip leading whitespaces */
-        while (*start == ' ')
-            start++;
-
-        if (*start == '(') {
-            start++;
-            next = strchr (start, ')');
-            if (next) {
-                len = next - start;
-                next = strchr (next, ',');
-                if (next)
-                    next++;
-            }
-        } else {
-            next = strchr (start, ',');
-            if (next) {
-                len = next - start;
-                next++;
-            }
-        }
-
-        if (len < 0)
-            item = g_strdup (start);
-        else
-            item = g_strndup (start, len);
-
-        g_ptr_array_add (array, item);
-    }
-
-    if (array->len > 0) {
-        g_ptr_array_add (array, NULL);
-        return (gchar **) g_ptr_array_free (array, FALSE);
-    }
-
-    g_ptr_array_unref (array);
-    return NULL;
-}
-
 gboolean
 mm_3gpp_parse_cpms_test_response (const gchar *reply,
                                   GArray **mem1,
@@ -1370,7 +1372,7 @@ mm_3gpp_parse_cpms_test_response (const gchar *reply,

 #define N_EXPECTED_GROUPS 3

-    split = helper_split_groups (mm_strip_tag (reply, "+CPMS:"));
+    split = mm_split_string_groups (mm_strip_tag (reply, "+CPMS:"));
     if (!split)
         return FALSE;

diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h
index e92b6c0..3be7c7b 100644
--- a/src/mm-modem-helpers.h
+++ b/src/mm-modem-helpers.h
@@ -51,6 +51,8 @@ gchar       *mm_strip_quotes (gchar *str);
 const gchar *mm_strip_tag    (const gchar *str,
                               const gchar *cmd);

+gchar **mm_split_string_groups (const gchar *str);
+
 guint mm_count_bits_set (gulong number);

 gchar *mm_create_device_identifier (guint vid,
--
2.6.2


More information about the ModemManager-devel mailing list