From aa5ea1a4ff8b582281302aeeaad5946b9636f1c3 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Wed, 14 Dec 2016 13:54:47 +0100 Subject: cinterion: implement connection status monitoring via ^SWWAN? And consolidate the connection status checks done during connection and disconnection so that we re-use the same logic. --- plugins/cinterion/mm-broadband-bearer-cinterion.c | 179 ++++++++++++++------- plugins/cinterion/mm-modem-helpers-cinterion.c | 36 +++-- plugins/cinterion/mm-modem-helpers-cinterion.h | 16 +- .../cinterion/tests/test-modem-helpers-cinterion.c | 60 +++---- 4 files changed, 181 insertions(+), 110 deletions(-) diff --git a/plugins/cinterion/mm-broadband-bearer-cinterion.c b/plugins/cinterion/mm-broadband-bearer-cinterion.c index 79a4a9c55..741d2935a 100644 --- a/plugins/cinterion/mm-broadband-bearer-cinterion.c +++ b/plugins/cinterion/mm-broadband-bearer-cinterion.c @@ -75,6 +75,90 @@ get_usb_interface_config_index (MMPort *data, return -1; } +/*****************************************************************************/ +/* Connection status loading + * NOTE: only CONNECTED or DISCONNECTED should be reported here. + */ + +static MMBearerConnectionStatus +load_connection_status_finish (MMBaseBearer *bearer, + GAsyncResult *res, + GError **error) +{ + gssize aux; + + aux = g_task_propagate_int (G_TASK (res), error); + return (aux < 0 ? MM_BEARER_CONNECTION_STATUS_UNKNOWN : (MMBearerConnectionStatus) aux); +} + +static void +swwan_check_status_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + const gchar *response; + GError *error = NULL; + MMBearerConnectionStatus status; + guint cid; + + cid = GPOINTER_TO_UINT (g_task_get_task_data (task)); + + response = mm_base_modem_at_command_finish (modem, res, &error); + if (!response) { + g_task_return_error (task, error); + goto out; + } + + status = mm_cinterion_parse_swwan_response (response, cid, &error); + if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) { + g_task_return_error (task, error); + goto out; + } + + g_assert (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED || + status == MM_BEARER_CONNECTION_STATUS_CONNECTED); + g_task_return_int (task, (gssize) status); + +out: + g_object_unref (task); +} + +static void +load_connection_status_by_cid (MMBroadbandBearerCinterion *bearer, + guint cid, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + MMBaseModem *modem; + + task = g_task_new (bearer, NULL, callback, user_data); + g_task_set_task_data (task, GUINT_TO_POINTER (cid), NULL); + + g_object_get (bearer, + MM_BASE_BEARER_MODEM, &modem, + NULL); + + mm_base_modem_at_command (modem, + "^SWWAN?", + 5, + FALSE, + (GAsyncReadyCallback) swwan_check_status_ready, + task); + g_object_unref (modem); +} + +static void +load_connection_status (MMBaseBearer *bearer, + GAsyncReadyCallback callback, + gpointer user_data) +{ + load_connection_status_by_cid (MM_BROADBAND_BEARER_CINTERION (bearer), + mm_broadband_bearer_get_3gpp_cid (MM_BROADBAND_BEARER (bearer)), + callback, + user_data); +} + /*****************************************************************************/ /* Auth helpers */ @@ -186,39 +270,31 @@ dial_3gpp_finish (MMBroadbandBearer *self, static void dial_3gpp_context_step (GTask *task); static void -swwan_dial_check_status_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) +dial_connection_status_ready (MMBroadbandBearerCinterion *self, + GAsyncResult *res, + GTask *task) { - Dial3gppContext *ctx; - const gchar *response; - GError *error = NULL; - MMSwwanState state; + MMBearerConnectionStatus status; + Dial3gppContext *ctx; + GError *error = NULL; ctx = (Dial3gppContext *) g_task_get_task_data (task); - response = mm_base_modem_at_command_full_finish (modem, res, &error); - if (!response) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - state = mm_cinterion_parse_swwan_response (response, ctx->cid, &error); - if (state == MM_SWWAN_STATE_UNKNOWN) { + status = load_connection_status_finish (MM_BASE_BEARER (self), res, &error); + if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) { g_task_return_error (task, error); g_object_unref (task); return; } - if (state == MM_SWWAN_STATE_DISCONNECTED) { + if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "CID %u is reported disconnected", ctx->cid); g_object_unref (task); return; } - g_assert (state == MM_SWWAN_STATE_CONNECTED); + g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED); /* Go to next step */ ctx->step++; @@ -362,14 +438,9 @@ dial_3gpp_context_step (GTask *task) case DIAL_3GPP_CONTEXT_STEP_VALIDATE_CONNECTION: mm_dbg ("cinterion dial step %u/%u: checking SWWAN interface %u status...", ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST, usb_interface_configs[ctx->usb_interface_config_index].swwan_index); - mm_base_modem_at_command_full (ctx->modem, - ctx->primary, - "^SWWAN?", - 5, - FALSE, - FALSE, - NULL, - (GAsyncReadyCallback) swwan_dial_check_status_ready, + load_connection_status_by_cid (ctx->self, + ctx->cid, + (GAsyncReadyCallback) dial_connection_status_ready, task); return; @@ -471,39 +542,34 @@ disconnect_3gpp_finish (MMBroadbandBearer *self, static void disconnect_3gpp_context_step (GTask *task); static void -swwan_disconnect_check_status_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) +disconnect_connection_status_ready (MMBroadbandBearerCinterion *self, + GAsyncResult *res, + GTask *task) { - Disconnect3gppContext *ctx; - const gchar *response; - GError *error = NULL; - MMSwwanState state; + MMBearerConnectionStatus status; + Disconnect3gppContext *ctx; + GError *error = NULL; ctx = (Disconnect3gppContext *) g_task_get_task_data (task); - response = mm_base_modem_at_command_full_finish (modem, res, &error); - if (error) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - state = mm_cinterion_parse_swwan_response (response, ctx->cid, &error); - if (state == MM_SWWAN_STATE_CONNECTED) { + status = load_connection_status_finish (MM_BASE_BEARER (self), res, &error); + switch (status) { + case MM_BEARER_CONNECTION_STATUS_UNKNOWN: + /* Assume disconnected */ + mm_dbg ("couldn't get CID %u status, assume disconnected: %s", ctx->cid, error->message); + g_clear_error (&error); + break; + case MM_BEARER_CONNECTION_STATUS_DISCONNECTED: + break; + case MM_BEARER_CONNECTION_STATUS_CONNECTED: g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "CID %u is reported connected", ctx->cid); g_object_unref (task); return; + default: + g_assert_not_reached (); } - if (state == MM_SWWAN_STATE_UNKNOWN) { - /* Assume disconnected */ - mm_dbg ("couldn't get CID %u status, assume disconnected: %s", ctx->cid, error->message); - g_error_free (error); - } else - g_assert (state == MM_SWWAN_STATE_DISCONNECTED); - /* Go on to next step */ ctx->step++; disconnect_3gpp_context_step (task); @@ -563,14 +629,9 @@ disconnect_3gpp_context_step (GTask *task) mm_dbg ("cinterion disconnect step %u/%u: checking SWWAN interface %u status...", ctx->step, DISCONNECT_3GPP_CONTEXT_STEP_LAST, usb_interface_configs[ctx->usb_interface_config_index].swwan_index); - mm_base_modem_at_command_full (ctx->modem, - ctx->primary, - "^SWWAN?", - 5, - FALSE, - FALSE, - NULL, - (GAsyncReadyCallback) swwan_disconnect_check_status_ready, + load_connection_status_by_cid (MM_BROADBAND_BEARER_CINTERION (ctx->self), + ctx->cid, + (GAsyncReadyCallback) disconnect_connection_status_ready, task); return; @@ -674,8 +735,12 @@ mm_broadband_bearer_cinterion_init (MMBroadbandBearerCinterion *self) static void mm_broadband_bearer_cinterion_class_init (MMBroadbandBearerCinterionClass *klass) { + MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass); + base_bearer_class->load_connection_status = load_connection_status; + base_bearer_class->load_connection_status_finish = load_connection_status_finish; + broadband_bearer_class->dial_3gpp = dial_3gpp; broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish; broadband_bearer_class->disconnect_3gpp = disconnect_3gpp; diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.c b/plugins/cinterion/mm-modem-helpers-cinterion.c index b9693b805..4f52761ab 100644 --- a/plugins/cinterion/mm-modem-helpers-cinterion.c +++ b/plugins/cinterion/mm-modem-helpers-cinterion.c @@ -512,34 +512,40 @@ mm_cinterion_parse_sind_response (const gchar *response, * ^SWWAN: 3,1,1 - 3rd PDP Context, Activated, First WWAN Adaptor * +CME ERROR: ? - */ -MMSwwanState + +enum { + MM_SWWAN_STATE_DISCONNECTED = 0, + MM_SWWAN_STATE_CONNECTED = 1, +}; + +MMBearerConnectionStatus mm_cinterion_parse_swwan_response (const gchar *response, guint cid, GError **error) { - GRegex *r; - GMatchInfo *match_info; - GError *inner_error = NULL; - MMSwwanState state; + GRegex *r; + GMatchInfo *match_info; + GError *inner_error = NULL; + MMBearerConnectionStatus status; 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; + return MM_BEARER_CONNECTION_STATUS_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; + return MM_BEARER_CONNECTION_STATUS_UNKNOWN; } 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; + status = MM_BEARER_CONNECTION_STATUS_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; @@ -550,22 +556,26 @@ mm_cinterion_parse_swwan_response (const gchar *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; + if (read_state == MM_SWWAN_STATE_CONNECTED) { + status = MM_BEARER_CONNECTION_STATUS_CONNECTED; + break; + } + if (read_state == MM_SWWAN_STATE_DISCONNECTED) { + status = MM_BEARER_CONNECTION_STATUS_DISCONNECTED; break; } mm_warn ("Invalid state read in ^SWWAN response: %u", read_state); + break; } - g_match_info_next (match_info, &inner_error); } g_match_info_free (match_info); g_regex_unref (r); - if (state == MM_SWWAN_STATE_UNKNOWN) + if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No state returned for CID %u", cid); - return state; + return status; } diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.h b/plugins/cinterion/mm-modem-helpers-cinterion.h index 5d05e2c6c..e09c73d66 100644 --- a/plugins/cinterion/mm-modem-helpers-cinterion.h +++ b/plugins/cinterion/mm-modem-helpers-cinterion.h @@ -18,7 +18,9 @@ #ifndef MM_MODEM_HELPERS_CINTERION_H #define MM_MODEM_HELPERS_CINTERION_H -#include "glib.h" +#include + +#include /*****************************************************************************/ /* ^SCFG test parser */ @@ -68,14 +70,8 @@ gboolean mm_cinterion_parse_sind_response (const gchar *response, /*****************************************************************************/ /* ^SWWAN response parser */ -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); +MMBearerConnectionStatus 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 7bfb5951c..237395163 100644 --- a/plugins/cinterion/tests/test-modem-helpers-cinterion.c +++ b/plugins/cinterion/tests/test-modem-helpers-cinterion.c @@ -336,8 +336,8 @@ test_cnmi_phs8 (void) #define SWWAN_TEST_MAX_CIDS 2 typedef struct { - guint cid; - MMSwwanState state; + guint cid; + MMBearerConnectionStatus state; } PdpContextState; typedef struct { @@ -352,8 +352,8 @@ static const SwwanTest swwan_tests[] = { { .response = "", .expected_items = { - { .cid = 2, .state = MM_SWWAN_STATE_DISCONNECTED }, - { .cid = 3, .state = MM_SWWAN_STATE_DISCONNECTED } + { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED }, + { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED } }, /* Don't test other CIDs because for those we would also return * DISCONNECTED, not UNKNOWN. */ @@ -363,80 +363,80 @@ static const SwwanTest swwan_tests[] = { { .response = "^SWWAN: 3,1\r\n", .expected_items = { - { .cid = 2, .state = MM_SWWAN_STATE_UNKNOWN }, - { .cid = 3, .state = MM_SWWAN_STATE_CONNECTED } + { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_UNKNOWN }, + { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_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 } + { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_UNKNOWN }, + { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_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 } + { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_UNKNOWN }, + { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_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 } + { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_UNKNOWN }, + { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_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 } + { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED }, + { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_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 } + { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED }, + { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_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 } + { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED }, + { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_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 } + { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED }, + { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_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 } + { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED }, + { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_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 } + { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED }, + { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED } } } }; @@ -444,9 +444,9 @@ static const SwwanTest swwan_tests[] = { static void test_swwan_pls8 (void) { - MMSwwanState read_state; - GError *error = NULL; - guint i; + MMBearerConnectionStatus read_state; + GError *error = NULL; + guint i; /* Base tests for successful responses */ for (i = 0; i < G_N_ELEMENTS (swwan_tests); i++) { @@ -455,7 +455,7 @@ test_swwan_pls8 (void) /* 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) { + if (swwan_tests[i].expected_items[j].state == MM_BEARER_CONNECTION_STATUS_UNKNOWN) { g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); g_clear_error (&error); } else @@ -467,7 +467,7 @@ test_swwan_pls8 (void) 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_assert_cmpint (read_state, ==, MM_BEARER_CONNECTION_STATUS_UNKNOWN); g_clear_error (&error); } } @@ -475,7 +475,7 @@ test_swwan_pls8 (void) /* 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_assert_cmpint (read_state, ==, MM_BEARER_CONNECTION_STATUS_UNKNOWN); g_clear_error (&error); } -- cgit v1.2.1