diff options
author | Sven Schwermer <sven.schwermer@disruptive-technologies.com> | 2022-03-09 13:25:06 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2022-06-27 19:15:31 +0000 |
commit | a2a0e2d75434d06f722a4c8ba02485d13ec1b0f0 (patch) | |
tree | 14af965a2db74c02ba8bcf1807e746744ce0b775 | |
parent | a87a1f8ced054aba80ed6f15a0ce2c47827c394d (diff) |
fibocom: ensure RNDIS disconnected before a new connection attempt
The ECM dialling guide requires to check whether RNDIS is already active
before attempting to establish an ECM/RNDIS connection.
If it is active (regardless of its settings), we will disconnect it
first, before attempting the new connection with the user-provided
settings.
Signed-off-by: Sven Schwermer <sven.schwermer@disruptive-technologies.com>
-rw-r--r-- | plugins/fibocom/mm-broadband-bearer-fibocom-ecm.c | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/plugins/fibocom/mm-broadband-bearer-fibocom-ecm.c b/plugins/fibocom/mm-broadband-bearer-fibocom-ecm.c index 0cad0003..a4faf6ab 100644 --- a/plugins/fibocom/mm-broadband-bearer-fibocom-ecm.c +++ b/plugins/fibocom/mm-broadband-bearer-fibocom-ecm.c @@ -19,10 +19,207 @@ #include "mm-broadband-modem-fibocom.h" #include "mm-base-modem-at.h" #include "mm-iface-modem-3gpp.h" +#include "mm-log.h" G_DEFINE_TYPE (MMBroadbandBearerFibocomEcm, mm_broadband_bearer_fibocom_ecm, MM_TYPE_BROADBAND_BEARER) /*****************************************************************************/ +/* Common helper functions */ + +static gboolean +parse_gtrndis_read_response (const gchar *response, + guint *state, + guint *cid, + GError **error) +{ + g_autoptr(GRegex) r = NULL; + g_autoptr(GMatchInfo) match_info = NULL; + + r = g_regex_new ("\\+GTRNDIS:\\s*(\\d+)(?:,(\\d+))?", + G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL); + g_assert (r != NULL); + + if (!g_regex_match (r, response, 0, &match_info)) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Invalid +GTRNDIS response: %s", response); + return FALSE; + } + if (!mm_get_uint_from_match_info (match_info, 1, state)) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Failed to match state in +GTRNDIS response: %s", response); + return FALSE; + } + + if (state) { + if (!mm_get_uint_from_match_info (match_info, 2, cid)) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Failed to match cid in +GTRNDIS response: %s", response); + return FALSE; + } + } else { + *cid = 0; + } + + return TRUE; +} + +/*****************************************************************************/ +/* 3GPP Connect */ + +typedef struct { + MMBroadbandModem *modem; + MMPortSerialAt *primary; + MMPortSerialAt *secondary; + MMBearerIpFamily ip_family; +} ConnectContext; + +static void +connect_context_free (ConnectContext *ctx) +{ + g_clear_object (&ctx->modem); + g_clear_object (&ctx->primary); + g_clear_object (&ctx->secondary); + g_slice_free (ConnectContext, ctx); +} + +static MMBearerConnectResult * +connect_3gpp_finish (MMBroadbandBearer *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static void +parent_connect_3gpp_ready (MMBroadbandBearer *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + MMBearerConnectResult *result; + + result = MM_BROADBAND_BEARER_CLASS (mm_broadband_bearer_fibocom_ecm_parent_class)->connect_3gpp_finish (self, res, &error); + if (result) + g_task_return_pointer (task, result, (GDestroyNotify) mm_bearer_connect_result_unref); + else + g_task_return_error (task, error); + g_object_unref (task); +} + +static void +disconnect_3gpp_ready (MMBroadbandBearer *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + gboolean result; + ConnectContext *ctx; + + result = MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp_finish (self, res, &error); + if (!result) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + ctx = g_task_get_task_data (task); + MM_BROADBAND_BEARER_CLASS (mm_broadband_bearer_fibocom_ecm_parent_class)->connect_3gpp ( + self, + ctx->modem, + ctx->primary, + ctx->secondary, + g_task_get_cancellable (task), + (GAsyncReadyCallback) parent_connect_3gpp_ready, + task); +} + +static void +gtrndis_check_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + MMBroadbandBearer *self; + ConnectContext *ctx; + GError *error = NULL; + const gchar *response; + guint state; + guint cid; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + response = mm_base_modem_at_command_finish (modem, res, &error); + if (!response) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + if (!parse_gtrndis_read_response (response, &state, &cid, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + if (state) { + /* RNDIS is already active, disconnect first. */ + mm_obj_dbg (self, "RNDIS active, tearing down existing connection..."); + MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp ( + MM_BROADBAND_BEARER (self), + ctx->modem, + ctx->primary, + ctx->secondary, + NULL, /* data port */ + cid, + (GAsyncReadyCallback) disconnect_3gpp_ready, + task); + return; + } + + /* Execute the regular connection flow if RNDIS is inactive. */ + mm_obj_dbg (self, "RNDIS inactive"); + MM_BROADBAND_BEARER_CLASS (mm_broadband_bearer_fibocom_ecm_parent_class)->connect_3gpp ( + MM_BROADBAND_BEARER (self), + ctx->modem, + ctx->primary, + ctx->secondary, + g_task_get_cancellable (task), + (GAsyncReadyCallback) parent_connect_3gpp_ready, + task); +} + +static void +connect_3gpp (MMBroadbandBearer *self, + MMBroadbandModem *modem, + MMPortSerialAt *primary, + MMPortSerialAt *secondary, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + ConnectContext *ctx; + GTask *task; + + ctx = g_slice_new0 (ConnectContext); + ctx->modem = g_object_ref (modem); + ctx->primary = g_object_ref (primary); + ctx->secondary = secondary ? g_object_ref (secondary) : NULL; + ctx->ip_family = mm_bearer_properties_get_ip_type (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); + mm_3gpp_normalize_ip_family (&ctx->ip_family); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_task_data (task, ctx, (GDestroyNotify) connect_context_free); + + /* First, we must check whether RNDIS is already active */ + mm_base_modem_at_command (MM_BASE_MODEM (modem), + "+GTRNDIS?", + 3, + FALSE, /* allow_cached */ + (GAsyncReadyCallback) gtrndis_check_ready, + task); +} + +/*****************************************************************************/ /* Dial context and task */ typedef struct { @@ -263,6 +460,8 @@ mm_broadband_bearer_fibocom_ecm_class_init (MMBroadbandBearerFibocomEcmClass *kl { MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass); + broadband_bearer_class->connect_3gpp = connect_3gpp; + broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish; broadband_bearer_class->dial_3gpp = dial_3gpp; broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish; broadband_bearer_class->disconnect_3gpp = disconnect_3gpp; |