diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2016-11-24 01:53:11 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2017-01-04 14:11:17 +0100 |
commit | 37521ed2b611bdccd288cabc5fc4e48c51a32e49 (patch) | |
tree | f7349c269b04c5f687ae62ae15c9bc85a2b053cb | |
parent | dc96829bb5a49216e45e770f249f29e725ce97ad (diff) |
cinterion: simplify ^SWWAN response parsing
We get as input the ^SWWAN index we're interested in, and we loop
through the list of ^SWWAN lines looking for the one we need.
This updated helper method allows working with multi-line ^SWWAN
responses, e.g. given when more than one PDP context is active.
-rw-r--r-- | plugins/cinterion/mm-broadband-bearer-cinterion.c | 98 | ||||
-rw-r--r-- | plugins/cinterion/mm-modem-helpers-cinterion.c | 134 | ||||
-rw-r--r-- | plugins/cinterion/mm-modem-helpers-cinterion.h | 14 | ||||
-rw-r--r-- | plugins/cinterion/tests/test-modem-helpers-cinterion.c | 270 |
4 files changed, 254 insertions, 262 deletions
diff --git a/plugins/cinterion/mm-broadband-bearer-cinterion.c b/plugins/cinterion/mm-broadband-bearer-cinterion.c index a1153b92..ed4ed176 100644 --- a/plugins/cinterion/mm-broadband-bearer-cinterion.c +++ b/plugins/cinterion/mm-broadband-bearer-cinterion.c @@ -87,49 +87,6 @@ get_usb_interface_config_index (MMPort *data, } /*****************************************************************************/ -/* Common - Helper Functions*/ - -static gint -verify_connection_state_from_swwan_response (GList *result, GError **error) -{ - /* Returns 0 if SWWAN is connected, 1 if not connected, -1 on error - * for the bearer's target interface */ - - if (g_list_length(result) != 0) { - int first_result = GPOINTER_TO_INT(result->data); - - /* Received an 'OK'(0) response */ - if (first_result == 0) - return 1; - /* 1 || 3 result is the CID, given when that context is activated. - * TODO: Rework for dual sim connections. */ - else if (first_result == 1 || first_result ==3) - return 0; - else { - for (; result; result = g_list_next(result)) - mm_err ("Unknown SWWAN response data:%i", GPOINTER_TO_INT(result->data)); - - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_INVALID_ARGS, - "Unparsable SWWAN response format."); - - return -1; - } - } - else { - mm_err ("Unable to parse zero length SWWAN response."); - - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_INVALID_ARGS, - "Unparsable SWWAN response format."); - - return -1; - } -} - -/*****************************************************************************/ /* Auth helpers */ typedef enum { @@ -254,9 +211,9 @@ swwan_connect_check_status_ready (MMBaseModem *modem, GAsyncResult *res, Connect3gppContext *ctx) { - const gchar *response; - GList *response_parsed = NULL; - GError *error = NULL; + const gchar *response; + GError *error = NULL; + MMSwwanState state; response = mm_base_modem_at_command_full_finish (modem, res, &error); if (!response) { @@ -265,22 +222,24 @@ swwan_connect_check_status_ready (MMBaseModem *modem, return; } - /* Error if parsing SWWAN read response fails */ - if (!mm_cinterion_parse_swwan_response (response, &response_parsed, &error)) { + state = mm_cinterion_parse_swwan_response (response, + usb_interface_configs[ctx->usb_interface_config_index].pdp_context, + &error); + if (state == MM_SWWAN_STATE_UNKNOWN) { g_simple_async_result_take_error (ctx->result, error); connect_3gpp_context_complete_and_free (ctx); return; } - /* Check parsed SWWAN reponse to see if we are now connected */ - if (verify_connection_state_from_swwan_response (response_parsed, &error)) { - g_simple_async_result_take_error (ctx->result, error); + if (state == MM_SWWAN_STATE_DISCONNECTED) { + g_simple_async_result_set_error (ctx->result, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "CID %u is reported disconnected", + usb_interface_configs[ctx->usb_interface_config_index].pdp_context); connect_3gpp_context_complete_and_free (ctx); - g_list_free (response_parsed); return; } - g_list_free (response_parsed); + g_assert (state == MM_SWWAN_STATE_CONNECTED); /* Go to next step */ ctx->step++; @@ -575,9 +534,9 @@ swwan_disconnect_check_status_ready (MMBaseModem *modem, GAsyncResult *res, Disconnect3gppContext *ctx) { - const gchar *response; - GList *response_parsed = NULL; - GError *error = NULL; + const gchar *response; + GError *error = NULL; + MMSwwanState state; response = mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { @@ -586,23 +545,26 @@ swwan_disconnect_check_status_ready (MMBaseModem *modem, return; } - /* Return if the SWWAN read threw an error or parsing it fails */ - if (!mm_cinterion_parse_swwan_response (response, &response_parsed, &error)) { - g_simple_async_result_take_error (ctx->result, error); - disconnect_3gpp_context_complete_and_free (ctx); - return; - } + state = mm_cinterion_parse_swwan_response (response, + usb_interface_configs[ctx->usb_interface_config_index].pdp_context, + &error); - /* Check parsed SWWAN reponse to see if we are now disconnected */ - if (verify_connection_state_from_swwan_response (response_parsed, &error) != 1) { - g_prefix_error (&error, "Disconnection attempt failed: "); - g_simple_async_result_take_error (ctx->result, error); + if (state == MM_SWWAN_STATE_CONNECTED) { + g_simple_async_result_set_error (ctx->result, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "CID %u is reported connected", + usb_interface_configs[ctx->usb_interface_config_index].pdp_context); disconnect_3gpp_context_complete_and_free (ctx); - g_list_free (response_parsed); return; } - g_list_free (response_parsed); + if (state == MM_SWWAN_STATE_UNKNOWN) { + /* Assume disconnected */ + mm_dbg ("couldn't get CID %u status, assume disconnected: %s", + usb_interface_configs[ctx->usb_interface_config_index].pdp_context, + error->message); + g_error_free (error); + } else + g_assert (state == MM_SWWAN_STATE_DISCONNECTED); /* Go on to next step */ ctx->step++; diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.c b/plugins/cinterion/mm-modem-helpers-cinterion.c index 6312e782..b9693b80 100644 --- a/plugins/cinterion/mm-modem-helpers-cinterion.c +++ b/plugins/cinterion/mm-modem-helpers-cinterion.c @@ -489,77 +489,83 @@ mm_cinterion_parse_sind_response (const gchar *response, /*****************************************************************************/ /* ^SWWAN read parser + * * Description: Parses <cid>, <state>[, <WWAN adapter>] or CME ERROR from SWWAN. - * Return: False if error occured while trying to parse SWWAN. - * Read Command - * AT^SWWAN? - * Response(s) - * [^SWWAN: <cid>, <state>[, <WWAN adapter>]] - * [^SWWAN: ...] - * OK - * ERROR - * +CME ERROR: <err> - * - * Examples: - * OK - If no WWAN connection is active, then read command just returns OK - * ^SWWAN: 3,1,1 - 3rd PDP Context, Activated, First WWAN Adaptor - * +CME ERROR: ? - -*/ - -gboolean -mm_cinterion_parse_swwan_response (const gchar *response, - GList **result, - GError **error) + * + * The method returns a MMSwwanState with the connection status of a single + * PDP context, the one being queried via the cid given as input. + * + * Note that we use CID for matching because the WWAN adapter field is optional + * it seems. + * + * Read Command + * AT^SWWAN? + * Response(s) + * [^SWWAN: <cid>, <state>[, <WWAN adapter>]] + * [^SWWAN: ...] + * OK + * ERROR + * +CME ERROR: <err> + * + * Examples: + * OK - If no WWAN connection is active, then read command just returns OK + * ^SWWAN: 3,1,1 - 3rd PDP Context, Activated, First WWAN Adaptor + * +CME ERROR: ? - + */ +MMSwwanState +mm_cinterion_parse_swwan_response (const gchar *response, + guint cid, + GError **error) { - if (*error) - return FALSE; - - if (!response) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Recieved NULL from SWWAN response."); - return FALSE; + GRegex *r; + GMatchInfo *match_info; + GError *inner_error = NULL; + MMSwwanState state; + + g_assert (response); + + /* If no WWAN connection is active, then ^SWWAN read command just returns OK + * (which we receive as an empty string) */ + if (!response[0]) + return MM_SWWAN_STATE_DISCONNECTED; + + if (!g_str_has_prefix (response, "^SWWAN:")) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't parse ^SWWAN response: '%s'", response); + return MM_SWWAN_STATE_UNKNOWN; } - /* Parse for [^SWWAN: <cid>, <state>[, <WWAN adapter>]] */ - if (strcasestr (response, "SWWAN")) { - gint matched; - guint cid, state, wwan_adapter; - matched = sscanf (response, "^SWWAN: %d,%d,%d", - &cid, - &state, - &wwan_adapter); - - if (matched != 3 || - cid < 1 || cid > 16 || - state < 0 || state > 1 || - wwan_adapter < 1 || wwan_adapter > 2) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Invalid format for SWWAN response: '%s'", - response); - return FALSE; + r = g_regex_new ("\\^SWWAN:\\s*(\\d+),\\s*(\\d+)(?:,\\s*(\\d+))?(?:\\r\\n)?", + G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); + g_assert (r != NULL); + + state = MM_SWWAN_STATE_UNKNOWN; + g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); + while (!inner_error && g_match_info_matches (match_info)) { + guint read_state; + guint read_cid; + + if (!mm_get_uint_from_match_info (match_info, 1, &read_cid)) + mm_warn ("Couldn't read cid in ^SWWAN response: '%s'", response); + else if (!mm_get_uint_from_match_info (match_info, 2, &read_state)) + mm_warn ("Couldn't read state in ^SWWAN response: '%s'", response); + else if (read_cid == cid) { + if (read_state == MM_SWWAN_STATE_CONNECTED || read_state == MM_SWWAN_STATE_DISCONNECTED) { + state = (MMSwwanState) read_state; + break; + } + mm_warn ("Invalid state read in ^SWWAN response: %u", read_state); } - *result = g_list_append (*result, GINT_TO_POINTER(cid)); - *result = g_list_append (*result, GINT_TO_POINTER(state)); - *result = g_list_append (*result, GINT_TO_POINTER(wwan_adapter)); - } - /* TODO: It'd be nice to get 'OK' from response so we don't have to assume that - * zero length response means 'OK' or am I doing it wrong?... */ - else if (!g_utf8_strlen (response, 100)) - *result = g_list_append (*result, GINT_TO_POINTER(0)); - else { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Could not parse SWWAN response: '%s'", - response); - return FALSE; + g_match_info_next (match_info, &inner_error); } + g_match_info_free (match_info); + g_regex_unref (r); - return TRUE; + if (state == MM_SWWAN_STATE_UNKNOWN) + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "No state returned for CID %u", cid); + + return state; } diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.h b/plugins/cinterion/mm-modem-helpers-cinterion.h index c94341ec..5d05e2c6 100644 --- a/plugins/cinterion/mm-modem-helpers-cinterion.h +++ b/plugins/cinterion/mm-modem-helpers-cinterion.h @@ -67,7 +67,15 @@ gboolean mm_cinterion_parse_sind_response (const gchar *response, /*****************************************************************************/ /* ^SWWAN response parser */ -gboolean mm_cinterion_parse_swwan_response (const gchar *response, - GList **result, - GError **error); + +typedef enum { + MM_SWWAN_STATE_UNKNOWN = -1, + MM_SWWAN_STATE_DISCONNECTED = 0, + MM_SWWAN_STATE_CONNECTED = 1, +} MMSwwanState; + +MMSwwanState mm_cinterion_parse_swwan_response (const gchar *response, + guint swwan_index, + GError **error); + #endif /* MM_MODEM_HELPERS_CINTERION_H */ diff --git a/plugins/cinterion/tests/test-modem-helpers-cinterion.c b/plugins/cinterion/tests/test-modem-helpers-cinterion.c index a59071e2..7bfb5951 100644 --- a/plugins/cinterion/tests/test-modem-helpers-cinterion.c +++ b/plugins/cinterion/tests/test-modem-helpers-cinterion.c @@ -327,142 +327,158 @@ test_cnmi_phs8 (void) g_array_unref (expected_mt); g_array_unref (expected_bm); g_array_unref (expected_ds); - g_array_unref (expected_bfr);} + g_array_unref (expected_bfr); +} -static void -test_swwan_parser (const GArray *expected_cid, - const GArray *expected_state, - const GArray *expected_adapter, - const gchar *at_cmd, - const gboolean test_for_errors) -{ - GError *error = NULL; - gboolean res = TRUE; - gchar *response; - guint i, j, k; - - g_assert (expected_cid != NULL); - g_assert (expected_state != NULL); - g_assert (expected_adapter != NULL); - - /* For each expected_cid */ - for (i = 0; i < expected_cid->len; i++) { - /* For each expected_state */ - for (j = 0; j < expected_state->len; j++) { - /* For each expected_adapter */ - for (k = 0; k < expected_adapter->len; k++) { - GList *response_parsed = NULL; - - /* Build a unique at_cmd string */ - response = g_strdup_printf ("%s: %i,%i,%i\r\n\r\n", - at_cmd, - g_array_index (expected_cid, guint, i), - g_array_index (expected_state, guint, j), - g_array_index (expected_adapter, guint, k)); - - /* and send it to the parser */ - res = mm_cinterion_parse_swwan_response (response, - &response_parsed, - &error); - - if (test_for_errors) { - /* There should be errors raised */ - g_assert (res == FALSE); - g_assert (error != NULL); - - /* reset the error's everytime */ - res = TRUE; - g_clear_error (&error); - } - else { - /* The parsed values we get back should match the AT string we sent */ - g_assert (g_array_index (expected_cid, guint, i) == - GPOINTER_TO_INT(g_list_nth_data (response_parsed, 0))); - g_assert (g_array_index (expected_state, guint, j) == - GPOINTER_TO_INT(g_list_nth_data (response_parsed, 1))); - g_assert (g_array_index (expected_adapter, guint, k) == - GPOINTER_TO_INT(g_list_nth_data (response_parsed, 2))); - - /* and there should be no errors raised */ - g_assert (res == TRUE); - g_assert_no_error (error); - } - - g_list_free(response_parsed); - } +/*****************************************************************************/ +/* Test ^SWWAN read */ + +#define SWWAN_TEST_MAX_CIDS 2 + +typedef struct { + guint cid; + MMSwwanState state; +} PdpContextState; + +typedef struct { + const gchar *response; + PdpContextState expected_items[SWWAN_TEST_MAX_CIDS]; + gboolean skip_test_other_cids; +} SwwanTest; + +/* Note: all tests are based on checking CIDs 2 and 3 */ +static const SwwanTest swwan_tests[] = { + /* No active PDP context reported (all disconnected) */ + { + .response = "", + .expected_items = { + { .cid = 2, .state = MM_SWWAN_STATE_DISCONNECTED }, + { .cid = 3, .state = MM_SWWAN_STATE_DISCONNECTED } + }, + /* Don't test other CIDs because for those we would also return + * DISCONNECTED, not UNKNOWN. */ + .skip_test_other_cids = TRUE + }, + /* Single PDP context active (short version without interface index) */ + { + .response = "^SWWAN: 3,1\r\n", + .expected_items = { + { .cid = 2, .state = MM_SWWAN_STATE_UNKNOWN }, + { .cid = 3, .state = MM_SWWAN_STATE_CONNECTED } + } + }, + /* Single PDP context active (long version with interface index) */ + { + .response = "^SWWAN: 3,1,1\r\n", + .expected_items = { + { .cid = 2, .state = MM_SWWAN_STATE_UNKNOWN }, + { .cid = 3, .state = MM_SWWAN_STATE_CONNECTED } + } + }, + /* Single PDP context inactive (short version without interface index) */ + { + .response = "^SWWAN: 3,0\r\n", + .expected_items = { + { .cid = 2, .state = MM_SWWAN_STATE_UNKNOWN }, + { .cid = 3, .state = MM_SWWAN_STATE_DISCONNECTED } + } + }, + /* Single PDP context inactive (long version with interface index) */ + { + .response = "^SWWAN: 3,0,1\r\n", + .expected_items = { + { .cid = 2, .state = MM_SWWAN_STATE_UNKNOWN }, + { .cid = 3, .state = MM_SWWAN_STATE_DISCONNECTED } + } + }, + /* Multiple PDP contexts active (short version without interface index) */ + { + .response = "^SWWAN: 2,1\r\n^SWWAN: 3,1\r\n", + .expected_items = { + { .cid = 2, .state = MM_SWWAN_STATE_CONNECTED }, + { .cid = 3, .state = MM_SWWAN_STATE_CONNECTED } + } + }, + /* Multiple PDP contexts active (long version with interface index) */ + { + .response = "^SWWAN: 2,1,3\r\n^SWWAN: 3,1,1\r\n", + .expected_items = { + { .cid = 2, .state = MM_SWWAN_STATE_CONNECTED }, + { .cid = 3, .state = MM_SWWAN_STATE_CONNECTED } + } + }, + /* Multiple PDP contexts inactive (short version without interface index) */ + { + .response = "^SWWAN: 2,0\r\n^SWWAN: 3,0\r\n", + .expected_items = { + { .cid = 2, .state = MM_SWWAN_STATE_DISCONNECTED }, + { .cid = 3, .state = MM_SWWAN_STATE_DISCONNECTED } + } + }, + /* Multiple PDP contexts inactive (long version with interface index) */ + { + .response = "^SWWAN: 2,0,3\r\n^SWWAN: 3,0,1\r\n", + .expected_items = { + { .cid = 2, .state = MM_SWWAN_STATE_DISCONNECTED }, + { .cid = 3, .state = MM_SWWAN_STATE_DISCONNECTED } + } + }, + /* Multiple PDP contexts active/inactive (short version without interface index) */ + { + .response = "^SWWAN: 2,0\r\n^SWWAN: 3,1\r\n", + .expected_items = { + { .cid = 2, .state = MM_SWWAN_STATE_DISCONNECTED }, + { .cid = 3, .state = MM_SWWAN_STATE_CONNECTED } + } + }, + /* Multiple PDP contexts active/inactive (long version with interface index) */ + { + .response = "^SWWAN: 2,0,3\r\n^SWWAN: 3,1,1\r\n", + .expected_items = { + { .cid = 2, .state = MM_SWWAN_STATE_DISCONNECTED }, + { .cid = 3, .state = MM_SWWAN_STATE_CONNECTED } } } -} +}; static void test_swwan_pls8 (void) { - GArray *good_cid; - GArray *good_state; - GArray *good_adapter; - GArray *bad_cid; - GArray *bad_state; - GArray *bad_adapter; - guint i; - guint val; + MMSwwanState read_state; + GError *error = NULL; + guint i; + + /* Base tests for successful responses */ + for (i = 0; i < G_N_ELEMENTS (swwan_tests); i++) { + guint j; - /* AT^SWWAN=? -> '^SWWAN: (0,1),(1-16),(1,2)' */ - /* Setup array with good SWWAN values */ - good_cid = g_array_sized_new (FALSE, FALSE, sizeof (guint), 16); - for (i = 1; i < 17; i++) - val = i, g_array_append_val (good_cid, val); - - good_state = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2); - val = 0, g_array_append_val (good_state, val); - val = 1, g_array_append_val (good_state, val); - - good_adapter = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2); - val = 1, g_array_append_val (good_adapter, val); - val = 2, g_array_append_val (good_adapter, val); - - /* and test */ - test_swwan_parser (good_cid, - good_state, - good_adapter, - "^SWWAN", - FALSE); - - /* Setup array with bad SWWAN values */ - bad_cid = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2); - val = -1, g_array_append_val (bad_cid, val); - val = 17, g_array_append_val (bad_cid, val); - - bad_state = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2); - val = -1, g_array_append_val (bad_state, val); - val = 2, g_array_append_val (bad_state, val); - - bad_adapter = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2); - val = -1, g_array_append_val (bad_adapter, val); - val = 0, g_array_append_val (bad_adapter, val); - val = 3, g_array_append_val (bad_adapter, val); - - /* and test */ - test_swwan_parser (bad_cid, - bad_state, - bad_adapter, - "^SWWAN", - TRUE); - - /* and again with a bad cmd */ - test_swwan_parser (bad_cid, - bad_state, - bad_adapter, - "^GARBAGE", - TRUE); - - - g_array_unref (good_cid); - g_array_unref (good_state); - g_array_unref (good_adapter); - g_array_unref (bad_cid); - g_array_unref (bad_state); - g_array_unref (bad_adapter); + /* Query for the expected items (CIDs 2 and 3) */ + for (j = 0; j < SWWAN_TEST_MAX_CIDS; j++) { + read_state = mm_cinterion_parse_swwan_response (swwan_tests[i].response, swwan_tests[i].expected_items[j].cid, &error); + if (swwan_tests[i].expected_items[j].state == MM_SWWAN_STATE_UNKNOWN) { + g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); + g_clear_error (&error); + } else + g_assert_no_error (error); + g_assert_cmpint (read_state, ==, swwan_tests[i].expected_items[j].state); + } + + /* Query for a CID which isn't replied (e.g. 12) */ + if (!swwan_tests[i].skip_test_other_cids) { + read_state = mm_cinterion_parse_swwan_response (swwan_tests[i].response, 12, &error); + g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); + g_assert_cmpint (read_state, ==, MM_SWWAN_STATE_UNKNOWN); + g_clear_error (&error); + } + } + + /* Additional tests for errors */ + read_state = mm_cinterion_parse_swwan_response ("^GARBAGE", 2, &error); + g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); + g_assert_cmpint (read_state, ==, MM_SWWAN_STATE_UNKNOWN); + g_clear_error (&error); } + /*****************************************************************************/ /* Test ^SIND responses */ |