UPSTREAM: fibocom: setup several +SIM URCs as hotplug event indications

Treat the following URCs as indications of possible SIM hot swap
events:
  +SIM: Inserted
  +SIM: Removed
  +SIM DROP

Any time any of these is received, a SIM hot swap event is reported,
and the logic can then determine what to do. These should lead to
doing nothing if they happen as part of a controlled SIM slot switch.

Signed-off-by: Aleksander Morgado <aleksandermj@google.com>
(cherry picked from commit a24b60e52e390d7a7332f5270ba8e3cfd8d2ba61)

BUG=b:422083198
TEST=Manual tests switching SIM slots

Change-Id: I9e5a586efbd026a024408b86ae97c4f2637cb20a
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/modemmanager-next/+/6672870
Reviewed-by: Ujjwal Pande <ujjwalpande@google.com>
Commit-Queue: Aleksander Morgado <aleksandermj@google.com>
Tested-by: Aleksander Morgado <aleksandermj@google.com>
diff --git a/src/plugins/fibocom/mm-broadband-modem-fibocom.c b/src/plugins/fibocom/mm-broadband-modem-fibocom.c
index 0789188..a94baca 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_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 @@
 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->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 @@
 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 e00086e..3b5f2a7 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))
 
@@ -67,6 +70,15 @@
 }
 
 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)
 {
     iface_modem_firmware_parent = g_type_interface_peek_parent (iface);
@@ -75,6 +87,12 @@
     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 @@
 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 3130cf2..7d81cb2 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_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 @@
 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->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 @@
 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 89be09e..d1398a1 100644
--- a/src/plugins/fibocom/mm-shared-fibocom.c
+++ b/src/plugins/fibocom/mm-shared-fibocom.c
@@ -42,6 +42,8 @@
 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 @@
         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 */
         g_assert (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 @@
 
 /*****************************************************************************/
 
+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 2a1eda0..0d28aa5 100644
--- a/src/plugins/fibocom/mm-shared-fibocom.h
+++ b/src/plugins/fibocom/mm-shared-fibocom.h
@@ -37,6 +37,9 @@
     /* 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 @@
                                                                                   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 ce8bc6a..83c17f4 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->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 @@
     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)
 {