[PATCH] Core logic to support SIM hot swap

Carlo Lobrano c.lobrano at gmail.com
Thu Jun 30 11:36:45 UTC 2016


BaseModem
  added reprobe property.

MMDevice
  added logic to recreate the modem if it is set invalid and "to reprobe"

MMBroadbandModem
 * added initialization step for SIM hot swap:
    1. keep dedicated ports open to listen to modem's unsolicited
    2. dedicated error management in case of initialization failure due to SIM missing
 * added function to be called in order to act upon SIM insertion/removal:
    1. close dedicated ports
    2. set the modem to be reprobed
    3. disable modem

MMIfaceModem
 * added initialization step for SIM hot swap, if supported by the plugin
 * dedicated error management in case of initialization failure due to SIM missing
---
 src/mm-base-modem.c      |  26 ++++++++++
 src/mm-base-modem.h      |   4 ++
 src/mm-broadband-modem.c | 126 ++++++++++++++++++++++++++++++++++++++++-------
 src/mm-broadband-modem.h |   5 ++
 src/mm-device.c          |  15 ++++++
 src/mm-iface-modem.c     |  41 +++++++++++++++
 src/mm-iface-modem.h     |   9 ++++
 7 files changed, 209 insertions(+), 17 deletions(-)

diff --git a/src/mm-base-modem.c b/src/mm-base-modem.c
index 9cd7c5f..ede014b 100644
--- a/src/mm-base-modem.c
+++ b/src/mm-base-modem.c
@@ -47,6 +47,7 @@ enum {
     PROP_VENDOR_ID,
     PROP_PRODUCT_ID,
     PROP_CONNECTION,
+    PROP_REPROBE,
     PROP_LAST
 };
 
@@ -70,6 +71,7 @@ struct _MMBaseModemPrivate {
 
     gboolean hotplugged;
     gboolean valid;
+    gboolean reprobe;
 
     guint max_timeouts;
 
@@ -392,6 +394,24 @@ mm_base_modem_set_valid (MMBaseModem *self,
     }
 }
 
