[PATCH 1/8] helpers: new parser for AT+IFC=?

Aleksander Morgado aleksander at aleksander.es
Sat Mar 25 18:32:10 UTC 2017


Instead of having the parser return separate list of supported flow
controls for TE and TA, we simplify it by only returning those
settings that apply to both TE and TA.

This logic isn't perfect either, though, as some settings (e.g. '3' in
TE in some modems, specifying a different XON/XOFF behavior) may not
have a corresponding setting in the other end.

The most common cases we care about (i.e. standard XON/XOFF, RTS/CTS)
should be properly reported with this logic.
---
 src/mm-modem-helpers.c         | 113 +++++++++++++++++++++++++++++++++++++++++
 src/mm-modem-helpers.h         |  17 +++++++
 src/tests/test-modem-helpers.c | 100 ++++++++++++++++++++++++++++++++++++
 3 files changed, 230 insertions(+)

diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index fc95e28f..587e6896 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -525,6 +525,119 @@ mm_voice_clip_regex_get (void)
 
 /*************************************************************************/
 
+static MMFlowControl
+flow_control_array_to_mask (GArray      *array,
+                            const gchar *item)
+{
+    MMFlowControl mask = MM_FLOW_CONTROL_UNKNOWN;
+    guint         i;
+
+    for (i = 0; i < array->len; i++) {
+        guint mode;
+
+        mode = g_array_index (array, guint, i);
+        switch (mode) {
+            case 0:
+                mm_dbg ("%s supports no flow control", item);
+                mask |= MM_FLOW_CONTROL_NONE;
+                break;
+            case 1:
+                mm_dbg ("%s supports XON/XOFF flow control", item);
+                mask |= MM_FLOW_CONTROL_XON_XOFF;
+                break;
+            case 2:
+                mm_dbg ("%s supports RTS/CTS flow control", item);
+                mask |= MM_FLOW_CONTROL_RTS_CTS;
+                break;
+            default:
+                break;
+        }
+    }
+
+    return mask;
+}
+
+static MMFlowControl
+flow_control_match_info_to_mask (GMatchInfo   *match_info,
+                                 guint         index,
+                                 const gchar  *item,
+                                 GError      **error)
+{
+    MMFlowControl  mask  = MM_FLOW_CONTROL_UNKNOWN;
+    gchar         *aux   = NULL;
+    GArray        *array = NULL;
+
+    if (!(aux = mm_get_string_unquoted_from_match_info (match_info, index))) {
+        g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+                     "Error retrieving list of supported %s flow control methods", item);
+        goto out;
+    }
+
+    if (!(array = mm_parse_uint_list (aux, error))) {
+        g_prefix_error (error, "Error parsing list of supported %s flow control methods: ", item);
+        goto out;
+    }
+
+    if ((mask = flow_control_array_to_mask (array, item)) == MM_FLOW_CONTROL_UNKNOWN) {
+        g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+                     "No known %s flow control method given", item);
+        goto out;
+    }
+
+out:
+    g_clear_pointer (&aux,  g_free);
+    g_clear_pointer (&array, g_array_unref);
+
+    return mask;
+}
+
+MMFlowControl
+mm_parse_ifc_test_response (const gchar  *response,
+                            GError      **error)
+{
+    GRegex        *r;
+    GError        *inner_error = NULL;
+    GMatchInfo    *match_info  = NULL;
+    MMFlowControl  te_mask     = MM_FLOW_CONTROL_UNKNOWN;
+    MMFlowControl  ta_mask     = MM_FLOW_CONTROL_UNKNOWN;
+    MMFlowControl  mask        = MM_FLOW_CONTROL_UNKNOWN;
+
+    r = g_regex_new ("(?:\\+IFC:)?\\s*\\((.*)\\),\\((.*)\\)(?:\\r\\n)?", 0, 0, NULL);
+    g_assert (r != NULL);
+
+    g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+    if (inner_error)
+        goto out;
+
+    if (!g_match_info_matches (match_info)) {
+        inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response");
+        goto out;
+    }
+
+    /* Parse TE flow control methods */
+    if ((te_mask = flow_control_match_info_to_mask (match_info, 1, "TE", &inner_error)) == MM_FLOW_CONTROL_UNKNOWN)
+        goto out;
+
+    /* Parse TA flow control methods */
+    if ((ta_mask = flow_control_match_info_to_mask (match_info, 2, "TA", &inner_error)) == MM_FLOW_CONTROL_UNKNOWN)
+        goto out;
+
+    /* Only those methods in both TA and TE will be the ones we report */
+    mask = te_mask & ta_mask;
+
+out:
+
+    g_clear_pointer (&match_info, g_match_info_free);
+    g_regex_unref (r);
+
+    if (inner_error)
+        g_propagate_error (error, inner_error);
+
+    return mask;
+}
+
+/*************************************************************************/
+
 /* +CREG: <stat>                      (GSM 07.07 CREG=1 unsolicited) */
 #define CREG1 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9])"
 
diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h
index 33af48b6..ea5ba422 100644
--- a/src/mm-modem-helpers.h
+++ b/src/mm-modem-helpers.h
@@ -95,6 +95,23 @@ GRegex *mm_voice_cring_regex_get(void);
 GRegex *mm_voice_clip_regex_get (void);
 
 /*****************************************************************************/
