Files
ModemManager/plugins/altair/mm-broadband-bearer-altair-lte.c
T
Aleksander Morgado 5d150ed23e core,plugins: consolidate connection/disconnection timeout values
Each different plugin or protocol had a different connection attempt
value. E.g. QMI and MBIM both used 60s max for the connection attempt,
while the u-blox plugin had up to 180s for ECM based connection
setups.

This commit consolidates all plugins and protocols to use the same
timeout values for commands that may take long to respond, e.g. a
connection atempt under low signal quality conditions.

A value of 180s for the connection attempt steps and 120s for a
disconnection attempt step is considered. Note, though, that in some
cases (like a IPv4v6 setup attempt using QMI) we may have more than
one such long step, so this doesn't mean that a connection attempt
will always take less than 180s.

Users of the connection/disconnection APIs should be able to handle
the case where the attempt times out in their side (e.g. with a lower
DBus request timeout), and which would not mean the actual request
they did really failed. E.g. a connection attempt with a DBus timeout
of 30s may fail in the user with a timeout error, but the attempt
would still go on for as much as the plugin/protocol needs.

Fixes https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/270
2020-11-14 09:41:43 +00:00

367 lines
12 KiB
C

/* -*- 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 Altair Semiconductor
*
* Author: Ori Inbar <ori.inbar@altair-semi.com>
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-base-modem-at.h"
#include "mm-broadband-bearer-altair-lte.h"
#include "mm-iface-modem-3gpp.h"
#include "mm-log.h"
#include "mm-modem-helpers.h"
#define CONNECTION_CHECK_TIMEOUT_SEC 5
#define STATCM_TAG "%STATCM:"
G_DEFINE_TYPE (MMBroadbandBearerAltairLte, mm_broadband_bearer_altair_lte, MM_TYPE_BROADBAND_BEARER);
/*****************************************************************************/
/* 3GPP Connect sequence */
typedef struct {
MMBaseModem *modem;
MMPortSerialAt *primary;
MMPort *data;
} DetailedConnectContext;
static void
detailed_connect_context_free (DetailedConnectContext *ctx)
{
g_object_unref (ctx->data);
g_object_unref (ctx->primary);
g_object_unref (ctx->modem);
g_free (ctx);
}
static MMBearerConnectResult *
connect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
connect_3gpp_connect_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
DetailedConnectContext *ctx;
const gchar *result;
GError *error = NULL;
MMBearerIpConfig *config;
result = mm_base_modem_at_command_full_finish (modem, res, &error);
if (!result) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
ctx = g_task_get_task_data (task);
config = mm_bearer_ip_config_new ();
mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP);
/* Set operation result */
g_task_return_pointer (
task,
mm_bearer_connect_result_new (ctx->data, config, config),
(GDestroyNotify)mm_bearer_connect_result_unref);
g_object_unref (task);
g_object_unref (config);
}
static void
connect_3gpp_apnsettings_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
DetailedConnectContext *ctx;
const gchar *result;
GError *error = NULL;
result = mm_base_modem_at_command_full_finish (modem, res, &error);
if (!result) {
g_prefix_error (&error, "setting APN failed: ");
g_task_return_error (task, error);
g_object_unref (task);
return;
}
ctx = g_task_get_task_data (task);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
"%DPDNACT=1",
MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, /* timeout */
FALSE, /* allow_cached */
FALSE, /* is_raw */
g_task_get_cancellable (task),
(GAsyncReadyCallback)connect_3gpp_connect_ready,
task); /* user_data */
}
static void
connect_3gpp (MMBroadbandBearer *self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
MMPortSerialAt *secondary,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
DetailedConnectContext *ctx;
gchar *command, *apn;
MMBearerProperties *config;
MMModem3gppRegistrationState registration_state;
MMPort *data;
GTask *task;
/* There is a known firmware bug that can leave the modem unusable if a
* connect attempt is made when out of coverage. So, fail without trying.
*/
g_object_get (modem,
MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &registration_state,
NULL);
if (registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN) {
g_task_report_new_error (self,
callback,
user_data,
connect_3gpp,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK,
"Out of coverage, can't connect.");
return;
}
/* Don't allow a connect while we detach from the network to process SIM
* refresh.
* */
if (mm_broadband_modem_altair_lte_is_sim_refresh_detach_in_progress (modem)) {
mm_obj_dbg (self, "detached from network to process SIM refresh, failing connect request");
g_task_report_new_error (self,
callback,
user_data,
connect_3gpp,
MM_CORE_ERROR,
MM_CORE_ERROR_RETRY,
"Detached from network to process SIM refresh, can't connect.");
return;
}
data = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET);
if (!data) {
g_task_report_new_error (self,
callback,
user_data,
connect_3gpp,
MM_CORE_ERROR,
MM_CORE_ERROR_CONNECTED,
"Couldn't connect: no available net port available");
return;
}
ctx = g_new0 (DetailedConnectContext, 1);
ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->primary = g_object_ref (primary);
ctx->data = g_object_ref (data);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_connect_context_free);
config = mm_base_bearer_peek_config (MM_BASE_BEARER (self));
apn = mm_port_serial_at_quote_string (mm_bearer_properties_get_apn (config));
command = g_strdup_printf ("%%APNN=%s", apn);
g_free (apn);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
10, /* timeout */
FALSE, /* allow_cached */
FALSE, /* is_raw */
cancellable,
(GAsyncReadyCallback)connect_3gpp_apnsettings_ready,
task); /* user_data */
g_free (command);
}
/*****************************************************************************/
/* 3GPP Disconnect sequence */
typedef struct {
MMBaseModem *modem;
MMPortSerialAt *primary;
MMPort *data;
} DetailedDisconnectContext;
static gboolean
disconnect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
detailed_disconnect_context_free (DetailedDisconnectContext *ctx)
{
g_object_unref (ctx->data);
g_object_unref (ctx->primary);
g_object_unref (ctx->modem);
g_free (ctx);
}
static void
disconnect_3gpp_check_status (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
const gchar *result;
GError *error = NULL;
result = mm_base_modem_at_command_full_finish (modem, res, &error);
if (!result)
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
disconnect_3gpp (MMBroadbandBearer *self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
MMPortSerialAt *secondary,
MMPort *data,
guint cid,
GAsyncReadyCallback callback,
gpointer user_data)
{
DetailedDisconnectContext *ctx;
MMModem3gppRegistrationState registration_state;
GTask *task;
/* There is a known firmware bug that can leave the modem unusable if a
* disconnect attempt is made when out of coverage. So, fail without trying.
*/
g_object_get (modem,
MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &registration_state,
NULL);
if (registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN) {
g_task_report_new_error (self,
callback,
user_data,
disconnect_3gpp,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK,
"Out of coverage, can't disconnect.");
return;
}
ctx = g_new0 (DetailedDisconnectContext, 1);
ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->primary = g_object_ref (primary);
ctx->data = g_object_ref (data);
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_disconnect_context_free);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
"%DPDNACT=0",
MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, /* timeout */
FALSE, /* allow_cached */
FALSE, /* is_raw */
NULL, /* cancellable */
(GAsyncReadyCallback)disconnect_3gpp_check_status,
task); /* user_data */
}
/*****************************************************************************/
MMBaseBearer *
mm_broadband_bearer_altair_lte_new_finish (GAsyncResult *res,
GError **error)
{
GObject *bearer;
GObject *source;
source = g_async_result_get_source_object (res);
bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error);
g_object_unref (source);
if (!bearer)
return NULL;
/* Only export valid bearers */
mm_base_bearer_export (MM_BASE_BEARER (bearer));
return MM_BASE_BEARER (bearer);
}
void
mm_broadband_bearer_altair_lte_new (MMBroadbandModemAltairLte *modem,
MMBearerProperties *config,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_async_initable_new_async (
MM_TYPE_BROADBAND_BEARER_ALTAIR_LTE,
G_PRIORITY_DEFAULT,
cancellable,
callback,
user_data,
MM_BASE_BEARER_MODEM, modem,
MM_BASE_BEARER_CONFIG, config,
NULL);
}
static void
mm_broadband_bearer_altair_lte_init (MMBroadbandBearerAltairLte *self)
{
}
static void
mm_broadband_bearer_altair_lte_class_init (MMBroadbandBearerAltairLteClass *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 = NULL;
base_bearer_class->load_connection_status_finish = NULL;
broadband_bearer_class->connect_3gpp = connect_3gpp;
broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish;
broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;
broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish;
}