+void
+mm_base_modem_set_reprobe (MMBaseModem *self,
+                           gboolean reprobe)
+{
+    g_return_if_fail (MM_IS_BASE_MODEM (self));
+
+    self->priv->reprobe = reprobe;
+}
+
+gboolean
+mm_base_modem_get_reprobe (MMBaseModem *self)
+{
+    g_return_val_if_fail (MM_IS_BASE_MODEM (self), FALSE);
+
+    return self->priv->reprobe;
+}
+
+
 gboolean
 mm_base_modem_get_valid (MMBaseModem *self)
 {
@@ -1295,6 +1315,9 @@ set_property (GObject *object,
     case PROP_VALID:
         mm_base_modem_set_valid (self, g_value_get_boolean (value));
         break;
+    case PROP_REPROBE:
+        mm_base_modem_set_reprobe (self, g_value_get_boolean (value));
+        break;
     case PROP_MAX_TIMEOUTS:
         self->priv->max_timeouts = g_value_get_uint (value);
         break;
@@ -1338,6 +1361,9 @@ get_property (GObject *object,
     case PROP_VALID:
         g_value_set_boolean (value, self->priv->valid);
         break;
+    case PROP_REPROBE:
+        g_value_set_boolean (value, self->priv->reprobe);
+        break;
     case PROP_MAX_TIMEOUTS:
         g_value_set_uint (value, self->priv->max_timeouts);
         break;
diff --git a/src/mm-base-modem.h b/src/mm-base-modem.h
index 3c0d16f..c4bcdce 100644
--- a/src/mm-base-modem.h
+++ b/src/mm-base-modem.h
@@ -166,6 +166,10 @@ void     mm_base_modem_set_valid    (MMBaseModem *self,
                                      gboolean valid);
 gboolean mm_base_modem_get_valid    (MMBaseModem *self);
 
+void     mm_base_modem_set_reprobe (MMBaseModem *self,
+                                    gboolean reprobe);
+gboolean mm_base_modem_get_reprobe (MMBaseModem *self);
+
 const gchar  *mm_base_modem_get_device  (MMBaseModem *self);
 const gchar **mm_base_modem_get_drivers (MMBaseModem *self);
 const gchar  *mm_base_modem_get_plugin  (MMBaseModem *self);
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
index e1fd7ca..38fabed 100644
--- a/src/mm-broadband-modem.c
+++ b/src/mm-broadband-modem.c
@@ -124,6 +124,7 @@ typedef struct _PortsContext PortsContext;
 struct _MMBroadbandModemPrivate {
     /* Broadband modem specific implementation */
     PortsContext *enabled_ports_ctx;
+    PortsContext *sim_hot_swap_ports_ctx;
     gboolean modem_init_run;
 
     /*<--- Modem interface --->*/
@@ -8317,6 +8318,11 @@ disabling_stopped (MMBroadbandModem *self,
         ports_context_unref (self->priv->enabled_ports_ctx);
         self->priv->enabled_ports_ctx = NULL;
     }
+
+    if (self->priv->sim_hot_swap_ports_ctx) {
+        ports_context_unref (self->priv->sim_hot_swap_ports_ctx);
+        self->priv->sim_hot_swap_ports_ctx = NULL;
+    }
     return TRUE;
 }
 
@@ -9435,6 +9441,7 @@ typedef enum {
     INITIALIZE_STEP_IFACE_SIGNAL,
     INITIALIZE_STEP_IFACE_OMA,
     INITIALIZE_STEP_IFACE_FIRMWARE,
+    INITIALIZE_STEP_SIM_HOT_SWAP,
     INITIALIZE_STEP_IFACE_SIMPLE,
     INITIALIZE_STEP_LAST,
 } InitializeStep;
@@ -9776,6 +9783,38 @@ initialize_step (InitializeContext *ctx)
                                             ctx);
         return;
 
+    case INITIALIZE_STEP_SIM_HOT_SWAP:
+        {
+            gboolean is_sim_hot_swap_supported = FALSE;
+
+            g_object_get(MM_BASE_MODEM (ctx->self),
+                         IS_SIM_HOT_SWAP_SUPPORTED_PROPERTY,
+                         &is_sim_hot_swap_supported,
+                         NULL);
+
+            if (!is_sim_hot_swap_supported) {
+                ctx->self->priv->sim_hot_swap_ports_ctx = NULL;
+            } else {
+                PortsContext *ports;
+                GError *error = NULL;
+
+                mm_dbg ("Modem supports SIM hot swap. Opening dedicated ports.");
+
+                ports = g_new0 (PortsContext, 1);
+                ports->ref_count = 1;
+
+                if (!open_ports_enabling (ctx->self, ports, FALSE, &error)) {
+                    g_prefix_error (&error, "Couldn't open ports during Modem SIM hot swap enabling: ");
+                    g_simple_async_result_take_error (ctx->result, error);
+                } else {
+                    ctx->self->priv->sim_hot_swap_ports_ctx = ports_context_ref (ports);
+                    ports_context_unref (ports);
+                }
+            }
+        }
+        /* Fall down to next step */
+        ctx->step++;
+
     case INITIALIZE_STEP_IFACE_SIMPLE:
         if (ctx->self->priv->modem_state != MM_MODEM_STATE_FAILED)
             mm_iface_modem_simple_initialize (MM_IFACE_MODEM_SIMPLE (ctx->self));
@@ -9796,23 +9835,45 @@ initialize_step (InitializeContext *ctx)
                                                  "cannot fully initialize");
             } else {
                 /* Fatal SIM, firmware, or modem failure :-( */
-                g_simple_async_result_set_error (ctx->result,
-                                                 MM_CORE_ERROR,
-                                                 MM_CORE_ERROR_WRONG_STATE,
-                                                 "Modem is unusable, "
-                                                 "cannot fully initialize");
-                /* Ensure we only leave the Modem, OMA, and Firmware interfaces
-                 * around.  A failure could be caused by firmware issues, which
-                 * a firmware update, switch, or provisioning could fix.
-                 */
-                mm_iface_modem_3gpp_shutdown (MM_IFACE_MODEM_3GPP (ctx->self));
-                mm_iface_modem_3gpp_ussd_shutdown (MM_IFACE_MODEM_3GPP_USSD (ctx->self));
-                mm_iface_modem_cdma_shutdown (MM_IFACE_MODEM_CDMA (ctx->self));
-                mm_iface_modem_location_shutdown (MM_IFACE_MODEM_LOCATION (ctx->self));
-                mm_iface_modem_messaging_shutdown (MM_IFACE_MODEM_MESSAGING (ctx->self));
-                mm_iface_modem_voice_shutdown (MM_IFACE_MODEM_VOICE (ctx->self));
-                mm_iface_modem_time_shutdown (MM_IFACE_MODEM_TIME (ctx->self));
-                mm_iface_modem_simple_shutdown (MM_IFACE_MODEM_SIMPLE (ctx->self));
+                gboolean is_sim_hot_swap_supported = FALSE;
+                MMModemStateFailedReason reason =
+                    mm_gdbus_modem_get_state_failed_reason (
+                        (MmGdbusModem*)ctx->self->priv->modem_dbus_skeleton);
+
+                g_object_get(MM_BASE_MODEM (ctx->self),
+                             IS_SIM_HOT_SWAP_SUPPORTED_PROPERTY,
+                             &is_sim_hot_swap_supported,
+                             NULL);
+
+                if (reason == MM_MODEM_STATE_FAILED_REASON_SIM_MISSING &&
+                    is_sim_hot_swap_supported) {
+                        mm_info("SIM is missing, but the modem supports SIM hot insertion. Waiting for SIM...");
+                        g_simple_async_result_set_error (ctx->result,
+                                                         MM_CORE_ERROR,
+                                                         MM_CORE_ERROR_WRONG_STATE,
+                                                         "Modem is unusable due to SIM missing, "
+                                                         "cannot fully initialize, "
+                                                         "waiting for SIM insertion.");
+                } else {
+                    mm_dbg ("SIM is missing and Modem does not support SIM Hot Swap");
+                    g_simple_async_result_set_error (ctx->result,
+                                                     MM_CORE_ERROR,
+                                                     MM_CORE_ERROR_WRONG_STATE,
+                                                     "Modem is unusable, "
+                                                     "cannot fully initialize");
+                    /* Ensure we only leave the Modem, OMA, and Firmware interfaces
+                     * around.  A failure could be caused by firmware issues, which
+                     * a firmware update, switch, or provisioning could fix.
+                     */
+                    mm_iface_modem_3gpp_shutdown (MM_IFACE_MODEM_3GPP (ctx->self));
+                    mm_iface_modem_3gpp_ussd_shutdown (MM_IFACE_MODEM_3GPP_USSD (ctx->self));
+                    mm_iface_modem_cdma_shutdown (MM_IFACE_MODEM_CDMA (ctx->self));
+                    mm_iface_modem_location_shutdown (MM_IFACE_MODEM_LOCATION (ctx->self));
+                    mm_iface_modem_messaging_shutdown (MM_IFACE_MODEM_MESSAGING (ctx->self));
+                    mm_iface_modem_voice_shutdown (MM_IFACE_MODEM_VOICE (ctx->self));
+                    mm_iface_modem_time_shutdown (MM_IFACE_MODEM_TIME (ctx->self));
+                    mm_iface_modem_simple_shutdown (MM_IFACE_MODEM_SIMPLE (ctx->self));
+                }
             }
             initialize_context_complete_and_free (ctx);
             return;
