[PATCH 1/2] modem-3gpp: allow loading and changing EPS UE mode of operation

Aleksander Morgado aleksander at aleksander.es
Thu Dec 28 17:41:50 UTC 2017


The UE modes of operation for LTE are defined in 3GPP TS 24.301 (e.g.
section 4.3 in v10.3.0):
  * PS mode 1: EPS only, 'voice centric'
  * PS mode 2: EPS only, 'data centric'
  * CS/PS mode 1: EPS and non-EPS, 'voice centric'
  * CS/PS mode 2: EPS and non-EPS, 'data centric'

The mode specifies, among other things, how the UE should behave w.r.t
CS fallback depending on the capabilities reported by the network.
---
 cli/mmcli-modem-3gpp.c                             |  76 +++++++++
 cli/mmcli-modem.c                                  |   9 +-
 docs/reference/api/ModemManager-sections.txt       |   1 +
 docs/reference/libmm-glib/libmm-glib-sections.txt  |  13 ++
 include/ModemManager-enums.h                       |  18 +++
 ...g.freedesktop.ModemManager1.Modem.Modem3gpp.xml |  19 +++
 libmm-glib/mm-common-helpers.c                     |  22 +++
 libmm-glib/mm-common-helpers.h                     |   3 +
 libmm-glib/mm-modem-3gpp.c                         |  53 ++++++
 libmm-glib/mm-modem-3gpp.h                         |  14 ++
 src/mm-iface-modem-3gpp.c                          | 177 ++++++++++++++++++++-
 src/mm-iface-modem-3gpp.h                          |  17 ++
 12 files changed, 418 insertions(+), 4 deletions(-)

diff --git a/cli/mmcli-modem-3gpp.c b/cli/mmcli-modem-3gpp.c
index a785d025..b687d88d 100644
--- a/cli/mmcli-modem-3gpp.c
+++ b/cli/mmcli-modem-3gpp.c
@@ -49,6 +49,7 @@ static Context *ctx;
 static gboolean scan_flag;
 static gboolean register_home_flag;
 static gchar *register_in_operator_str;
+static gchar *set_eps_ue_mode_operation_str;
 static gboolean ussd_status_flag;
 static gchar *ussd_initiate_str;
 static gchar *ussd_respond_str;
@@ -67,6 +68,10 @@ static GOptionEntry entries[] = {
       "Request a given modem to register in the network of the given operator",
       "[MCCMNC]"
     },
