diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2020-08-01 09:59:37 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2020-08-28 14:59:06 +0000 |
commit | 924cf1af3c5e1aec1df680d50e4b9ae6dd8e0ba9 (patch) | |
tree | 48c1492b1412ca81b3e0abfc860d55a60a5d47cd | |
parent | 5041b9c99b8587185b629715e3466d31a619abf3 (diff) |
api,modem: new 'SetPrimarySimSlot' method
This new method allows changing the SIM slot considered as primary,
when the modem supports multiple SIM slots.
The generic handling of this method will make sure that the modem
object and all its SIM objects are re-probed from scratch as soon as a
successful SIM slot switch happens.
Implementations may report MM_CORE_ERROR_EXISTS when the switch
doesn't need to happen (e.g. if the requested SIM slot is already the
active one).
-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, |