@@ -9960,6 +10021,37 @@ mm_broadband_modem_create_device_identifier (MMBroadbandModem *self,
                     MM_GDBUS_MODEM (MM_BROADBAND_MODEM (self)->priv->modem_dbus_skeleton))));
 }
 
+
+/*****************************************************************************/
+static void
+disable_ready (MMBaseModem *self,
+               GAsyncResult *res,
+               gpointer user_data)
+{
+    GError *error = NULL;
+    if (!mm_base_modem_disable_finish (self, res, &error)) {
+        mm_err ("Disable modem error: %s", error->message);
+    } else {
+      mm_base_modem_set_valid (MM_BASE_MODEM (self), FALSE);
+    }
+}
+
+
+void mm_broadband_modem_update_sim_hot_swap_status (MMBroadbandModem *self,
+                                                    gboolean is_sim_missing)
+{
+    if (self->priv->sim_hot_swap_ports_ctx) {
+        mm_dbg ("Releasing SIM hot swap ports context");
+        ports_context_unref (self->priv->sim_hot_swap_ports_ctx);
+        self->priv->sim_hot_swap_ports_ctx = NULL;
+    }
+
+    mm_base_modem_set_reprobe (MM_BASE_MODEM (self), TRUE);
+    mm_base_modem_disable (MM_BASE_MODEM (self),
+                           (GAsyncReadyCallback)disable_ready,
+                           NULL);
+}
+
 /*****************************************************************************/
 
 MMBroadbandModem *