+/* SERIAL specific helpers and utilities */
+
+/* AT+IFC=? response parser.
+ * For simplicity, we'll only consider flow control methods available in both
+ * TE and TA. */
+
+typedef enum {
+    MM_FLOW_CONTROL_UNKNOWN   = 0,
+    MM_FLOW_CONTROL_NONE      = 1 << 0,  /* IFC=0,0 */
+    MM_FLOW_CONTROL_XON_XOFF  = 1 << 1,  /* IFC=1,1 */
+    MM_FLOW_CONTROL_RTS_CTS   = 1 << 2,  /* IFC=2,2 */
+} MMFlowControl;
+
+MMFlowControl mm_parse_ifc_test_response (const gchar  *response,
+                                          GError      **error);
+
+/*****************************************************************************/
 /* 3GPP specific helpers and utilities */
 /*****************************************************************************/
 
diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c
index 1fc8c353..a6e2f655 100644
--- a/src/tests/test-modem-helpers.c
+++ b/src/tests/test-modem-helpers.c
@@ -33,6 +33,93 @@
     g_assert_cmpfloat (fabs (val1 - val2), <, tolerance)
 
 /*****************************************************************************/
+/* Test IFC=? responses */
+
+static void
+test_ifc_response (const gchar         *str,
+                   const MMFlowControl  expected)
+{
+    MMFlowControl  mask;
+    GError        *error = NULL;
+
+    mask = mm_parse_ifc_test_response (str, &error);
+    g_assert_no_error (error);
+    g_assert_cmpuint (mask, ==, expected);
+}
+
+static void
+test_ifc_response_all_simple (void)
+{
+    test_ifc_response ("+IFC (0,1,2),(0,1,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF | MM_FLOW_CONTROL_RTS_CTS));
+}
+
+static void
+test_ifc_response_all_groups (void)
+{
+    test_ifc_response ("+IFC (0-2),(0-2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF | MM_FLOW_CONTROL_RTS_CTS));
+}
+
+static void
+test_ifc_response_none_only (void)
+{
+    test_ifc_response ("+IFC (0),(0)", MM_FLOW_CONTROL_NONE);
+}
+
+static void
+test_ifc_response_xon_xoff_only (void)
+{
+    test_ifc_response ("+IFC (1),(1)", MM_FLOW_CONTROL_XON_XOFF);
+}
+
+static void
+test_ifc_response_rts_cts_only (void)
+{
+    test_ifc_response ("+IFC (2),(2)", MM_FLOW_CONTROL_RTS_CTS);
+}
+
+static void
+test_ifc_response_no_xon_xoff (void)
+{
+    test_ifc_response ("+IFC (0,2),(0,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_RTS_CTS));
+}
+
+static void
+test_ifc_response_no_xon_xoff_in_ta (void)
+{
+    test_ifc_response ("+IFC (0,1,2),(0,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_RTS_CTS));
+}
+
+static void
+test_ifc_response_no_xon_xoff_in_te (void)
+{
+    test_ifc_response ("+IFC (0,2),(0,1,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_RTS_CTS));
+}
+
+static void
+test_ifc_response_no_rts_cts_simple (void)
+{
+    test_ifc_response ("+IFC (0,1),(0,1)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF));
+}
+
+static void
+test_ifc_response_no_rts_cts_groups (void)
+{
+    test_ifc_response ("+IFC (0-1),(0-1)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF));
+}
+
+static void
+test_ifc_response_all_simple_and_unknown (void)
+{
+    test_ifc_response ("+IFC (0,1,2,3),(0,1,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF | MM_FLOW_CONTROL_RTS_CTS));
+}
+
+static void
+test_ifc_response_all_groups_and_unknown (void)
+{
+    test_ifc_response ("+IFC (0-3),(0-2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF | MM_FLOW_CONTROL_RTS_CTS));
+}
+
+/*****************************************************************************/
 /* Test WS46=? responses */
 
 static void
@@ -3488,6 +3575,19 @@ int main (int argc, char **argv)
     suite = g_test_get_root ();
     reg_data = reg_test_data_new ();
 
+    g_test_suite_add (suite, TESTCASE (test_ifc_response_all_simple, NULL));
+    g_test_suite_add (suite, TESTCASE (test_ifc_response_all_groups, NULL));
+    g_test_suite_add (suite, TESTCASE (test_ifc_response_none_only, NULL));
+    g_test_suite_add (suite, TESTCASE (test_ifc_response_xon_xoff_only, NULL));
+    g_test_suite_add (suite, TESTCASE (test_ifc_response_rts_cts_only, NULL));
+    g_test_suite_add (suite, TESTCASE (test_ifc_response_no_xon_xoff, NULL));
+    g_test_suite_add (suite, TESTCASE (test_ifc_response_no_xon_xoff_in_ta, NULL));
+    g_test_suite_add (suite, TESTCASE (test_ifc_response_no_xon_xoff_in_te, NULL));
+    g_test_suite_add (suite, TESTCASE (test_ifc_response_no_rts_cts_simple, NULL));
+    g_test_suite_add (suite, TESTCASE (test_ifc_response_no_rts_cts_groups, NULL));
+    g_test_suite_add (suite, TESTCASE (test_ifc_response_all_simple_and_unknown, NULL));
+    g_test_suite_add (suite, TESTCASE (test_ifc_response_all_groups_and_unknown, NULL));
+
     g_test_suite_add (suite, TESTCASE (test_ws46_response_generic_2g3g4g, NULL));
     g_test_suite_add (suite, TESTCASE (test_ws46_response_generic_2g3g, NULL));
     g_test_suite_add (suite, TESTCASE (test_ws46_response_generic_2g3g_v2, NULL));
-- 
2.12.0



More information about the ModemManager-devel mailing list