+    { "3gpp-set-eps-ue-mode-operation", 0, 0, G_OPTION_ARG_STRING, &set_eps_ue_mode_operation_str,
+      "Set the UE mode of operation for EPS",
+      "[ps-1|ps-2|csps-1|csps-2]"
+    },
     { "3gpp-ussd-status", 0, 0, G_OPTION_ARG_NONE, &ussd_status_flag,
       "Show status of any ongoing USSD session",
       NULL
@@ -113,6 +118,7 @@ mmcli_modem_3gpp_options_enabled (void)
     n_actions = (scan_flag +
                  register_home_flag +
                  !!register_in_operator_str +
+                 !!set_eps_ue_mode_operation_str +
                  ussd_status_flag +
                  !!ussd_initiate_str +
                  !!ussd_respond_str +
@@ -291,6 +297,45 @@ register_ready (MMModem3gpp  *modem_3gpp,
     mmcli_async_operation_done ();
 }
 
+static void
+set_eps_ue_mode_operation_process_reply (gboolean      result,
+                                         const GError *error)
+{
+    if (!result) {
+        g_printerr ("error: couldn't set UE mode of operation for EPS: '%s'\n",
+                    error ? error->message : "unknown error");
+        exit (EXIT_FAILURE);
+    }
+
+    g_print ("successfully set UE mode of operation for EPS\n");
+}
+
+static void
+set_eps_ue_mode_operation_ready (MMModem3gpp  *modem,
+                                 GAsyncResult *result)
+{
+    gboolean operation_result;
+    GError *error = NULL;
+
+    operation_result = mm_modem_3gpp_set_eps_ue_mode_operation_finish (modem, result, &error);
+    set_eps_ue_mode_operation_process_reply (operation_result, error);
+
+    mmcli_async_operation_done ();
+}
+
+static void
+parse_eps_ue_mode_operation (MMModem3gppEpsUeModeOperation *uemode)
+{
+    GError *error = NULL;
+
+    *uemode = mm_common_get_eps_ue_mode_operation_from_string (set_eps_ue_mode_operation_str, &error);
+    if (error) {
+        g_printerr ("error: couldn't parse UE mode of operation for EPS: '%s'\n",
+                    error->message);
+        exit (EXIT_FAILURE);
+    }
+}
+
 static void
 print_ussd_status (void)
 {
@@ -438,6 +483,21 @@ get_modem_ready (GObject      *source,
         return;
     }
 
+    /* Request to set UE mode of operation for EPS? */
+    if (set_eps_ue_mode_operation_str) {
+        MMModem3gppEpsUeModeOperation uemode;
+
+        parse_eps_ue_mode_operation (&uemode);
+
+        g_debug ("Asynchronously setting UE mode of operation for EPS...");
+        mm_modem_3gpp_set_eps_ue_mode_operation (ctx->modem_3gpp,
+                                                 uemode,
+                                                 ctx->cancellable,
+                                                 (GAsyncReadyCallback)set_eps_ue_mode_operation_ready,
+                                                 NULL);
+        return;
+    }
+
     /* Request to initiate USSD session? */
     if (ussd_initiate_str) {
         ensure_modem_3gpp_ussd ();
@@ -538,6 +598,22 @@ mmcli_modem_3gpp_run_synchronous (GDBusConnection *connection)
         return;
     }
 
+    /* Request to set UE mode of operation for EPS? */
+    if (set_eps_ue_mode_operation_str) {
+        MMModem3gppEpsUeModeOperation uemode;
+        gboolean                      result;
+
+        parse_eps_ue_mode_operation (&uemode);
+
+        g_debug ("Synchronously setting UE mode of operation for EPS...");
+        result = mm_modem_3gpp_set_eps_ue_mode_operation_sync (ctx->modem_3gpp,
+                                                               uemode,
+                                                               NULL,
+                                                               &error);
+        set_eps_ue_mode_operation_process_reply (result, error);
+        return;
+    }
+
     /* Request to show USSD status? */
     if (ussd_status_flag) {
         ensure_modem_3gpp_ussd ();
diff --git a/cli/mmcli-modem.c b/cli/mmcli-modem.c
index a3c1152c..0f220f25 100644
--- a/cli/mmcli-modem.c
+++ b/cli/mmcli-modem.c
@@ -470,15 +470,18 @@ print_modem_info (void)
                  "           |    operator id: '%s'\n"
                  "           |  operator name: '%s'\n"
                  "           |   subscription: '%s'\n"
-                 "           |   registration: '%s'\n",
+                 "           |   registration: '%s'\n"
+                 "           |    EPS UE mode: '%s'\n",
                  VALIDATE_UNKNOWN (mm_modem_3gpp_get_imei (ctx->modem_3gpp)),
                  facility_locks,
                  VALIDATE_UNKNOWN (mm_modem_3gpp_get_operator_code (ctx->modem_3gpp)),
                  VALIDATE_UNKNOWN (mm_modem_3gpp_get_operator_name (ctx->modem_3gpp)),
                  mm_modem_3gpp_subscription_state_get_string (
-                     mm_modem_3gpp_get_subscription_state ((ctx->modem_3gpp))),
+                     mm_modem_3gpp_get_subscription_state (ctx->modem_3gpp)),
                  mm_modem_3gpp_registration_state_get_string (
-                     mm_modem_3gpp_get_registration_state ((ctx->modem_3gpp))));
+                     mm_modem_3gpp_get_registration_state (ctx->modem_3gpp)),
+                 mm_modem_3gpp_eps_ue_mode_operation_get_string (
+                     mm_modem_3gpp_get_eps_ue_mode_operation (ctx->modem_3gpp)));
 
         g_free (facility_locks);
     }
