diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c index a7dc8dca..ee11d223 100644 --- a/src/mm-broadband-modem-mbim.c +++ b/src/mm-broadband-modem-mbim.c @@ -191,7 +191,6 @@ struct _MMBroadbandModemMbimPrivate { /* Multi-SIM support */ guint32 executor_index; guint active_slot_index; - gboolean pending_sim_slot_switch_action; MMUnlockRetries *unlock_retries; }; @@ -5511,7 +5510,7 @@ basic_connect_notification_subscriber_ready_status (MMBroadbandModemMbim *self, g_autoptr(GError) error = NULL; gboolean active_sim_event = FALSE; - if (self->priv->pending_sim_slot_switch_action) { + if (mm_iface_modem_is_primary_sim_slot_switch_ongoing (MM_IFACE_MODEM (self))) { mm_obj_dbg (self, "ignoring slot status change"); return; } @@ -6216,7 +6215,7 @@ ms_basic_connect_extensions_notification_slot_info_status (MMBroadbandModemMbim return; } - if (self->priv->pending_sim_slot_switch_action) { + if (mm_iface_modem_is_primary_sim_slot_switch_ongoing (MM_IFACE_MODEM (self))) { mm_obj_dbg (self, "ignoring slot status change in SIM slot %d: %s", slot_index + 1, mbim_uicc_slot_state_get_string (slot_state)); return; } @@ -10009,8 +10008,6 @@ set_device_slot_mappings_ready (MbimDevice *device, self = g_task_get_source_object (task); - g_assert (self->priv->pending_sim_slot_switch_action); - /* the slot index in MM starts at 1 */ slot_number = GPOINTER_TO_UINT (g_task_get_task_data (task)) - 1; @@ -10021,7 +10018,6 @@ set_device_slot_mappings_ready (MbimDevice *device, &map_count, &slot_mappings, &error)) { - self->priv->pending_sim_slot_switch_action = FALSE; g_task_return_error (task, error); g_object_unref (task); return; @@ -10030,13 +10026,9 @@ set_device_slot_mappings_ready (MbimDevice *device, for (i = 0; i < map_count; i++) { if (i == self->priv->executor_index) { if (slot_number != slot_mappings[i]->slot) { - self->priv->pending_sim_slot_switch_action = FALSE; g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "SIM slot switch to '%u' failed", slot_number); } else { - /* Keep pending_sim_slot_switch_action flag TRUE to cleanly ignore SIM related indications - * during slot switching, We don't want SIM related indications received trigger the update - * of SimSlots property, which may not be what we want as the modem object is being shutdown */ self->priv->active_slot_index = slot_number + 1; g_task_return_boolean (task, TRUE); } @@ -10046,7 +10038,6 @@ set_device_slot_mappings_ready (MbimDevice *device, } } - self->priv->pending_sim_slot_switch_action = FALSE; g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Can't find executor index '%u'", self->priv->executor_index); g_object_unref (task); @@ -10098,16 +10089,6 @@ before_set_query_device_slot_mappings_ready (MbimDevice *device, } } - /* Flag a pending SIM slot switch operation, so that we can ignore slot state updates - * during the process. */ - if (self->priv->pending_sim_slot_switch_action) { - g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, - "there is already an ongoing SIM slot switch operation"); - g_object_unref (task); - return; - } - self->priv->pending_sim_slot_switch_action = TRUE; - for (i = 0; i < map_count; i++) { if (i == self->priv->executor_index) slot_mappings[i]->slot = slot_number; diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c index 1af2666b..0e14d77c 100644 --- a/src/mm-iface-modem.c +++ b/src/mm-iface-modem.c @@ -92,6 +92,10 @@ typedef struct { /* Timer that tracks when the last power operation request was * performed, so that we can throttle the requests to the modem. */ GTimer *power_state_timer; + + /* Flag indicating whether a primary SIM slot switch operation is + * ongoing */ + gboolean ongoing_primary_sim_slot_switch; } Private; static void @@ -350,10 +354,10 @@ after_sim_event_disable_ready (MMBaseModem *self, mm_base_modem_set_valid (self, FALSE); } -void -mm_iface_modem_process_sim_event (MMIfaceModem *self) +static void +iface_modem_process_sim_event_internal (MMIfaceModem *self) { - mm_obj_info (self, "Processing SIM event"); + mm_obj_info (self, "processing SIM event"); if (MM_IFACE_MODEM_GET_IFACE (self)->cleanup_sim_hot_swap) MM_IFACE_MODEM_GET_IFACE (self)->cleanup_sim_hot_swap (self); @@ -369,6 +373,19 @@ mm_iface_modem_process_sim_event (MMIfaceModem *self) NULL); } +void +mm_iface_modem_process_sim_event (MMIfaceModem *self) +{ + Private *priv; + + priv = get_private (self); + if (priv->ongoing_primary_sim_slot_switch) { + mm_obj_info (self, "ignoring SIM event during a SIM slot switch"); + return; + } + iface_modem_process_sim_event_internal (self); +} + /*****************************************************************************/ void @@ -1351,6 +1368,17 @@ handle_list_bearers (MmGdbusModem *skeleton, /*****************************************************************************/ +gboolean +mm_iface_modem_is_primary_sim_slot_switch_ongoing (MMIfaceModem *self) +{ + Private *priv; + + priv = get_private (self); + return priv->ongoing_primary_sim_slot_switch; +} + +/*****************************************************************************/ + typedef struct { MmGdbusModem *skeleton; GDBusMethodInvocation *invocation; @@ -1372,24 +1400,34 @@ set_primary_sim_slot_ready (MMIfaceModem *self, GAsyncResult *res, HandleSetPrimarySimSlotContext *ctx) { - g_autoptr(GError) error = NULL; + g_autoptr(GError) error = NULL; + Private *priv; + + priv = get_private (self); + g_assert (priv->ongoing_primary_sim_slot_switch); if (!MM_IFACE_MODEM_GET_IFACE (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, "failed setting primary SIM slot '%u': %s", ctx->requested_sim_slot, error->message); + priv->ongoing_primary_sim_slot_switch = FALSE; mm_dbus_method_invocation_take_error (ctx->invocation, g_steal_pointer (&error)); handle_set_primary_sim_slot_context_free (ctx); return; } mm_obj_dbg (self, "ignored request to set primary SIM slot '%u': already set", ctx->requested_sim_slot); + priv->ongoing_primary_sim_slot_switch = FALSE; } 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_obj_info (self, "primary SIM slot '%u' set", ctx->requested_sim_slot); - mm_iface_modem_process_sim_event (self); + /* Process SIM event without checking the ongoing_primary_sim_slot_switch flag, as + * we are the ones who set it in this operation. This flag is kept enabled for as + * long as the modem object exists! Once the reprobe happens, a new modem object + * will be created. */ + iface_modem_process_sim_event_internal (self); } mm_gdbus_modem_complete_set_primary_sim_slot (ctx->skeleton, ctx->invocation); @@ -1401,6 +1439,7 @@ handle_set_primary_sim_slot_auth_ready (MMIfaceAuth *self, GAsyncResult *res, HandleSetPrimarySimSlotContext *ctx) { + Private *priv; GError *error = NULL; const gchar *const *sim_slot_paths; @@ -1428,7 +1467,16 @@ handle_set_primary_sim_slot_auth_ready (MMIfaceAuth *self, return; } + priv = get_private (MM_IFACE_MODEM (self)); + if (priv->ongoing_primary_sim_slot_switch) { + mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, + "SIM slot switch operation already ongoing."); + handle_set_primary_sim_slot_context_free (ctx); + return; + } + mm_obj_info (self, "processing user request to set primary SIM slot '%u'...", ctx->requested_sim_slot); + priv->ongoing_primary_sim_slot_switch = TRUE; MM_IFACE_MODEM_GET_IFACE (self)->set_primary_sim_slot (MM_IFACE_MODEM (self), ctx->requested_sim_slot, (GAsyncReadyCallback)set_primary_sim_slot_ready, diff --git a/src/mm-iface-modem.h b/src/mm-iface-modem.h index 18c7e9ea..4bd6ac19 100644 --- a/src/mm-iface-modem.h +++ b/src/mm-iface-modem.h @@ -620,4 +620,6 @@ void mm_iface_modem_modify_sim (MMIfaceModem *self, void mm_iface_modem_process_sim_event (MMIfaceModem *self); +gboolean mm_iface_modem_is_primary_sim_slot_switch_ongoing (MMIfaceModem *self); + #endif /* MM_IFACE_MODEM_H */ diff --git a/src/plugins/fibocom/mm-broadband-modem-fibocom.c b/src/plugins/fibocom/mm-broadband-modem-fibocom.c index 0789188e..a94bacad 100644 --- a/src/plugins/fibocom/mm-broadband-modem-fibocom.c +++ b/src/plugins/fibocom/mm-broadband-modem-fibocom.c @@ -27,6 +27,7 @@ static void iface_modem_init (MMIfaceModemInterface *iface); static void iface_modem_firmware_init (MMIfaceModemFirmwareInterface *iface); static void shared_fibocom_init (MMSharedFibocomInterface *iface); +static MMIfaceModemInterface *iface_modem_parent; static MMIfaceModemFirmwareInterface *iface_modem_firmware_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemFibocom, mm_broadband_modem_fibocom, MM_TYPE_BROADBAND_MODEM, 0, @@ -254,6 +255,10 @@ mm_broadband_modem_fibocom_init (MMBroadbandModemFibocom *self) static void iface_modem_init (MMIfaceModemInterface *iface) { + iface_modem_parent = g_type_interface_peek_parent (iface); + + iface->setup_sim_hot_swap = mm_shared_fibocom_setup_sim_hot_swap; + iface->setup_sim_hot_swap_finish = mm_shared_fibocom_setup_sim_hot_swap_finish; iface->create_bearer = modem_create_bearer; iface->create_bearer_finish = modem_create_bearer_finish; iface->reset = modem_reset; @@ -273,6 +278,12 @@ iface_modem_firmware_init (MMIfaceModemFirmwareInterface *iface) iface->load_update_settings_finish = mm_shared_fibocom_firmware_load_update_settings_finish; } +static MMIfaceModemInterface * +peek_parent_modem_interface (MMSharedFibocom *self) +{ + return iface_modem_parent; +} + static MMIfaceModemFirmwareInterface * peek_parent_firmware_interface (MMSharedFibocom *self) { @@ -288,8 +299,9 @@ peek_parent_class (MMSharedFibocom *self) static void shared_fibocom_init (MMSharedFibocomInterface *iface) { - iface->peek_parent_class = peek_parent_class; + iface->peek_parent_modem_interface = peek_parent_modem_interface; iface->peek_parent_firmware_interface = peek_parent_firmware_interface; + iface->peek_parent_class = peek_parent_class; } static void diff --git a/src/plugins/fibocom/mm-broadband-modem-mbim-fibocom.c b/src/plugins/fibocom/mm-broadband-modem-mbim-fibocom.c index e00086e3..3b5f2a71 100644 --- a/src/plugins/fibocom/mm-broadband-modem-mbim-fibocom.c +++ b/src/plugins/fibocom/mm-broadband-modem-mbim-fibocom.c @@ -27,12 +27,15 @@ #include "mm-broadband-modem-mbim-fibocom.h" #include "mm-shared-fibocom.h" -static void shared_fibocom_init (MMSharedFibocomInterface *iface); +static void iface_modem_init (MMIfaceModemInterface *iface); static void iface_modem_firmware_init (MMIfaceModemFirmwareInterface *iface); +static void shared_fibocom_init (MMSharedFibocomInterface *iface); +static MMIfaceModemInterface *iface_modem_parent; static MMIfaceModemFirmwareInterface *iface_modem_firmware_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimFibocom, mm_broadband_modem_mbim_fibocom, MM_TYPE_BROADBAND_MODEM_MBIM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_FIBOCOM, shared_fibocom_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init)) @@ -66,6 +69,15 @@ mm_broadband_modem_mbim_fibocom_init (MMBroadbandModemMbimFibocom *self) { } +static void +iface_modem_init (MMIfaceModemInterface *iface) +{ + iface_modem_parent = g_type_interface_peek_parent (iface); + + iface->setup_sim_hot_swap = mm_shared_fibocom_setup_sim_hot_swap; + iface->setup_sim_hot_swap_finish = mm_shared_fibocom_setup_sim_hot_swap_finish; +} + static void iface_modem_firmware_init (MMIfaceModemFirmwareInterface *iface) { @@ -75,6 +87,12 @@ iface_modem_firmware_init (MMIfaceModemFirmwareInterface *iface) iface->load_update_settings_finish = mm_shared_fibocom_firmware_load_update_settings_finish; } +static MMIfaceModemInterface * +peek_parent_modem_interface (MMSharedFibocom *self) +{ + return iface_modem_parent; +} + static MMIfaceModemFirmwareInterface * peek_parent_firmware_interface (MMSharedFibocom *self) { @@ -90,8 +108,9 @@ peek_parent_class (MMSharedFibocom *self) static void shared_fibocom_init (MMSharedFibocomInterface *iface) { - iface->peek_parent_class = peek_parent_class; + iface->peek_parent_modem_interface = peek_parent_modem_interface; iface->peek_parent_firmware_interface = peek_parent_firmware_interface; + iface->peek_parent_class = peek_parent_class; } static void diff --git a/src/plugins/fibocom/mm-broadband-modem-mbim-xmm-fibocom.c b/src/plugins/fibocom/mm-broadband-modem-mbim-xmm-fibocom.c index 3130cf2e..7d81cb25 100644 --- a/src/plugins/fibocom/mm-broadband-modem-mbim-xmm-fibocom.c +++ b/src/plugins/fibocom/mm-broadband-modem-mbim-xmm-fibocom.c @@ -31,7 +31,7 @@ static void iface_modem_init (MMIfaceModemInterface *iface); static void iface_modem_firmware_init (MMIfaceModemFirmwareInterface *iface); static void shared_fibocom_init (MMSharedFibocomInterface *iface); -static MMIfaceModemInterface *iface_modem_parent; +static MMIfaceModemInterface *iface_modem_parent; static MMIfaceModemFirmwareInterface *iface_modem_firmware_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimXmmFibocom, mm_broadband_modem_mbim_xmm_fibocom, MM_TYPE_BROADBAND_MODEM_MBIM_XMM, 0, @@ -212,6 +212,7 @@ static void iface_modem_init (MMIfaceModemInterface *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); + iface->load_revision = load_revision; iface->load_revision_finish = load_revision_finish; } @@ -225,6 +226,12 @@ iface_modem_firmware_init (MMIfaceModemFirmwareInterface *iface) iface->load_update_settings_finish = mm_shared_fibocom_firmware_load_update_settings_finish; } +static MMIfaceModemInterface * +peek_parent_modem_interface (MMSharedFibocom *self) +{ + return iface_modem_parent; +} + static MMIfaceModemFirmwareInterface * peek_parent_firmware_interface (MMSharedFibocom *self) { @@ -240,8 +247,9 @@ peek_parent_class (MMSharedFibocom *self) static void shared_fibocom_init (MMSharedFibocomInterface *iface) { - iface->peek_parent_class = peek_parent_class; + iface->peek_parent_modem_interface = peek_parent_modem_interface; iface->peek_parent_firmware_interface = peek_parent_firmware_interface; + iface->peek_parent_class = peek_parent_class; } static void diff --git a/src/plugins/fibocom/mm-shared-fibocom.c b/src/plugins/fibocom/mm-shared-fibocom.c index 0fbc9b59..1f9559c4 100644 --- a/src/plugins/fibocom/mm-shared-fibocom.c +++ b/src/plugins/fibocom/mm-shared-fibocom.c @@ -42,6 +42,8 @@ static GQuark private_quark; typedef struct { /* Parent class */ MMBaseModemClass *class_parent; + /* Modem interface of parent class */ + MMIfaceModemInterface *iface_modem_parent; /* Firmware interface of parent class */ MMIfaceModemFirmwareInterface *iface_modem_firmware_parent; /* URCs to ignore */ @@ -74,6 +76,10 @@ get_private (MMSharedFibocom *self) g_assert (MM_SHARED_FIBOCOM_GET_IFACE (self)->peek_parent_class); priv->class_parent = MM_SHARED_FIBOCOM_GET_IFACE (self)->peek_parent_class (self); + /* Setup modem interface of parent class */ + if (MM_SHARED_FIBOCOM_GET_IFACE (self)->peek_parent_modem_interface) + priv->iface_modem_parent = MM_SHARED_FIBOCOM_GET_IFACE (self)->peek_parent_modem_interface (self); + /* Setup firmware interface of parent class */ if (MM_SHARED_FIBOCOM_GET_IFACE (self)->peek_parent_firmware_interface) priv->iface_modem_firmware_parent = MM_SHARED_FIBOCOM_GET_IFACE (self)->peek_parent_firmware_interface (self); @@ -86,6 +92,96 @@ get_private (MMSharedFibocom *self) /*****************************************************************************/ +static void +sim_hotswap_unsolicited_handler (MMPortSerialAt *port, + GMatchInfo *match_info, + MMIfaceModem *self) +{ + mm_obj_dbg (self, "processing +SIM URC reporting a SIM hotswap event"); + mm_iface_modem_process_sim_event (MM_IFACE_MODEM (self)); +} + +/*****************************************************************************/ +/* Setup SIM hot swap context (Modem interface) */ + +gboolean +mm_shared_fibocom_setup_sim_hot_swap_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +parent_setup_sim_hot_swap_ready (MMIfaceModem *self, + GAsyncResult *res, + GTask *task) +{ + Private *priv; + g_autoptr(GError) error = NULL; + + priv = get_private (MM_SHARED_FIBOCOM (self)); + + if (!priv->iface_modem_parent->setup_sim_hot_swap_finish (self, res, &error)) + mm_obj_dbg (self, "additional SIM hot swap detection setup failed: %s", error->message); + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +void +mm_shared_fibocom_setup_sim_hot_swap (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + Private *priv; + MMPortSerialAt *ports[2]; + GTask *task; + guint i; + g_autoptr(GRegex) pattern = NULL; + g_autoptr(GError) error = NULL; + + priv = get_private (MM_SHARED_FIBOCOM (self)); + + task = g_task_new (self, NULL, callback, user_data); + + ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); + ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); + + pattern = g_regex_new ("(\\+SIM: Inserted)|(\\+SIM: Removed)|(\\+SIM DROP)\\r\\n", G_REGEX_RAW, 0, NULL); + g_assert (pattern); + + for (i = 0; i < G_N_ELEMENTS (ports); i++) { + if (ports[i]) + mm_port_serial_at_add_unsolicited_msg_handler ( + ports[i], + pattern, + (MMPortSerialAtUnsolicitedMsgFn)sim_hotswap_unsolicited_handler, + self, + NULL); + } + + mm_obj_dbg (self, "+SIM based hotswap detection set up"); + + if (!mm_broadband_modem_sim_hot_swap_ports_context_init (MM_BROADBAND_MODEM (self), &error)) + mm_obj_warn (self, "failed to initialize SIM hot swap ports context: %s", error->message); + + /* Now, if available, setup parent logic */ + if (priv->iface_modem_parent->setup_sim_hot_swap && + priv->iface_modem_parent->setup_sim_hot_swap_finish) { + priv->iface_modem_parent->setup_sim_hot_swap (self, + (GAsyncReadyCallback) parent_setup_sim_hot_swap_ready, + task); + return; + } + + /* Otherwise, we're done */ + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +/*****************************************************************************/ + #if defined WITH_MBIM MMPort * diff --git a/src/plugins/fibocom/mm-shared-fibocom.h b/src/plugins/fibocom/mm-shared-fibocom.h index 2a1eda05..0d28aa52 100644 --- a/src/plugins/fibocom/mm-shared-fibocom.h +++ b/src/plugins/fibocom/mm-shared-fibocom.h @@ -37,6 +37,9 @@ struct _MMSharedFibocomInterface { /* Peek parent class of the object */ MMBaseModemClass * (* peek_parent_class) (MMSharedFibocom *self); + /* Peek modem interface of the parent class of the object */ + MMIfaceModemInterface * (* peek_parent_modem_interface) (MMSharedFibocom *self); + /* Peek firmware interface of the parent class of the object */ MMIfaceModemFirmwareInterface * (* peek_parent_firmware_interface) (MMSharedFibocom *self); }; @@ -61,4 +64,11 @@ MMFirmwareUpdateSettings *mm_shared_fibocom_firmware_load_update_settings_finish GAsyncResult *res, GError **error); +void mm_shared_fibocom_setup_sim_hot_swap (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_shared_fibocom_setup_sim_hot_swap_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error); + #endif /* MM_SHARED_FIBOCOM_H */ diff --git a/src/plugins/mtk/mm-broadband-modem-mbim-mtk-fibocom.c b/src/plugins/mtk/mm-broadband-modem-mbim-mtk-fibocom.c index ce8bc6a3..83c17f44 100644 --- a/src/plugins/mtk/mm-broadband-modem-mbim-mtk-fibocom.c +++ b/src/plugins/mtk/mm-broadband-modem-mbim-mtk-fibocom.c @@ -426,7 +426,6 @@ iface_modem_init (MMIfaceModemInterface *iface) iface->create_bearer_list = create_bearer_list; iface->load_unlock_retries = mm_shared_mtk_load_unlock_retries; iface->load_unlock_retries_finish = mm_shared_mtk_load_unlock_retries_finish; - } static void @@ -444,6 +443,8 @@ peek_parent_class (MMSharedFibocom *self) return MM_BASE_MODEM_CLASS (mm_broadband_modem_mbim_mtk_fibocom_parent_class); } +/* Note: shared fibocom is only used to create usbmisc/wwan ports, so there is no need + * to initialize the parent modem/firmware interface objects. */ static void shared_fibocom_init (MMSharedFibocomInterface *iface) {