diff options
-rw-r--r-- | src/mm-iface-modem-3gpp.c | 12 | ||||
-rw-r--r-- | src/mm-iface-modem-3gpp.h | 3 | ||||
-rw-r--r-- | src/mm-iface-modem.c | 23 | ||||
-rw-r--r-- | src/mm-iface-modem.h | 5 | ||||
-rw-r--r-- | src/plugins/cinterion/mm-broadband-modem-cinterion.c | 122 | ||||
-rw-r--r-- | src/plugins/cinterion/mm-modem-helpers-cinterion.c | 68 | ||||
-rw-r--r-- | src/plugins/cinterion/mm-modem-helpers-cinterion.h | 8 | ||||
-rw-r--r-- | src/plugins/cinterion/tests/test-modem-helpers-cinterion.c | 133 |
8 files changed, 353 insertions, 21 deletions
diff --git a/src/mm-iface-modem-3gpp.c b/src/mm-iface-modem-3gpp.c index 94d51f86..c103fd13 100644 --- a/src/mm-iface-modem-3gpp.c +++ b/src/mm-iface-modem-3gpp.c @@ -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) { diff --git a/src/mm-iface-modem-3gpp.h b/src/mm-iface-modem-3gpp.h index b0f74403..4860e5c0 100644 --- a/src/mm-iface-modem-3gpp.h +++ b/src/mm-iface-modem-3gpp.h @@ -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. * diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c index ff285dee..63996fb5 100644 --- a/src/mm-iface-modem.c +++ b/src/mm-iface-modem.c @@ -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) { diff --git a/src/mm-iface-modem.h b/src/mm-iface-modem.h index 6a82fa26..18c7e9ea 100644 --- a/src/mm-iface-modem.h +++ b/src/mm-iface-modem.h @@ -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, diff --git a/src/plugins/cinterion/mm-broadband-modem-cinterion.c b/src/plugins/cinterion/mm-broadband-modem-cinterion.c index 5cca384b..9cdd4a3f 100644 --- a/src/plugins/cinterion/mm-broadband-modem-cinterion.c +++ b/src/plugins/cinterion/mm-broadband-modem-cinterion.c @@ -1111,6 +1111,85 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self, } /*****************************************************************************/ +/* 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 */ static gint @@ -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; diff --git a/src/plugins/cinterion/mm-modem-helpers-cinterion.c b/src/plugins/cinterion/mm-modem-helpers-cinterion.c index e429cbc1..52b2baf8 100644 --- a/src/plugins/cinterion/mm-modem-helpers-cinterion.c +++ b/src/plugins/cinterion/mm-modem-helpers-cinterion.c @@ -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; +} diff --git a/src/plugins/cinterion/mm-modem-helpers-cinterion.h b/src/plugins/cinterion/mm-modem-helpers-cinterion.h index ad794c86..36b88315 100644 --- a/src/plugins/cinterion/mm-modem-helpers-cinterion.h +++ b/src/plugins/cinterion/mm-modem-helpers-cinterion.h @@ -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 */ diff --git a/src/plugins/cinterion/tests/test-modem-helpers-cinterion.c b/src/plugins/cinterion/tests/test-modem-helpers-cinterion.c index d4816199..ae76223c 100644 --- a/src/plugins/cinterion/tests/test-modem-helpers-cinterion.c +++ b/src/plugins/cinterion/tests/test-modem-helpers-cinterion.c @@ -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 (); } |