diff --git a/src/mm-broadband-modem.h b/src/mm-broadband-modem.h
index 93ffeb5..ca5bab3 100644
--- a/src/mm-broadband-modem.h
+++ b/src/mm-broadband-modem.h
@@ -127,5 +127,10 @@ gboolean mm_broadband_modem_lock_sms_storages_finish (MMBroadbandModem *self,
 void     mm_broadband_modem_unlock_sms_storages      (MMBroadbandModem *self,
                                                       gboolean mem1,
                                                       gboolean mem2);
+/* Helper to update SIM hot swap */
+void mm_broadband_modem_update_sim_hot_swap_status (MMBroadbandModem *self,
+                                                    gboolean is_sim_missing);
+
+
 
 #endif /* MM_BROADBAND_MODEM_H */
diff --git a/src/mm-device.c b/src/mm-device.c
index 2456cc8..abc8f30 100644
--- a/src/mm-device.c
+++ b/src/mm-device.c
@@ -489,8 +489,23 @@ modem_valid (MMBaseModem *modem,
              MMDevice    *self)
 {
     if (!mm_base_modem_get_valid (modem)) {
+        GDBusObjectManagerServer *object_manager = self->priv->object_manager;
+
         /* Modem no longer valid */
         mm_device_remove_modem (self);
+
+        if (mm_base_modem_get_reprobe (modem)) {
+            GError *error = NULL;
+
+            if (!mm_device_create_modem (self, object_manager, &error)) {
+                 mm_warn ("Could not recreate modem for device at '%s': %s",
+                         mm_device_get_path(self),
+                         error ? error->message : "unknown");
+                g_error_free (error);
+            } else {
+                mm_dbg ("Modem recreated for device '%s'", mm_device_get_path(self));
+            }
+        }
     } else {
         /* Modem now valid, export it, but only if we really have it around.
          * It may happen that the initialization sequence fails because the
diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c
index 516ed78..9aa6df8 100644
--- a/src/mm-iface-modem.c
+++ b/src/mm-iface-modem.c
@@ -3745,6 +3745,7 @@ typedef enum {
     INITIALIZATION_STEP_DEVICE_ID,
     INITIALIZATION_STEP_SUPPORTED_MODES,
     INITIALIZATION_STEP_SUPPORTED_BANDS,
+    INITIALIZATION_STEP_SIM_HOT_SWAP,
     INITIALIZATION_STEP_SUPPORTED_IP_FAMILIES,
     INITIALIZATION_STEP_POWER_STATE,
     INITIALIZATION_STEP_UNLOCK_REQUIRED,
@@ -4203,6 +4204,22 @@ load_current_bands_ready (MMIfaceModem *self,
     interface_initialization_step (ctx);
 }
 
+/*****************************************************************************/
+/* Setup SIM hot swap (Modem interface) */
+static void
+setup_sim_hot_swap_ready (MMIfaceModem *self,
+                          GAsyncResult *res,
+                          gpointer user_data) {
+    GError *error = NULL;
+
+    MM_IFACE_MODEM_GET_INTERFACE (self)->setup_sim_hot_swap_finish (self, res, &error);
+    if (error)
+        mm_warn ("SIM hot swap setup failed: '%s'", error->message);
+    else
+        mm_dbg ("SIM hot swap setup succeded");
+}
+
 static void
 interface_initialization_step (InitializationContext *ctx)
 {
@@ -4544,6 +4561,15 @@ interface_initialization_step (InitializationContext *ctx)
         /* Fall down to next step */
         ctx->step++;
 
+    case INITIALIZATION_STEP_SIM_HOT_SWAP:
+        if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->setup_sim_hot_swap &&
+            MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->setup_sim_hot_swap_finish) {
+            MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->setup_sim_hot_swap (
+                MM_IFACE_MODEM (ctx->self),
+                (GAsyncReadyCallback) setup_sim_hot_swap_ready,
+                NULL);
+        }
+
     case INITIALIZATION_STEP_UNLOCK_REQUIRED:
         /* Only check unlock required if we were previously not unlocked */
         if (mm_gdbus_modem_get_unlock_required (ctx->skeleton) != MM_MODEM_LOCK_NONE) {
@@ -4707,6 +4733,21 @@ interface_initialization_step (InitializationContext *ctx)
                           ctx->self);
 
         if (ctx->fatal_error) {
+            if (g_error_matches (ctx->fatal_error,
+                                 MM_MOBILE_EQUIPMENT_ERROR,
+                                 MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED)) {
+                gboolean is_sim_hot_swap_supported = FALSE;
+
+                g_object_get(MM_BASE_MODEM (ctx->self),
+                             IS_SIM_HOT_SWAP_SUPPORTED_PROPERTY,
+                             &is_sim_hot_swap_supported,
+                             NULL);
+
+                if(is_sim_hot_swap_supported) {
+                    mm_iface_modem_update_failed_state (ctx->self,
+                                                        MM_MODEM_STATE_FAILED_REASON_SIM_MISSING);
+                }
+            }
             g_simple_async_result_take_error (ctx->result, ctx->fatal_error);
             ctx->fatal_error = NULL;
         } else {
diff --git a/src/mm-iface-modem.h b/src/mm-iface-modem.h
index 74ea9f9..eabb0be 100644
--- a/src/mm-iface-modem.h
+++ b/src/mm-iface-modem.h
@@ -36,6 +36,7 @@
 #define MM_IFACE_MODEM_STATE         "iface-modem-state"
 #define MM_IFACE_MODEM_SIM           "iface-modem-sim"
 #define MM_IFACE_MODEM_BEARER_LIST   "iface-modem-bearer-list"
+#define IS_SIM_HOT_SWAP_SUPPORTED_PROPERTY "is-sim-hot-swap-supported"
 
 typedef struct _MMIfaceModem MMIfaceModem;
 
@@ -327,6 +328,14 @@ struct _MMIfaceModem {
     MMBaseBearer * (*create_bearer_finish) (MMIfaceModem *self,
                                             GAsyncResult *res,
                                             GError **error);
+    /* Setup SIM hot swap */
+    void (*setup_sim_hot_swap) (MMIfaceModem *self,
+                                GAsyncReadyCallback callback,
+                                gpointer user_data);
+
+    gboolean (*setup_sim_hot_swap_finish) (MMIfaceModem *self,
+                                            GAsyncResult *res,
+                                            GError **error);
 };
 
 GType mm_iface_modem_get_type (void);
-- 
2.7.4



More information about the ModemManager-devel mailing list