diff --git a/docs/reference/api/ModemManager-sections.txt b/docs/reference/api/ModemManager-sections.txt
index a60c3a41..497f6e38 100644
--- a/docs/reference/api/ModemManager-sections.txt
+++ b/docs/reference/api/ModemManager-sections.txt
@@ -22,6 +22,7 @@ MMModem3gppNetworkAvailability
 MMModem3gppSubscriptionState
 MMModem3gppRegistrationState
 MMModem3gppUssdSessionState
+MMModem3gppEpsUeModeOperation
 MMModemAccessTechnology
 MMModemBand
 MMModemCapability
diff --git a/docs/reference/libmm-glib/libmm-glib-sections.txt b/docs/reference/libmm-glib/libmm-glib-sections.txt
index fd790475..77df5a79 100644
--- a/docs/reference/libmm-glib/libmm-glib-sections.txt
+++ b/docs/reference/libmm-glib/libmm-glib-sections.txt
@@ -283,6 +283,9 @@ mm_modem_3gpp_register_sync
 mm_modem_3gpp_scan
 mm_modem_3gpp_scan_finish
 mm_modem_3gpp_scan_sync
+mm_modem_3gpp_set_eps_ue_mode_operation
+mm_modem_3gpp_set_eps_ue_mode_operation_finish
+mm_modem_3gpp_set_eps_ue_mode_operation_sync
 <SUBSECTION Standard>
 MMModem3gppClass
 MM_IS_MODEM_3GPP
@@ -1325,6 +1328,7 @@ mm_modem_3gpp_subscription_state_get_string
 mm_modem_3gpp_facility_build_string_from_mask
 mm_modem_3gpp_network_availability_get_string
 mm_modem_3gpp_ussd_session_state_get_string
+mm_modem_3gpp_eps_ue_mode_operation_get_string
 mm_modem_cdma_registration_state_get_string
 mm_modem_cdma_activation_state_get_string
 mm_modem_cdma_rm_protocol_get_string
@@ -1375,6 +1379,7 @@ mm_modem_3gpp_subscription_state_build_string_from_mask
 mm_modem_3gpp_facility_get_string
 mm_modem_3gpp_network_availability_build_string_from_mask
 mm_modem_3gpp_ussd_session_state_build_string_from_mask
+mm_modem_3gpp_eps_ue_mode_operation_build_string_from_mask
 mm_firmware_image_type_build_string_from_mask
 mm_modem_port_type_build_string_from_mask
 mm_oma_feature_get_string
@@ -1394,6 +1399,7 @@ MM_TYPE_MODEM_3GPP_NETWORK_AVAILABILITY
 MM_TYPE_MODEM_3GPP_REGISTRATION_STATE
 MM_TYPE_MODEM_3GPP_SUBSCRIPTION_STATE
 MM_TYPE_MODEM_3GPP_USSD_SESSION_STATE
+MM_TYPE_MODEM_3GPP_EPS_UE_MODE_OPERATION
 MM_TYPE_MODEM_ACCESS_TECHNOLOGY
 MM_TYPE_MODEM_BAND
 MM_TYPE_MODEM_CAPABILITY
@@ -1432,6 +1438,7 @@ mm_modem_3gpp_network_availability_get_type
 mm_modem_3gpp_registration_state_get_type
 mm_modem_3gpp_subscription_state_get_type
 mm_modem_3gpp_ussd_session_state_get_type
+mm_modem_3gpp_eps_ue_mode_operation_get_type
 mm_modem_access_technology_get_type
 mm_modem_band_get_type
 mm_modem_capability_get_type
@@ -1666,6 +1673,7 @@ mm_gdbus_modem3gpp_get_operator_name
 mm_gdbus_modem3gpp_dup_operator_name
 mm_gdbus_modem3gpp_get_enabled_facility_locks
 mm_gdbus_modem3gpp_get_subscription_state
+mm_gdbus_modem3gpp_get_eps_ue_mode_operation
 <SUBSECTION Methods>
 mm_gdbus_modem3gpp_call_register
 mm_gdbus_modem3gpp_call_register_finish
@@ -1673,9 +1681,13 @@ mm_gdbus_modem3gpp_call_register_sync
 mm_gdbus_modem3gpp_call_scan
 mm_gdbus_modem3gpp_call_scan_finish
 mm_gdbus_modem3gpp_call_scan_sync
+mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation
+mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation_finish
+mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation_sync
 <SUBSECTION Private>
 mm_gdbus_modem3gpp_complete_register
 mm_gdbus_modem3gpp_complete_scan
+mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation
 mm_gdbus_modem3gpp_interface_info
 mm_gdbus_modem3gpp_override_properties
 mm_gdbus_modem3gpp_set_enabled_facility_locks
@@ -1684,6 +1696,7 @@ mm_gdbus_modem3gpp_set_operator_code
 mm_gdbus_modem3gpp_set_operator_name
 mm_gdbus_modem3gpp_set_registration_state
 mm_gdbus_modem3gpp_set_subscription_state
+mm_gdbus_modem3gpp_set_eps_ue_mode_operation
 <SUBSECTION Standard>
 MM_GDBUS_IS_MODEM3GPP
 MM_GDBUS_MODEM3GPP
diff --git a/include/ModemManager-enums.h b/include/ModemManager-enums.h
index ff4779fd..8f05ec00 100644
--- a/include/ModemManager-enums.h
+++ b/include/ModemManager-enums.h
@@ -1155,6 +1155,24 @@ typedef enum { /*< underscore_name=mm_modem_3gpp_ussd_session_state >*/
     MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE = 3,
 } MMModem3gppUssdSessionState;
 
