aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mm-iface-modem-3gpp.c12
-rw-r--r--src/mm-iface-modem-3gpp.h3
-rw-r--r--src/mm-iface-modem.c23
-rw-r--r--src/mm-iface-modem.h5
-rw-r--r--src/plugins/cinterion/mm-broadband-modem-cinterion.c122
-rw-r--r--src/plugins/cinterion/mm-modem-helpers-cinterion.c68
-rw-r--r--src/plugins/cinterion/mm-modem-helpers-cinterion.h8
-rw-r--r--src/plugins/cinterion/tests/test-modem-helpers-cinterion.c133
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 ();
}