helpers: new parser for AT+IFC=?

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.
This commit is contained in:
Aleksander Morgado
2017-03-25 00:47:50 +01:00
parent 75d9c0bc95
commit 973c7d0970
3 changed files with 230 additions and 0 deletions

View File

@@ -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])"

View File

@@ -94,6 +94,23 @@ GRegex *mm_voice_ring_regex_get (void);
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 */
/*****************************************************************************/

View File

@@ -32,6 +32,93 @@
#define g_assert_cmpfloat_tolerance(val1, val2, tolerance) \
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 */
@@ -3536,6 +3623,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));