diff options
-rw-r--r-- | cli/mmcli-modem.c | 51 | ||||
-rw-r--r-- | docs/man/mmcli.1 | 6 | ||||
-rw-r--r-- | docs/reference/libmm-glib/libmm-glib-sections.txt | 7 | ||||
-rw-r--r-- | introspection/org.freedesktop.ModemManager1.Modem.xml | 18 | ||||
-rw-r--r-- | libmm-glib/mm-modem.c | 87 | ||||
-rw-r--r-- | libmm-glib/mm-modem.h | 13 | ||||
-rw-r--r-- | src/mm-iface-modem.c | 112 | ||||
-rw-r--r-- | src/mm-iface-modem.h | 9 |
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, |