diff options
Diffstat (limited to 'plugins/huawei')
-rw-r--r-- | plugins/huawei/mm-broadband-bearer-huawei.c | 129 | ||||
-rw-r--r-- | plugins/huawei/mm-modem-helpers-huawei.c | 100 | ||||
-rw-r--r-- | plugins/huawei/mm-modem-helpers-huawei.h | 30 | ||||
-rw-r--r-- | plugins/huawei/tests/test-modem-helpers-huawei.c | 103 |
4 files changed, 317 insertions, 45 deletions
diff --git a/plugins/huawei/mm-broadband-bearer-huawei.c b/plugins/huawei/mm-broadband-bearer-huawei.c index 6d1414d..1f330d3 100644 --- a/plugins/huawei/mm-broadband-bearer-huawei.c +++ b/plugins/huawei/mm-broadband-bearer-huawei.c @@ -29,6 +29,7 @@ #include "mm-broadband-bearer-huawei.h" #include "mm-log.h" #include "mm-modem-helpers.h" +#include "mm-modem-helpers-huawei.h" G_DEFINE_TYPE (MMBroadbandBearerHuawei, mm_broadband_bearer_huawei, MM_TYPE_BROADBAND_BEARER) @@ -43,7 +44,7 @@ struct _MMBroadbandBearerHuaweiPrivate { typedef enum { CONNECT_3GPP_CONTEXT_STEP_FIRST = 0, CONNECT_3GPP_CONTEXT_STEP_NDISDUP, - CONNECT_3GPP_CONTEXT_STEP_DHCP, + CONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY, CONNECT_3GPP_CONTEXT_STEP_LAST } Connect3gppContextStep; @@ -85,7 +86,7 @@ connect_3gpp_finish (MMBroadbandBearer *self, static void connect_3gpp_context_step (Connect3gppContext *ctx); static gboolean -connect_retry_dhcp_check_cb (MMBroadbandBearerHuawei *self) +connect_retry_ndisstatqry_check_cb (MMBroadbandBearerHuawei *self) { Connect3gppContext *ctx; @@ -103,12 +104,17 @@ connect_retry_dhcp_check_cb (MMBroadbandBearerHuawei *self) } static void -connect_dhcp_check_ready (MMBaseModem *modem, - GAsyncResult *res, - MMBroadbandBearerHuawei *self) +connect_ndisstatqry_check_ready (MMBaseModem *modem, + GAsyncResult *res, + MMBroadbandBearerHuawei *self) { Connect3gppContext *ctx; + const gchar *response; GError *error = NULL; + gboolean ipv4_available; + gboolean ipv4_connected; + gboolean ipv6_available; + gboolean ipv6_connected; ctx = self->priv->connect_pending; g_assert (ctx != NULL); @@ -116,31 +122,38 @@ connect_dhcp_check_ready (MMBaseModem *modem, /* Balance refcount */ g_object_unref (self); - if (!mm_base_modem_at_command_full_finish (modem, res, &error)) { - /* Only retry the DHCP check if we get a mobile equipment error, or if - * the command timed out. */ - if (error->domain == MM_MOBILE_EQUIPMENT_ERROR || - g_error_matches (error, - MM_SERIAL_ERROR, - MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { - g_error_free (error); - /* Setup timeout to retry the same step */ - g_timeout_add_seconds (1, - (GSourceFunc)connect_retry_dhcp_check_cb, - g_object_ref (self)); - return; - } + response = mm_base_modem_at_command_full_finish (modem, res, &error); + if (!response || + !mm_huawei_parse_ndisstatqry_response (response, + &ipv4_available, + &ipv4_connected, + &ipv6_available, + &ipv6_connected, + &error)) { + mm_dbg ("Modem doesn't properly support ^NDISSTATQRY command: %s", error->message); + g_error_free (error); - /* Fatal error happened; e.g. modem unplugged */ - self->priv->connect_pending = NULL; - g_simple_async_result_take_error (ctx->result, error); + ctx->self->priv->connect_pending = NULL; + g_simple_async_result_set_error (ctx->result, + MM_MOBILE_EQUIPMENT_ERROR, + MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED, + "Connection attempt not supported"); connect_3gpp_context_complete_and_free (ctx); return; } - /* Success! */ - ctx->step++; - connect_3gpp_context_step (ctx); + /* Connected in IPv4? */ + if (ipv4_available && ipv4_connected) { + /* Success! */ + ctx->step++; + connect_3gpp_context_step (ctx); + return; + } + + /* Setup timeout to retry the same step */ + g_timeout_add_seconds (1, + (GSourceFunc)connect_retry_ndisstatqry_check_cb, + g_object_ref (self)); } static void @@ -287,7 +300,7 @@ connect_3gpp_context_step (Connect3gppContext *ctx) return; } - case CONNECT_3GPP_CONTEXT_STEP_DHCP: + case CONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY: /* Wait for dial up timeout, retries for 60 times * (1s between the retries, so it means 1 minute). * If too many retries, failed @@ -307,12 +320,12 @@ connect_3gpp_context_step (Connect3gppContext *ctx) ctx->check_count++; mm_base_modem_at_command_full (ctx->modem, ctx->primary, - "^DHCP?", + "^NDISSTATQRY?", 3, FALSE, FALSE, NULL, - (GAsyncReadyCallback)connect_dhcp_check_ready, + (GAsyncReadyCallback)connect_ndisstatqry_check_ready, g_object_ref (ctx->self)); return; @@ -389,7 +402,7 @@ connect_3gpp (MMBroadbandBearer *self, typedef enum { DISCONNECT_3GPP_CONTEXT_STEP_FIRST = 0, DISCONNECT_3GPP_CONTEXT_STEP_NDISDUP, - DISCONNECT_3GPP_CONTEXT_STEP_DHCP, + DISCONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY, DISCONNECT_3GPP_CONTEXT_STEP_LAST } Disconnect3gppContextStep; @@ -424,7 +437,7 @@ disconnect_3gpp_finish (MMBroadbandBearer *self, static void disconnect_3gpp_context_step (Disconnect3gppContext *ctx); static gboolean -disconnect_retry_dhcp_check_cb (MMBroadbandBearerHuawei *self) +disconnect_retry_ndisstatqry_check_cb (MMBroadbandBearerHuawei *self) { Disconnect3gppContext *ctx; @@ -441,11 +454,17 @@ disconnect_retry_dhcp_check_cb (MMBroadbandBearerHuawei *self) } static void -disconnect_dhcp_check_ready (MMBaseModem *modem, - GAsyncResult *res, - MMBroadbandBearerHuawei *self) +disconnect_ndisstatqry_check_ready (MMBaseModem *modem, + GAsyncResult *res, + MMBroadbandBearerHuawei *self) { Disconnect3gppContext *ctx; + const gchar *response; + GError *error = NULL; + gboolean ipv4_available; + gboolean ipv4_connected; + gboolean ipv6_available; + gboolean ipv6_connected; ctx = self->priv->disconnect_pending; g_assert (ctx != NULL); @@ -453,18 +472,38 @@ disconnect_dhcp_check_ready (MMBaseModem *modem, /* Balance refcount */ g_object_unref (self); - /* If any response give, we're still connected */ - if (mm_base_modem_at_command_full_finish (modem, res, NULL)) { - /* Setup timeout to retry the same step */ - g_timeout_add_seconds (1, - (GSourceFunc)disconnect_retry_dhcp_check_cb, - g_object_ref (self)); + response = mm_base_modem_at_command_full_finish (modem, res, &error); + if (!response || + !mm_huawei_parse_ndisstatqry_response (response, + &ipv4_available, + &ipv4_connected, + &ipv6_available, + &ipv6_connected, + &error)) { + mm_dbg ("Modem doesn't properly support ^NDISSTATQRY command: %s", error->message); + g_error_free (error); + + ctx->self->priv->connect_pending = NULL; + g_simple_async_result_set_error (ctx->result, + MM_MOBILE_EQUIPMENT_ERROR, + MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED, + "Disconnection attempt not supported"); + disconnect_3gpp_context_complete_and_free (ctx); return; } - /* Success! */ - ctx->step++; - disconnect_3gpp_context_step (ctx); + /* Disconnected IPv4? */ + if (ipv4_available && !ipv4_connected) { + /* Success! */ + ctx->step++; + disconnect_3gpp_context_step (ctx); + return; + } + + /* Setup timeout to retry the same step */ + g_timeout_add_seconds (1, + (GSourceFunc)disconnect_retry_ndisstatqry_check_cb, + g_object_ref (self)); } static void @@ -517,7 +556,7 @@ disconnect_3gpp_context_step (Disconnect3gppContext *ctx) g_object_ref (ctx->self)); return; - case DISCONNECT_3GPP_CONTEXT_STEP_DHCP: + case DISCONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY: /* If too many retries (1s of wait between the retries), failed */ if (ctx->check_count > 10) { /* Clear context */ @@ -534,12 +573,12 @@ disconnect_3gpp_context_step (Disconnect3gppContext *ctx) ctx->check_count++; mm_base_modem_at_command_full (ctx->modem, ctx->primary, - "^DHCP?", + "^NDISSTATQRY?", 3, FALSE, FALSE, NULL, - (GAsyncReadyCallback)disconnect_dhcp_check_ready, + (GAsyncReadyCallback)disconnect_ndisstatqry_check_ready, g_object_ref (ctx->self)); return; diff --git a/plugins/huawei/mm-modem-helpers-huawei.c b/plugins/huawei/mm-modem-helpers-huawei.c new file mode 100644 index 0000000..d9b038d --- /dev/null +++ b/plugins/huawei/mm-modem-helpers-huawei.c @@ -0,0 +1,100 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2013 Huawei Technologies Co., Ltd + * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org> + */ + +#include <string.h> + +#include <ModemManager.h> +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-modem-helpers-huawei.h" + +/*****************************************************************************/ +/* ^NDISSTATQRY response parser */ + +gboolean +mm_huawei_parse_ndisstatqry_response (const gchar *response, + gboolean *ipv4_available, + gboolean *ipv4_connected, + gboolean *ipv6_available, + gboolean *ipv6_connected, + GError **error) +{ + GRegex *r; + GMatchInfo *match_info; + GError *inner_error = NULL; + + if (!response || !g_str_has_prefix (response, "^NDISSTATQRY:")) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing ^NDISSTATQRY prefix"); + return FALSE; + } + + *ipv4_available = FALSE; + *ipv6_available = FALSE; + + /* The response maybe as: + * <CR><LF>^NDISSTATQRY: 1,,,IPV4<CR><LF>^NDISSTATQRY: 0,33,,IPV6<CR><LF> + * <CR><LF><CR><LF>OK<CR><LF> + * So we have to split the status for IPv4 and IPv6. For now, we only care + * about IPv4. + */ + r = g_regex_new ("\\^NDISSTATQRY:\\s*(\\d),(.*),(.*),(.*)(\\r\\n)?", + G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, + 0, NULL); + g_assert (r != NULL); + + g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); + while (!inner_error && g_match_info_matches (match_info)) { + gchar *ip_type_str; + guint connected; + + /* Read values */ + ip_type_str = mm_get_string_unquoted_from_match_info (match_info, 4); + if (!ip_type_str || + !mm_get_uint_from_match_info (match_info, 1, &connected) || + (connected != 0 && connected != 1)) { + inner_error = g_error_new (MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't parse ^NDISSTATQRY fields"); + } else if (g_ascii_strcasecmp (ip_type_str, "IPV4") == 0) { + *ipv4_available = TRUE; + *ipv4_connected = (gboolean)connected; + } else if (g_ascii_strcasecmp (ip_type_str, "IPV6") == 0) { + *ipv6_available = TRUE; + *ipv6_connected = (gboolean)connected; + } + + g_free (ip_type_str); + if (!inner_error) + g_match_info_next (match_info, &inner_error); + } + + g_match_info_free (match_info); + g_regex_unref (r); + + if (!ipv4_available && !ipv6_available) { + inner_error = g_error_new (MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't find IPv4 or IPv6 info in ^NDISSTATQRY response"); + } + + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + + return TRUE; +} diff --git a/plugins/huawei/mm-modem-helpers-huawei.h b/plugins/huawei/mm-modem-helpers-huawei.h new file mode 100644 index 0000000..cc32087 --- /dev/null +++ b/plugins/huawei/mm-modem-helpers-huawei.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2013 Huawei Technologies Co., Ltd + * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org> + */ + +#ifndef MM_MODEM_HELPERS_HUAWEI_H +#define MM_MODEM_HELPERS_HUAWEI_H + +#include "glib.h" + +/* ^NDISSTATQRY response parser */ +gboolean mm_huawei_parse_ndisstatqry_response (const gchar *response, + gboolean *ipv4_available, + gboolean *ipv4_connected, + gboolean *ipv6_available, + gboolean *ipv6_connected, + GError **error); + +#endif /* MM_MODEM_HELPERS_HUAWEI_H */ diff --git a/plugins/huawei/tests/test-modem-helpers-huawei.c b/plugins/huawei/tests/test-modem-helpers-huawei.c new file mode 100644 index 0000000..c1bc0ed --- /dev/null +++ b/plugins/huawei/tests/test-modem-helpers-huawei.c @@ -0,0 +1,103 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org> + */ + +#include <glib.h> +#include <glib-object.h> +#include <locale.h> + +#include "mm-modem-helpers-huawei.h" + +/*****************************************************************************/ +/* Test ^NDISSTATQRY responses */ + +typedef struct { + const gchar *str; + gboolean expected_ipv4_available; + gboolean expected_ipv4_connected; + gboolean expected_ipv6_available; + gboolean expected_ipv6_connected; +} NdisstatqryTest; + +static const NdisstatqryTest ndisstatqry_tests[] = { + { "^NDISSTATQRY: 1,,,IPV4\r\n", TRUE, TRUE, FALSE, FALSE }, + { "^NDISSTATQRY: 0,,,IPV4\r\n", TRUE, FALSE, FALSE, FALSE }, + { "^NDISSTATQRY: 1,,,IPV6\r\n", FALSE, FALSE, TRUE, TRUE }, + { "^NDISSTATQRY: 0,,,IPV6\r\n", FALSE, FALSE, TRUE, FALSE }, + { "^NDISSTATQRY: 1,,,IPV4\r\n" + "^NDISSTATQRY: 1,,,IPV6\r\n", TRUE, TRUE, TRUE, TRUE }, + { "^NDISSTATQRY: 1,,,IPV4\r\n" + "^NDISSTATQRY: 0,,,IPV6\r\n", TRUE, TRUE, TRUE, FALSE }, + { "^NDISSTATQRY: 0,,,IPV4\r\n" + "^NDISSTATQRY: 1,,,IPV6\r\n", TRUE, FALSE, TRUE, TRUE }, + { "^NDISSTATQRY: 0,,,IPV4\r\n" + "^NDISSTATQRY: 0,,,IPV6\r\n", TRUE, FALSE, TRUE, FALSE }, + { "^NDISSTATQRY: 1,,,IPV4", TRUE, TRUE, FALSE, FALSE }, + { "^NDISSTATQRY: 0,,,IPV4", TRUE, FALSE, FALSE, FALSE }, + { "^NDISSTATQRY: 1,,,IPV6", FALSE, FALSE, TRUE, TRUE }, + { "^NDISSTATQRY: 0,,,IPV6", FALSE, FALSE, TRUE, FALSE }, + { "^NDISSTATQRY: 1,,,IPV4\r\n" + "^NDISSTATQRY: 1,,,IPV6", TRUE, TRUE, TRUE, TRUE }, + { "^NDISSTATQRY: 1,,,IPV4\r\n" + "^NDISSTATQRY: 0,,,IPV6", TRUE, TRUE, TRUE, FALSE }, + { "^NDISSTATQRY: 0,,,IPV4\r\n" + "^NDISSTATQRY: 1,,,IPV6", TRUE, FALSE, TRUE, TRUE }, + { "^NDISSTATQRY: 0,,,IPV4\r\n" + "^NDISSTATQRY: 0,,,IPV6", TRUE, FALSE, TRUE, FALSE }, + { NULL, FALSE, FALSE, FALSE, FALSE } +}; + +static void +test_ndisstatqry (void) +{ + guint i; + + for (i = 0; ndisstatqry_tests[i].str; i++) { + GError *error = NULL; + gboolean ipv4_available; + gboolean ipv4_connected; + gboolean ipv6_available; + gboolean ipv6_connected; + + g_assert (mm_huawei_parse_ndisstatqry_response ( + ndisstatqry_tests[i].str, + &ipv4_available, + &ipv4_connected, + &ipv6_available, + &ipv6_connected, + &error) == TRUE); + g_assert_no_error (error); + + g_assert (ipv4_available == ndisstatqry_tests[i].expected_ipv4_available); + if (ipv4_available) + g_assert (ipv4_connected == ndisstatqry_tests[i].expected_ipv4_connected); + g_assert (ipv6_available == ndisstatqry_tests[i].expected_ipv6_available); + if (ipv6_available) + g_assert (ipv6_connected == ndisstatqry_tests[i].expected_ipv6_connected); + } +} + +/*****************************************************************************/ + +int main (int argc, char **argv) +{ + setlocale (LC_ALL, ""); + + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/MM/huawei/ndisstatqry", test_ndisstatqry); + + return g_test_run (); +} |