+/**
+ * MMModem3gppEpsUeModeOperation:
+ * @MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN: Unknown or not applicable.
+ * @MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_1: PS mode 1 of operation: EPS only, voice-centric.
+ * @MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_2: PS mode 2 of operation: EPS only, data-centric.
+ * @MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_1: CS/PS mode 1 of operation: EPS and non-EPS, voice-centric.
+ * @MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_2: CS/PS mode 2 of operation: EPS and non-EPS, data-centric.
+ *
+ * UE mode of operation for EPS, as per 3GPP TS 24.301.
+ */
+typedef enum { /*< underscore_name=mm_modem_3gpp_eps_ue_mode_operation >*/
+    MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN = 0,
+    MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_1    = 1,
+    MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_2    = 2,
+    MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_1  = 3,
+    MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_2  = 4,
+} MMModem3gppEpsUeModeOperation;
+
 /**
  * MMFirmwareImageType:
  * @MM_FIRMWARE_IMAGE_TYPE_UNKNOWN: Unknown firmware type.
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml b/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml
index 6d082f70..1aaba4c0 100644
--- a/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml
+++ b/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml
@@ -85,6 +85,16 @@
       <arg name="results" type="aa{sv}" direction="out" />
     </method>
 
+    <!--
+        SetEpsUeModeOperation:
+        @mode: a <link linkend="MMModem3gppEpsUeModeOperation">MMModem3gppEpsUeModeOperation</link>.
+
+        Sets the UE mode of operation for EPS.
+    -->
+    <method name="SetEpsUeModeOperation">
+      <arg name="mode" type="u" direction="in" />
+    </method>
+
     <!--
         Imei:
 
@@ -145,5 +155,14 @@
     -->
     <property name="SubscriptionState" type="u" access="read" />
 
+    <!--
+        EpsUeModeOperation:
+
+        A <link linkend="MMModem3gppEpsUeModeOperation">MMModem3gppEpsUeModeOperation</link>
+        value representing the UE mode of operation for EPS, given as an unsigned integer
+        (signature <literal>"u"</literal>).
+    -->
+    <property name="EpsUeModeOperation" type="u" access="read" />
+
   </interface>
 </node>
diff --git a/libmm-glib/mm-common-helpers.c b/libmm-glib/mm-common-helpers.c
index f99e58d8..3a009ad3 100644
--- a/libmm-glib/mm-common-helpers.c
+++ b/libmm-glib/mm-common-helpers.c
@@ -758,6 +758,28 @@ mm_common_build_mode_combinations_default (void)
     return g_variant_builder_end (&builder);
 }
 
+MMModem3gppEpsUeModeOperation
+mm_common_get_eps_ue_mode_operation_from_string (const gchar  *str,
+                                                 GError      **error)
+{
+    GEnumClass *enum_class;
+    guint       i;
+
+    enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_MODEM_3GPP_EPS_UE_MODE_OPERATION));
+
+    for (i = 0; enum_class->values[i].value_nick; i++) {
+        if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
+            return enum_class->values[i].value;
+    }
+
+    g_set_error (error,
+                 MM_CORE_ERROR,
+                 MM_CORE_ERROR_INVALID_ARGS,
+                 "Couldn't match '%s' with a valid MMModem3gppEpsUeModeOperation value",
+                 str);
+    return MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN;
+}
+
 GArray *
 mm_common_oma_pending_network_initiated_sessions_variant_to_garray (GVariant *variant)
 {
diff --git a/libmm-glib/mm-common-helpers.h b/libmm-glib/mm-common-helpers.h
index 772068e9..229186ce 100644
--- a/libmm-glib/mm-common-helpers.h
+++ b/libmm-glib/mm-common-helpers.h
@@ -76,6 +76,9 @@ MMOmaFeature          mm_common_get_oma_features_from_string (const gchar *str,
 MMOmaSessionType      mm_common_get_oma_session_type_from_string (const gchar *str,
                                                                   GError **error);
 
+MMModem3gppEpsUeModeOperation mm_common_get_eps_ue_mode_operation_from_string (const gchar  *str,
+                                                                               GError      **error);
+
 GArray          *mm_common_ports_variant_to_garray (GVariant *variant);
 MMModemPortInfo *mm_common_ports_variant_to_array  (GVariant *variant,
                                                  guint *n_ports);
diff --git a/libmm-glib/mm-modem-3gpp.c b/libmm-glib/mm-modem-3gpp.c
index a926acc1..69edc276 100644
--- a/libmm-glib/mm-modem-3gpp.c
+++ b/libmm-glib/mm-modem-3gpp.c
@@ -280,6 +280,22 @@ mm_modem_3gpp_get_enabled_facility_locks (MMModem3gpp *self)
     return mm_gdbus_modem3gpp_get_enabled_facility_locks (MM_GDBUS_MODEM3GPP (self));
 }
 
+/**
+ * mm_modem_3gpp_get_eps_ue_mode_operation:
+ * @self: A #MMModem3gpp.
+ *
+ * Get the UE mode of operation for EPS.
+ *
+ * Returns: A #MMModem3gppEpsUeModeOperation.
+ */
+MMModem3gppEpsUeModeOperation
+mm_modem_3gpp_get_eps_ue_mode_operation (MMModem3gpp *self)
+{
+    g_return_val_if_fail (MM_IS_MODEM_3GPP (self), MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN);
+
+    return mm_gdbus_modem3gpp_get_eps_ue_mode_operation (MM_GDBUS_MODEM3GPP (self));
+}
+
 /*****************************************************************************/
 
 /**
@@ -590,6 +606,43 @@ mm_modem_3gpp_scan_sync (MMModem3gpp *self,
 
 /*****************************************************************************/
 
