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:
committed by
Aleksander Morgado
parent
3de6cca46d
commit
32435b430f
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 ();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user