aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/mmcli-modem.c51
-rw-r--r--docs/man/mmcli.16
-rw-r--r--docs/reference/libmm-glib/libmm-glib-sections.txt7
-rw-r--r--introspection/org.freedesktop.ModemManager1.Modem.xml18
-rw-r--r--libmm-glib/mm-modem.c87
-rw-r--r--libmm-glib/mm-modem.h13
-rw-r--r--src/mm-iface-modem.c112
-rw-r--r--src/mm-iface-modem.h9
8 files changed, 303 insertions, 0 deletions
diff --git a/cli/mmcli-modem.c b/cli/mmcli-modem.c
index 2ca24495..9d82fa33 100644
--- a/cli/mmcli-modem.c
+++ b/cli/mmcli-modem.c
@@ -63,6 +63,7 @@ static gchar *set_current_capabilities_str;
static gchar *set_allowed_modes_str;
static gchar *set_preferred_mode_str;
static gchar *set_current_bands_str;
+static gint set_primary_sim_slot_int;
static gboolean inhibit_flag;
static GOptionEntry entries[] = {
@@ -126,6 +127,10 @@ static GOptionEntry entries[] = {
"Set bands to be used by a given modem.",
"[BAND1|BAND2...]"
},
+ { "set-primary-sim-slot", 0, 0, G_OPTION_ARG_INT, &set_primary_sim_slot_int,
+ "Switch to the selected SIM slot",
+ "[SLOT NUMBER]"
+ },
{ "inhibit", 0, 0, G_OPTION_ARG_NONE, &inhibit_flag,
"Inhibit the modem",
NULL
@@ -173,6 +178,7 @@ mmcli_modem_options_enabled (void)
!!set_allowed_modes_str +
!!set_preferred_mode_str +
!!set_current_bands_str +
+ (set_primary_sim_slot_int > 0) +
inhibit_flag);
if (n_actions == 0 && mmcli_get_common_modem_string ()) {
@@ -891,6 +897,32 @@ parse_current_bands (MMModemBand **bands,
}
static void
+set_primary_sim_slot_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't request primary SIM switch: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully requested primary SIM switch in modem\n");
+}
+
+static void
+set_primary_sim_slot_ready (MMModem *modem,
+ GAsyncResult *result)
+{
+ gboolean operation_result;
+ g_autoptr(GError) error = NULL;
+
+ operation_result = mm_modem_set_primary_sim_slot_finish (modem, result, &error);
+ set_primary_sim_slot_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
state_changed (MMModem *modem,
MMModemState old_state,
MMModemState new_state,
@@ -1132,6 +1164,16 @@ get_modem_ready (GObject *source,
return;
}
+ /* Request to switch SIM? */
+ if (set_primary_sim_slot_int > 0) {
+ mm_modem_set_primary_sim_slot (ctx->modem,
+ set_primary_sim_slot_int,
+ ctx->cancellable,
+ (GAsyncReadyCallback)set_primary_sim_slot_ready,
+ NULL);
+ return;
+ }
+
/* Request to inhibit the modem? */
if (inhibit_flag) {
gchar *uid;
@@ -1391,5 +1433,14 @@ mmcli_modem_run_synchronous (GDBusConnection *connection)
return;
}
+ /* Request to switch current SIM? */
+ if (set_primary_sim_slot_int > 0) {
+ gboolean result;
+
+ result = mm_modem_set_primary_sim_slot_sync (ctx->modem, set_primary_sim_slot_int, NULL, &error);
+ set_primary_sim_slot_process_reply (result, error);
+ return;
+ }
+
g_warn_if_reached ();
}
diff --git a/docs/man/mmcli.1 b/docs/man/mmcli.1
index 3730a883..3e86931c 100644
--- a/docs/man/mmcli.1
+++ b/docs/man/mmcli.1
@@ -281,6 +281,12 @@ MMModemBand documentation.
An example would be: 'egsm|dcs|pcs|g850' to select all the GSM
frequency bands.
.TP
+.B \-\-set\-primary\-sim\-slot=[SLOT]
+Request to switch the primary SIM slot.
+
+The given \fBSLOT\fR must be a valid slot number in the [1,N] range, where
+N is the amount of SIM slots available in the system.
+.TP
.B \-\-inhibit
Inhibit the specific modem from being used by ModemManager. This method
is completely equivalent to \fB\-\-inhibit\-device\fR, with the only
diff --git a/docs/reference/libmm-glib/libmm-glib-sections.txt b/docs/reference/libmm-glib/libmm-glib-sections.txt
index eb8d66e2..1e283f2c 100644
--- a/docs/reference/libmm-glib/libmm-glib-sections.txt
+++ b/docs/reference/libmm-glib/libmm-glib-sections.txt
@@ -188,6 +188,9 @@ mm_modem_get_primary_sim_slot
mm_modem_list_sim_slots
mm_modem_list_sim_slots_finish
mm_modem_list_sim_slots_sync
+mm_modem_set_primary_sim_slot
+mm_modem_set_primary_sim_slot_finish
+mm_modem_set_primary_sim_slot_sync
<SUBSECTION Methods>
mm_modem_enable
mm_modem_enable_finish
@@ -2135,6 +2138,9 @@ mm_gdbus_modem_call_set_current_bands_sync
mm_gdbus_modem_call_set_current_capabilities
mm_gdbus_modem_call_set_current_capabilities_finish
mm_gdbus_modem_call_set_current_capabilities_sync
+mm_gdbus_modem_call_set_primary_sim_slot
+mm_gdbus_modem_call_set_primary_sim_slot_finish
+mm_gdbus_modem_call_set_primary_sim_slot_sync
mm_gdbus_modem_call_command
mm_gdbus_modem_call_command_finish
mm_gdbus_modem_call_command_sync
@@ -2185,6 +2191,7 @@ mm_gdbus_modem_complete_reset
mm_gdbus_modem_complete_set_current_modes
mm_gdbus_modem_complete_set_current_bands
mm_gdbus_modem_complete_set_current_capabilities
+mm_gdbus_modem_complete_set_primary_sim_slot
mm_gdbus_modem_interface_info
mm_gdbus_modem_override_properties
<SUBSECTION Standard>
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.xml b/introspection/org.freedesktop.ModemManager1.Modem.xml
index 9b8d2d6d..d9dbd436 100644
--- a/introspection/org.freedesktop.ModemManager1.Modem.xml
+++ b/introspection/org.freedesktop.ModemManager1.Modem.xml
@@ -183,6 +183,24 @@
</method>
<!--
+ SetPrimarySimSlot:
+ @sim_slot: SIM slot number to set as primary.
+
+ Selects which SIM slot to be considered as primary, on devices that expose
+ multiple slots in the #org.freedesktop.ModemManager1.Modem:SimSlots property.
+
+ When the switch happens the modem may require a full device reprobe, so the modem
+ object in DBus will get removed, and recreated once the selected SIM slot is in
+ use.
+
+ There is no limitation on which SIM slot to select, so the user may also set as
+ primary a slot that doesn't currently have any valid SIM card inserted.
+ -->
+ <method name="SetPrimarySimSlot">
+ <arg name="sim_slot" type="u" direction="in" />
+ </method>
+
+ <!--
Command:
@cmd: The command string, e.g. "AT+GCAP" or "+GCAP" (leading AT is inserted if necessary).
@timeout: The number of seconds to wait for a response.
diff --git a/libmm-glib/mm-modem.c b/libmm-glib/mm-modem.c
index 96e113bd..38300d49 100644
--- a/libmm-glib/mm-modem.c
+++ b/libmm-glib/mm-modem.c
@@ -3699,6 +3699,93 @@ mm_modem_list_sim_slots_sync (MMModem *self,
/*****************************************************************************/
+/**
+ * mm_modem_set_primary_sim_slot_finish:
+ * @self: A #MMModem.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_set_primary_sim_slot().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_set_primary_sim_slot().
+ *
+ * Returns: %TRUE if the SIM slot switch has been successfully requested, %FALSE if
+ * @error is set.
+ *
+ * Since: 1.16
+ */
+gboolean
+mm_modem_set_primary_sim_slot_finish (MMModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
+
+ return mm_gdbus_modem_call_set_primary_sim_slot_finish (MM_GDBUS_MODEM (self), res, error);
+}
+
+/**
+ * mm_modem_set_primary_sim_slot:
+ * @self: A #MMModem.
+ * @sim_slot: SIM slot number.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously requests to select which SIM slot to be considered as primary.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_set_primary_sim_slot_finish() to get the result of the operation.
+ *
+ * See mm_modem_set_primary_sim_slot_sync() for the synchronous, blocking version of
+ * this method.
+ *
+ * Since: 1.16
+ */
+void
+mm_modem_set_primary_sim_slot (MMModem *self,
+ guint sim_slot,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+
+ mm_gdbus_modem_call_set_primary_sim_slot (MM_GDBUS_MODEM (self), sim_slot, cancellable, callback, user_data);
+}
+
+/**
+ * mm_modem_set_primary_sim_slot_sync:
+ * @self: A #MMModem.
+ * @sim_slot: SIM slot number.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously requests to select which SIM slot to be considered as primary.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_set_primary_sim_slot() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the SIM slot switch has been successfully requested, %FALSE if
+ * @error is set.
+ *
+ * Since: 1.16
+ */
+gboolean
+mm_modem_set_primary_sim_slot_sync (MMModem *self,
+ guint sim_slot,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
+
+ return mm_gdbus_modem_call_set_primary_sim_slot_sync (MM_GDBUS_MODEM (self), sim_slot, cancellable, error);
+}
+
+/*****************************************************************************/
+
static void
mm_modem_init (MMModem *self)
{
diff --git a/libmm-glib/mm-modem.h b/libmm-glib/mm-modem.h
index 58732204..0d887eae 100644
--- a/libmm-glib/mm-modem.h
+++ b/libmm-glib/mm-modem.h
@@ -360,6 +360,19 @@ GPtrArray *mm_modem_list_sim_slots_sync (MMModem *self,
GCancellable *cancellable,
GError **error);
+void mm_modem_set_primary_sim_slot (MMModem *self,
+ guint sim_slot,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_set_primary_sim_slot_finish (MMModem *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_set_primary_sim_slot_sync (MMModem *self,
+ guint sim_slot,
+ GCancellable *cancellable,
+ GError **error);
+
G_END_DECLS
#endif /* _MM_MODEM_H_ */
diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c
index 9045d290..955453be 100644
--- a/src/mm-iface-modem.c
+++ b/src/mm-iface-modem.c
@@ -957,6 +957,117 @@ handle_list_bearers (MmGdbusModem *skeleton,
/*****************************************************************************/
+typedef struct {
+ MmGdbusModem *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModem *self;
+ guint requested_sim_slot;
+} HandleSetPrimarySimSlotContext;
+
+static void
+handle_set_primary_sim_slot_context_free (HandleSetPrimarySimSlotContext *ctx)
+{
+ g_clear_object (&ctx->skeleton);
+ g_clear_object (&ctx->invocation);
+ g_clear_object (&ctx->self);
+ g_free (ctx);
+}
+
+static void
+set_primary_sim_slot_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ HandleSetPrimarySimSlotContext *ctx)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot_finish (self, res, &error)) {
+ /* If the implementation returns EXISTS, we're already in the requested SIM slot,
+ * so we can safely return a success on the operation and skip the reprobing */
+ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_EXISTS)) {
+ mm_obj_warn (self, "couldn't process primary SIM update request: %s", error->message);
+ g_dbus_method_invocation_take_error (ctx->invocation, g_steal_pointer (&error));
+ handle_set_primary_sim_slot_context_free (ctx);
+ return;
+ }
+ mm_obj_dbg (self, "ignoring SIM update request: %s", error->message);
+ } else {
+ /* Notify about the SIM swap, which will disable and reprobe the device.
+ * There is no need to update the PrimarySimSlot property, as this value will be
+ * reloaded automatically during the reprobe. */
+ mm_base_modem_process_sim_switch (MM_BASE_MODEM (self));
+ }
+
+ mm_gdbus_modem_complete_set_primary_sim_slot (ctx->skeleton, ctx->invocation);
+ handle_set_primary_sim_slot_context_free (ctx);
+}
+
+static void
+handle_set_primary_sim_slot_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleSetPrimarySimSlotContext *ctx)
+{
+ GError *error = NULL;
+ const gchar *const *sim_slot_paths;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_set_primary_sim_slot_context_free (ctx);
+ return;
+ }
+
+ /* If SIM switching is not implemented, report an error */
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot ||
+ !MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot switch sim: "
+ "operation not supported");
+ handle_set_primary_sim_slot_context_free (ctx);
+ return;
+ }
+
+ /* Validate SIM slot number */
+ sim_slot_paths = mm_gdbus_modem_get_sim_slots (ctx->skeleton);
+ if (ctx->requested_sim_slot > g_strv_length ((gchar **)sim_slot_paths)) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot switch sim: requested SIM slot number is out of bounds");
+ handle_set_primary_sim_slot_context_free (ctx);
+ return;
+ }
+
+ MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot (MM_IFACE_MODEM (self),
+ ctx->requested_sim_slot,
+ (GAsyncReadyCallback)set_primary_sim_slot_ready,
+ ctx);
+}
+
+static gboolean
+handle_set_primary_sim_slot (MmGdbusModem *skeleton,
+ GDBusMethodInvocation *invocation,
+ guint sim_slot,
+ MMIfaceModem *self)
+{
+ HandleSetPrimarySimSlotContext *ctx;
+
+ ctx = g_new0 (HandleSetPrimarySimSlotContext, 1);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->requested_sim_slot = sim_slot;
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_set_primary_sim_slot_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
void
mm_iface_modem_update_access_technologies (MMIfaceModem *self,
MMModemAccessTechnology new_access_tech,
@@ -5362,6 +5473,7 @@ interface_initialization_step (GTask *task)
"signal::handle-enable", G_CALLBACK (handle_enable), self,
"signal::handle-set-current-bands", G_CALLBACK (handle_set_current_bands), self,
"signal::handle-set-current-modes", G_CALLBACK (handle_set_current_modes), self,
+ "signal::handle-set-primary-sim-slot", G_CALLBACK (handle_set_primary_sim_slot), self,
NULL);
/* Finally, export the new interface, even if we got errors, but only if not
diff --git a/src/mm-iface-modem.h b/src/mm-iface-modem.h
index 0393155d..c40323fc 100644
--- a/src/mm-iface-modem.h
+++ b/src/mm-iface-modem.h
@@ -354,6 +354,15 @@ struct _MMIfaceModem {
guint *primary_sim_slot,
GError **error);
+ /* Set primary SIM slot */
+ void (* set_primary_sim_slot) (MMIfaceModem *self,
+ guint sim_slot,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* set_primary_sim_slot_finish) (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
/* Create bearer */
void (*create_bearer) (MMIfaceModem *self,
MMBearerProperties *properties,