+gboolean
+mm_modem_3gpp_set_eps_ue_mode_operation_finish (MMModem3gpp   *self,
+                                                GAsyncResult  *res,
+                                                GError       **error)
+{
+    g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE);
+
+    return mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation_finish (MM_GDBUS_MODEM3GPP (self), res, error);
+}
+
+void
+mm_modem_3gpp_set_eps_ue_mode_operation (MMModem3gpp                    *self,
+                                         MMModem3gppEpsUeModeOperation   mode,
+                                         GCancellable                   *cancellable,
+                                         GAsyncReadyCallback             callback,
+                                         gpointer                        user_data)
+{
+    g_return_if_fail (MM_IS_MODEM_3GPP (self));
+    g_return_if_fail (mode != MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN);
+
+    mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation (MM_GDBUS_MODEM3GPP (self), (guint) mode, cancellable, callback, user_data);
+}
+
+gboolean
+mm_modem_3gpp_set_eps_ue_mode_operation_sync (MMModem3gpp                    *self,
+                                              MMModem3gppEpsUeModeOperation   mode,
+                                              GCancellable                   *cancellable,
+                                              GError                        **error)
+{
+    g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE);
+    g_return_val_if_fail (mode != MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN, FALSE);
+
+    return mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation_sync (MM_GDBUS_MODEM3GPP (self), (guint) mode, cancellable, error);
+}
+
+/*****************************************************************************/
+
 static void
 mm_modem_3gpp_init (MMModem3gpp *self)
 {
diff --git a/libmm-glib/mm-modem-3gpp.h b/libmm-glib/mm-modem-3gpp.h
index 7cfe0115..a3b37f08 100644
--- a/libmm-glib/mm-modem-3gpp.h
+++ b/libmm-glib/mm-modem-3gpp.h
@@ -80,6 +80,7 @@ MMModem3gppSubscriptionState  mm_modem_3gpp_get_subscription_state     (MMModem3
 
 MMModem3gppFacility           mm_modem_3gpp_get_enabled_facility_locks (MMModem3gpp *self);
 
+MMModem3gppEpsUeModeOperation mm_modem_3gpp_get_eps_ue_mode_operation  (MMModem3gpp *self);
 
 void     mm_modem_3gpp_register        (MMModem3gpp *self,
                                         const gchar *network_id,
@@ -120,6 +121,19 @@ GList *mm_modem_3gpp_scan_sync   (MMModem3gpp *self,
                                   GCancellable *cancellable,
                                   GError **error);
 
+void     mm_modem_3gpp_set_eps_ue_mode_operation        (MMModem3gpp                    *self,
+                                                         MMModem3gppEpsUeModeOperation   mode,
+                                                         GCancellable                   *cancellable,
+                                                         GAsyncReadyCallback             callback,
+                                                         gpointer                        user_data);
+gboolean mm_modem_3gpp_set_eps_ue_mode_operation_finish (MMModem3gpp                    *self,
+                                                         GAsyncResult                   *res,
+                                                         GError                        **error);
+gboolean mm_modem_3gpp_set_eps_ue_mode_operation_sync   (MMModem3gpp                    *self,
+                                                         MMModem3gppEpsUeModeOperation   mode,
+                                                         GCancellable                   *cancellable,
+                                                         GError                        **error);
+
 G_END_DECLS
 
 #endif /* _MM_MODEM_3GPP_H_ */
diff --git a/src/mm-iface-modem-3gpp.c b/src/mm-iface-modem-3gpp.c
index e1a61ced..7a9f7785 100644
--- a/src/mm-iface-modem-3gpp.c
+++ b/src/mm-iface-modem-3gpp.c
@@ -774,6 +774,141 @@ handle_scan (MmGdbusModem3gpp *skeleton,
 
 /*****************************************************************************/
 
+typedef struct {
+    MmGdbusModem3gpp              *skeleton;
+    GDBusMethodInvocation         *invocation;
+    MMIfaceModem3gpp              *self;
+    MMModem3gppEpsUeModeOperation  mode;
+} HandleSetEpsUeModeOperationContext;
+
+static void
+handle_set_eps_ue_mode_operation_context_free (HandleSetEpsUeModeOperationContext *ctx)
+{
+    g_object_unref (ctx->skeleton);
+    g_object_unref (ctx->invocation);
+    g_object_unref (ctx->self);
+    g_slice_free (HandleSetEpsUeModeOperationContext, ctx);
+}
+
+static void
+after_set_load_eps_ue_mode_operation_ready (MMIfaceModem3gpp                   *self,
+                                            GAsyncResult                       *res,
+                                            HandleSetEpsUeModeOperationContext *ctx)
+{
+    MMModem3gppEpsUeModeOperation  uemode;
+    GError                        *error = NULL;
+
+    uemode = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish (self, res, &error);
+    if (error) {
+        g_dbus_method_invocation_take_error (ctx->invocation, error);
+        handle_set_eps_ue_mode_operation_context_free (ctx);
+        return;
+    }
+
+    if (uemode != ctx->mode) {
+        g_dbus_method_invocation_return_error_literal (ctx->invocation,
+                                                       MM_CORE_ERROR,
+                                                       MM_CORE_ERROR_FAILED,
+                                                       "UE mode of operation for EPS wasn't updated");
+        handle_set_eps_ue_mode_operation_context_free (ctx);
+        return;
+    }
+
+    mm_gdbus_modem3gpp_set_eps_ue_mode_operation (ctx->skeleton, uemode);
+    mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation (ctx->skeleton, ctx->invocation);
+    handle_set_eps_ue_mode_operation_context_free (ctx);
+}
+
+static void
+handle_set_eps_ue_mode_operation_ready (MMIfaceModem3gpp                   *self,
+                                        GAsyncResult                       *res,
+                                        HandleSetEpsUeModeOperationContext *ctx)
+{
+    GError *error = NULL;
+
+    if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation_finish (self, res, &error)) {
+        g_dbus_method_invocation_take_error (ctx->invocation, error);
+        handle_set_eps_ue_mode_operation_context_free (ctx);
+        return;
+    }
+
+    if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation &&
+        MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish) {
+        MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation (
+            self,
+            (GAsyncReadyCallback)after_set_load_eps_ue_mode_operation_ready,
+            ctx);
+        return;
+    }
+
+    /* Assume we're ok */
+    mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation (ctx->skeleton, ctx->invocation);
+    handle_set_eps_ue_mode_operation_context_free (ctx);
+}
+
+static void
+handle_set_eps_ue_mode_operation_auth_ready (MMBaseModem                        *self,
+                                             GAsyncResult                       *res,
+                                             HandleSetEpsUeModeOperationContext *ctx)
+{
+    GError *error = NULL;
+
+    if (!mm_base_modem_authorize_finish (self, res, &error)) {
+        g_dbus_method_invocation_take_error (ctx->invocation, error);
+        handle_set_eps_ue_mode_operation_context_free (ctx);
+        return;
+    }
+
+    /* Check if we already are in the requested mode */
+    if (mm_gdbus_modem3gpp_get_eps_ue_mode_operation (ctx->skeleton) == ctx->mode) {
+        /* Nothing to do */
+        mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation (ctx->skeleton, ctx->invocation);
+        handle_set_eps_ue_mode_operation_context_free (ctx);
+        return;
+    }
+
+    /* If UE mode update is not implemented, report an error */
+    if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation ||
+        !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation_finish) {
+        g_dbus_method_invocation_return_error (ctx->invocation,
+                                               MM_CORE_ERROR,
+                                               MM_CORE_ERROR_UNSUPPORTED,
+                                               "Cannot set UE mode of operation for EPS: operation not supported");
+        handle_set_eps_ue_mode_operation_context_free (ctx);
+        return;
+    }
+
+    MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation (
+        MM_IFACE_MODEM_3GPP (self),
+        ctx->mode,
+        (GAsyncReadyCallback)handle_set_eps_ue_mode_operation_ready,
+        ctx);
+}
+
+static gboolean
+handle_set_eps_ue_mode_operation (MmGdbusModem3gpp      *skeleton,
+                                  GDBusMethodInvocation *invocation,
+                                  guint                  mode,
+                                  MMIfaceModem3gpp      *self)
+{
+    HandleSetEpsUeModeOperationContext *ctx;
+
+    ctx = g_slice_new (HandleSetEpsUeModeOperationContext);
+    ctx->skeleton   = g_object_ref (skeleton);
+    ctx->invocation = g_object_ref (invocation);
+    ctx->self       = g_object_ref (self);
+    ctx->mode       = mode;
+
+    mm_base_modem_authorize (MM_BASE_MODEM (self),
+                             invocation,
+                             MM_AUTHORIZATION_DEVICE_CONTROL,
+                             (GAsyncReadyCallback)handle_set_eps_ue_mode_operation_auth_ready,
+                             ctx);
+    return TRUE;
+}
+
+/*****************************************************************************/
+
 gboolean
 mm_iface_modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self,
                                                     GAsyncResult *res,
@@ -1861,6 +1996,7 @@ typedef enum {
     INITIALIZATION_STEP_FIRST,
     INITIALIZATION_STEP_IMEI,
     INITIALIZATION_STEP_ENABLED_FACILITY_LOCKS,
+    INITIALIZATION_STEP_EPS_UE_MODE_OPERATION,
     INITIALIZATION_STEP_LAST
 } InitializationStep;
 
@@ -1892,6 +2028,30 @@ sim_pin_lock_enabled_cb (MMBaseSim *self,
     mm_gdbus_modem3gpp_set_enabled_facility_locks (skeleton, facilities);
 }
 
+static void
+load_eps_ue_mode_operation_ready (MMIfaceModem3gpp *self,
+                                  GAsyncResult     *res,
+                                  GTask            *task)
+{
+    InitializationContext         *ctx;
+    MMModem3gppEpsUeModeOperation  uemode;
+    GError                        *error = NULL;
+
+    ctx = g_task_get_task_data (task);
+
+    uemode = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish (self, res, &error);
+    mm_gdbus_modem3gpp_set_eps_ue_mode_operation (ctx->skeleton, uemode);
+
+    if (error) {
+        mm_warn ("couldn't load UE mode of operation for EPS: '%s'", error->message);
+        g_error_free (error);
+    }
+
+    /* Go on to next step */
+    ctx->step++;
+    interface_initialization_step (task);
+}
+
 static void
 load_enabled_facility_locks_ready (MMIfaceModem3gpp *self,
                                    GAsyncResult *res,
@@ -2004,6 +2164,18 @@ interface_initialization_step (GTask *task)
         /* Fall down to next step */
         ctx->step++;
 
+    case INITIALIZATION_STEP_EPS_UE_MODE_OPERATION:
+        if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation &&
+            MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish) {
+            MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation (
+                self,
+                (GAsyncReadyCallback)load_eps_ue_mode_operation_ready,
+                task);
+            return;
+        }
+        /* Fall down to next step */
+        ctx->step++;
+
     case INITIALIZATION_STEP_LAST:
         /* We are done without errors! */
 
@@ -2016,7 +2188,10 @@ interface_initialization_step (GTask *task)
                           "handle-scan",
                           G_CALLBACK (handle_scan),
                           self);
