broadband-modem-cinterion: never discard operator or mode

When setting an operator, AT+COPS overwrites the currently set allowed mode. Equally, the other way around, when setting the current mode, the configured operator is overwritten.

Signed-off-by: Lukas Voegl <lvoegl@tdt.de>
This commit is contained in:
Lukas Voegl
2024-11-20 12:07:20 +00:00
committed by Aleksander Morgado
parent 3de6cca46d
commit 32435b430f
8 changed files with 353 additions and 21 deletions
+12
View File
@@ -3882,6 +3882,18 @@ mm_iface_modem_3gpp_shutdown (MMIfaceModem3gpp *self)
/*****************************************************************************/
gchar *
mm_iface_modem_3gpp_get_manual_registration_operator_id (MMIfaceModem3gpp *self)
{
Private *priv;
priv = get_private (self);
return g_strdup (priv->manual_registration_operator_id);
}
/*****************************************************************************/
static void
mm_iface_modem_3gpp_default_init (MMIfaceModem3gppInterface *iface)
{
+3
View File
@@ -323,6 +323,9 @@ gboolean mm_iface_modem_3gpp_sync_finish (MMIfaceModem3gpp *self,
/* Shutdown Modem 3GPP interface */
void mm_iface_modem_3gpp_shutdown (MMIfaceModem3gpp *self);
/* Helpers to query manual operator id */
gchar *mm_iface_modem_3gpp_get_manual_registration_operator_id (MMIfaceModem3gpp *self);
/* Objects implementing this interface can report new registration info,
* access technologies and location.
*
+23
View File
@@ -6683,6 +6683,29 @@ mm_iface_modem_get_carrier_config (MMIfaceModem *self,
/*****************************************************************************/
gboolean
mm_iface_modem_get_current_modes (MMIfaceModem *self,
MMModemMode *allowed,
MMModemMode *preferred)
{
g_autoptr(MmGdbusModemSkeleton) skeleton = NULL;
g_object_get (self,
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
NULL);
if (!skeleton)
return FALSE;
g_variant_get (mm_gdbus_modem_get_current_modes (MM_GDBUS_MODEM (skeleton)),
"(uu)",
allowed,
preferred);
return TRUE;
}
/*****************************************************************************/
static void
mm_iface_modem_default_init (MMIfaceModemInterface *iface)
{
+5
View File
@@ -451,6 +451,11 @@ gboolean mm_iface_modem_get_carrier_config (MMIfaceModem *self,
const gchar **name,
const gchar **revision);
/* Helpers to query current modes */
gboolean mm_iface_modem_get_current_modes (MMIfaceModem *self,
MMModemMode *allowed,
MMModemMode *preferred);
/* Initialize Modem interface (async) */
void mm_iface_modem_initialize (MMIfaceModem *self,
GCancellable *cancellable,
@@ -1110,6 +1110,85 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
task);
}
/*****************************************************************************/
/* Register in network (3GPP interface) */
static gboolean
modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
cops_set_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_finish (self, res, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static gboolean
is_valid_mode_combination (MMIfaceModem *self,
MMModemMode allowed)
{
return ((mm_iface_modem_is_4g (self) && allowed == MM_MODEM_MODE_4G) ||
(mm_iface_modem_is_3g (self) && allowed == MM_MODEM_MODE_3G) ||
(mm_iface_modem_is_2g (self) && allowed == MM_MODEM_MODE_2G) ||
allowed == MM_MODEM_MODE_ANY);
}
static void
modem_3gpp_register_in_network (MMIfaceModem3gpp *_self,
const gchar *operator_code,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
g_autofree gchar *command = NULL;
g_autoptr(GError) error = NULL;
MMModemMode allowed = MM_MODEM_MODE_NONE;
MMModemMode preferred = MM_MODEM_MODE_NONE;
GTask *task;
task = g_task_new (self, cancellable, callback, user_data);
if (!mm_iface_modem_get_current_modes (MM_IFACE_MODEM (self), &allowed, &preferred)) {
mm_obj_msg (self, "Could not get current modes, using any");
allowed = MM_MODEM_MODE_ANY;
} else if (!is_valid_mode_combination (MM_IFACE_MODEM (self), allowed)) {
mm_obj_msg (self, "Modem does not support mode '%s', using any",
mm_modem_mode_build_string_from_mask (allowed));
allowed = MM_MODEM_MODE_ANY;
}
/* Build cops command with selected mode and operator */
if (!mm_cinterion_build_cops_set_command (allowed,
operator_code,
&command,
&error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Set operator and mode using +COPS */
mm_base_modem_at_command (MM_BASE_MODEM (self),
command,
120,
FALSE,
(GAsyncReadyCallback)cops_set_ready,
task);
}
/*****************************************************************************/
/* Common operation to load expected CID for the initial EPS bearer */
@@ -1647,38 +1726,38 @@ cops_set_current_modes (MMBroadbandModemCinterion *self,
MMModemMode preferred,
GTask *task)
{
gchar *command;
g_autofree gchar *operator_id = NULL;
g_autofree gchar *command = NULL;
g_autoptr(GError) error = NULL;
g_assert (preferred == MM_MODEM_MODE_NONE);
operator_id = mm_iface_modem_3gpp_get_manual_registration_operator_id (MM_IFACE_MODEM_3GPP (self));
/* We will try to simulate the possible allowed modes here. The
* Cinterion devices do not seem to allow setting preferred access
* technology in devices, but they allow restricting to a given
* one:
* - 2G-only is forced by forcing GERAN RAT (AcT=0)
* - 3G-only is forced by forcing UTRAN RAT (AcT=2)
* - 4G-only is forced by forcing E-UTRAN RAT (AcT=7)
* - for the remaining ones, we default to automatic selection of RAT,
* which is based on the quality of the connection.
* one.
*/
if (mm_iface_modem_is_4g (MM_IFACE_MODEM (self)) && allowed == MM_MODEM_MODE_4G)
command = g_strdup ("+COPS=,,,7");
else if (mm_iface_modem_is_3g (MM_IFACE_MODEM (self)) && allowed == MM_MODEM_MODE_3G)
command = g_strdup ("+COPS=,,,2");
else if (mm_iface_modem_is_2g (MM_IFACE_MODEM (self)) && allowed == MM_MODEM_MODE_2G)
command = g_strdup ("+COPS=,,,0");
else {
/* For any other combination (e.g. ANY or no AcT given, defaults to Auto. For this case, we cannot provide
* AT+COPS=,,, (i.e. just without a last value). Instead, we need to
* re-run the last manual/automatic selection command which succeeded,
* (or auto by default if none was launched) */
if (!is_valid_mode_combination (MM_IFACE_MODEM (self), allowed)) {
/* Invalid device and mode combination. Default to automatic selection
* of RAT, which is based on the quality of the connection.
*/
mm_iface_modem_3gpp_reregister_in_network (MM_IFACE_MODEM_3GPP (self),
(GAsyncReadyCallback) set_current_modes_reregister_in_network_ready,
task);
return;
}
if (!mm_cinterion_build_cops_set_command (allowed,
operator_id,
&command,
&error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
mm_base_modem_at_command (
MM_BASE_MODEM (self),
command,
@@ -1686,8 +1765,6 @@ cops_set_current_modes (MMBroadbandModemCinterion *self,
FALSE,
(GAsyncReadyCallback)allowed_access_technology_update_ready,
task);
g_free (command);
}
static void
@@ -3122,6 +3199,9 @@ iface_modem_3gpp_init (MMIfaceModem3gppInterface *iface)
iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events;
iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish;
iface->register_in_network = modem_3gpp_register_in_network;
iface->register_in_network_finish = modem_3gpp_register_in_network_finish;
iface->load_initial_eps_bearer_settings = modem_3gpp_load_initial_eps_bearer_settings;
iface->load_initial_eps_bearer_settings_finish = modem_3gpp_load_initial_eps_bearer_settings_finish;
iface->set_initial_eps_bearer_settings = modem_3gpp_set_initial_eps_bearer_settings;
@@ -1934,3 +1934,71 @@ mm_cinterion_build_sxrat_set_command (MMModemMode allowed,
return g_string_free (command, FALSE);
}
static gboolean
modem_mode_to_cops_uint (MMModemMode mode,
guint *out,
GError **error)
{
switch (mode) {
case MM_MODEM_MODE_2G:
/* 2G-only force GERAN RAT (AcT=0) */
*out = 0;
break;
case MM_MODEM_MODE_3G:
/* 3G-only force UTRAN RAT (AcT=2) */
*out = 2;
break;
case MM_MODEM_MODE_4G:
/* 4G-only force E-UTRAN RAT (AcT=7) */
*out = 7;
break;
case MM_MODEM_MODE_NONE:
case MM_MODEM_MODE_CS:
case MM_MODEM_MODE_5G:
case MM_MODEM_MODE_ANY:
default:
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Cannot use mode '%s' for COPS",
mm_modem_mode_build_string_from_mask (mode));
return FALSE;
}
return TRUE;
}
gboolean
mm_cinterion_build_cops_set_command (MMModemMode mode,
const gchar *operator_code,
gchar **out,
GError **error)
{
GString *command;
guint cops_mode;
command = g_string_new ("+COPS=");
if (!operator_code && mode == MM_MODEM_MODE_ANY) {
/* any operator, any mode */
g_string_append (command, "0");
} else {
/* append <mode>,<operator format>,<operator> */
if (!operator_code)
g_string_append (command, "0,,");
else
g_string_append_printf (command, "1,2,\"%s\"", operator_code);
if (mode != MM_MODEM_MODE_ANY) {
/* append <RaT> */
if (!modem_mode_to_cops_uint (mode, &cops_mode, error)) {
return FALSE;
}
g_string_append_printf (command, ",%u", cops_mode);
}
}
*out = g_string_free (command, FALSE);
return TRUE;
}
@@ -229,4 +229,12 @@ gchar *mm_cinterion_build_sxrat_set_command (MMModemMode allowed,
MMModemMode preferred,
GError **error);
/*****************************************************************************/
/* +COPS command helper */
gboolean mm_cinterion_build_cops_set_command (MMModemMode mode,
const gchar *operator_code,
gchar **out,
GError **error);
#endif /* MM_MODEM_HELPERS_CINTERION_H */
@@ -1855,6 +1855,132 @@ test_sxrat_response_other (void)
g_array_unref (expected_pref1);
}
static void
test_cops_only_mode_2g (void)
{
const gchar *operator = NULL;
MMModemMode mode = MM_MODEM_MODE_2G;
GError *error = NULL;
g_autofree gchar *cops_command = NULL;
gboolean res;
res = mm_cinterion_build_cops_set_command (mode,
operator,
&cops_command,
&error);
g_assert_no_error (error);
g_assert (res == TRUE);
g_assert_cmpstr (cops_command, ==, "+COPS=0,,,0");
}
static void
test_cops_only_mode_3g (void)
{
const gchar *operator = NULL;
MMModemMode mode = MM_MODEM_MODE_3G;
GError *error = NULL;
g_autofree gchar *cops_command = NULL;
gboolean res;
res = mm_cinterion_build_cops_set_command (mode,
operator,
&cops_command,
&error);
g_assert_no_error (error);
g_assert (res == TRUE);
g_assert_cmpstr (cops_command, ==, "+COPS=0,,,2");
}
static void
test_cops_only_mode_4g (void)
{
const gchar *operator = NULL;
MMModemMode mode = MM_MODEM_MODE_4G;
GError *error = NULL;
g_autofree gchar *cops_command = NULL;
gboolean res;
res = mm_cinterion_build_cops_set_command (mode,
operator,
&cops_command,
&error);
g_assert_no_error (error);
g_assert (res == TRUE);
g_assert_cmpstr (cops_command, ==, "+COPS=0,,,7");
}
static void
test_cops_only_mode_other (void)
{
const gchar *operator = NULL;
MMModemMode mode = MM_MODEM_MODE_5G;
GError *error = NULL;
gchar *cops_command = NULL;
gboolean res;
res = mm_cinterion_build_cops_set_command (mode,
operator,
&cops_command,
&error);
g_assert (res == FALSE);
g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED);
g_clear_error (&error);
}
static void
test_cops_only_operator (void)
{
const gchar *operator = "26201";
MMModemMode mode = MM_MODEM_MODE_ANY;
GError *error = NULL;
g_autofree gchar *cops_command = NULL;
gboolean res;
res = mm_cinterion_build_cops_set_command (mode,
operator,
&cops_command,
&error);
g_assert_no_error (error);
g_assert (res == TRUE);
g_assert_cmpstr (cops_command, ==, "+COPS=1,2,\"26201\"");
}
static void
test_cops_operator_and_mode (void)
{
const gchar *operator = "26202";
MMModemMode mode = MM_MODEM_MODE_2G;
GError *error = NULL;
g_autofree gchar *cops_command = NULL;
gboolean res;
res = mm_cinterion_build_cops_set_command (mode,
operator,
&cops_command,
&error);
g_assert_no_error (error);
g_assert (res == TRUE);
g_assert_cmpstr (cops_command, ==, "+COPS=1,2,\"26202\",0");
}
static void
test_cops_any (void)
{
const gchar *operator = NULL;
MMModemMode mode = MM_MODEM_MODE_ANY;
GError *error = NULL;
g_autofree gchar *cops_command = NULL;
gboolean res;
res = mm_cinterion_build_cops_set_command (mode,
operator,
&cops_command,
&error);
g_assert_no_error (error);
g_assert (res == TRUE);
g_assert_cmpstr (cops_command, ==, "+COPS=0");
}
typedef struct {
const gchar *str;
MMModemMode allowed;
@@ -1962,6 +2088,13 @@ int main (int argc, char **argv)
g_test_add_func ("/MM/cinterion/sxrat", test_sxrat);
g_test_add_func ("/MM/cinterion/sxrat/response/els61", test_sxrat_response_els61);
g_test_add_func ("/MM/cinterion/sxrat/response/other", test_sxrat_response_other);
g_test_add_func ("/MM/cinterion/cops/only-mode-2g", test_cops_only_mode_2g);
g_test_add_func ("/MM/cinterion/cops/only-mode-3g", test_cops_only_mode_3g);
g_test_add_func ("/MM/cinterion/cops/only-mode-4g", test_cops_only_mode_4g);
g_test_add_func ("/MM/cinterion/cops/only-mode-other", test_cops_only_mode_other);
g_test_add_func ("/MM/cinterion/cops/only-operator", test_cops_only_operator);
g_test_add_func ("/MM/cinterion/cops/operator-and-mode", test_cops_operator_and_mode);
g_test_add_func ("/MM/cinterion/cops/any", test_cops_any);
return g_test_run ();
}