-
+        g_signal_connect (ctx->skeleton,
+                          "handle-set-eps-ue-mode-operation",
+                          G_CALLBACK (handle_set_eps_ue_mode_operation),
+                          self);
 
         /* Finally, export the new interface */
         mm_gdbus_object_skeleton_set_modem3gpp (MM_GDBUS_OBJECT_SKELETON (self),
diff --git a/src/mm-iface-modem-3gpp.h b/src/mm-iface-modem-3gpp.h
index 6237b8aa..7b6d47e1 100644
--- a/src/mm-iface-modem-3gpp.h
+++ b/src/mm-iface-modem-3gpp.h
@@ -68,6 +68,14 @@ struct _MMIfaceModem3gpp {
                                                                GAsyncResult *res,
                                                                GError **error);
 
+    /* Loading of the UE mode of operation for EPS property */
+    void                          (* load_eps_ue_mode_operation)        (MMIfaceModem3gpp     *self,
+                                                                         GAsyncReadyCallback   callback,
+                                                                         gpointer              user_data);
+    MMModem3gppEpsUeModeOperation (* load_eps_ue_mode_operation_finish) (MMIfaceModem3gpp     *self,
+                                                                         GAsyncResult         *res,
+                                                                         GError              **error);
+
     /* Asynchronous setting up unsolicited events */
     void (*setup_unsolicited_events) (MMIfaceModem3gpp *self,
                                       GAsyncReadyCallback callback,
@@ -191,6 +199,15 @@ struct _MMIfaceModem3gpp {
     GList * (*scan_networks_finish) (MMIfaceModem3gpp *self,
                                      GAsyncResult *res,
                                      GError **error);
+
+    /* Set UE mode of operation for EPS */
+    void     (* set_eps_ue_mode_operation)        (MMIfaceModem3gpp               *self,
+                                                   MMModem3gppEpsUeModeOperation   mode,
+                                                   GAsyncReadyCallback             callback,
+                                                   gpointer                        user_data);
+    gboolean (* set_eps_ue_mode_operation_finish) (MMIfaceModem3gpp               *self,
+                                                   GAsyncResult                   *res,
+                                                   GError                        **error);
 };
 
 GType mm_iface_modem_3gpp_get_type (void);
-- 
2.15.0



More information about the ModemManager-devel mailing list