aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@lanedo.com>2012-12-28 10:23:52 +0100
committerAleksander Morgado <aleksander@lanedo.com>2012-12-28 11:50:27 +0100
commitd1d856994edc3cb61201a78a527d7c33683bf733 (patch)
treea102eb20cbd05f5751e948da7f29f50491142a7d
parentd40ef838986eb62b4c657a981791e8225ce3ce2c (diff)
sierra: cache SIM PIN and re-send it after power-up if neededsierra-pin-after-power-up
When we recover from low-power mode without a modem reset, we may need to re-send the PIN: (ttyUSB6): --> 'AT+CFUN?<CR>' (ttyUSB8): <-- '<CR><LF>OK<CR><LF>' (ttyUSB6): <-- '<CR><LF>+CFUN: 4<CR><LF><CR><LF>OK<CR><LF>' (ttyUSB6): --> 'AT+CFUN=1,0<CR>' (ttyUSB6): <-- '<CR><LF>OK<CR><LF>' (ttyUSB6): --> 'AT+CPIN?<CR>' (ttyUSB6): <-- '<CR><LF>+CPIN: SIM PIN<CR><LF><CR><LF>OK<CR><LF>' (ttyUSB6): --> 'AT+CPIN="1234"<CR>' (ttyUSB6): <-- '<CR><LF>OK<CR><LF>' The cached SIM PIN will be the last SIM PIN used to unlock the modem. If, for any reason, sending the cached PIN fails, we'll end up removing the cache, so that the user doesn't try to enable the modem too many times using a possible wrong PIN. If we get PIN-locked after the power-up and we didn't cache any PIN, which may happen if MM crashes, reboots and the modem was already unlocked (we won't ask for PIN during initialization), then we'll perform a device reset. This situation is really a corner case to be handled nicely.
-rw-r--r--plugins/sierra/mm-common-sierra.c95
-rw-r--r--plugins/sierra/mm-sim-sierra.c97
-rw-r--r--plugins/sierra/mm-sim-sierra.h2
3 files changed, 190 insertions, 4 deletions
diff --git a/plugins/sierra/mm-common-sierra.c b/plugins/sierra/mm-common-sierra.c
index d0bde95c..3bfbf2ca 100644
--- a/plugins/sierra/mm-common-sierra.c
+++ b/plugins/sierra/mm-common-sierra.c
@@ -32,12 +32,99 @@ mm_common_sierra_modem_power_up_finish (MMIfaceModem *self,
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
+static void
+send_pin_ready (MMSim *sim,
+ GAsyncResult *res,
+ GSimpleAsyncResult *simple)
+{
+ GError *error = NULL;
+
+ if (!MM_SIM_GET_CLASS (sim)->send_pin_finish (sim, res, &error)) {
+ mm_warn ("Error sending cached SIM PIN after power-up: '%s'", error->message);
+
+ /* If the error is because PIN wasn't found, we need to fully reset the
+ * modem and re-start the state machine from the beginning, so that we
+ * ask the user for the PIN while in locked state. */
+ if (g_error_matches (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND)) {
+ MMBroadbandModem *self = NULL;
+
+ self = MM_BROADBAND_MODEM (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+ /* Launch modem reset... if possible */
+ if (MM_IFACE_MODEM_GET_INTERFACE (MM_IFACE_MODEM (self))->reset &&
+ MM_IFACE_MODEM_GET_INTERFACE (MM_IFACE_MODEM (self))->reset_finish) {
+ mm_warn ("Launching modem reset...");
+ MM_IFACE_MODEM_GET_INTERFACE (MM_IFACE_MODEM (self))->reset (MM_IFACE_MODEM (self), NULL, NULL);
+ }
+ g_object_unref (self);
+ }
+
+ g_simple_async_result_take_error (simple, error);
+ } else {
+ mm_dbg ("Assuming we got unlocked after power-up");
+ /* Assume we're done */
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ }
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+unlock_check_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GSimpleAsyncResult *simple)
+{
+ MMModemLock lock;
+ GError *error = NULL;
+
+ lock = MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required_finish (self, res, &error);
+ if (error) {
+ mm_dbg ("Couldn't check lock status after power up: '%s'", error->message);
+ g_error_free (error);
+ } else if (lock == MM_MODEM_LOCK_SIM_PIN) {
+ MMSim *sim = NULL;
+
+ /* If we need SIM PIN unlocking, re-send the cached one */
+ g_object_get (self,
+ MM_IFACE_MODEM_SIM, &sim,
+ NULL);
+ if (sim) {
+ mm_dbg ("Sending cached SIM PIN...");
+ /* On the Sierra-specific SIM implementation, we do allow passing a
+ * NULL pin. It just means 'try to use the last cached one'.
+ * NOTE: do not call mm_sim_send_pin(), as that may ignore the errors. */
+ MM_SIM_GET_CLASS (sim)->send_pin (sim,
+ NULL,
+ (GAsyncReadyCallback)send_pin_ready,
+ simple);
+ g_object_unref (sim);
+ return;
+ }
+ mm_dbg ("No SIM found, assuming we won't need re-unlock");
+ } else
+ mm_info ("Modem is locked with '%s' after power-up", mm_modem_lock_get_string (lock));
+
+ /* On error, or READY, or other lock, or no SIM, just go on */
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
static gboolean
-sierra_power_up_wait_cb (GSimpleAsyncResult *result)
+sierra_power_up_wait_cb (GSimpleAsyncResult *simple)
{
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete (result);
- g_object_unref (result);
+ MMBroadbandModem *self;
+
+ self = MM_BROADBAND_MODEM (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+
+ /* Some modems may actually need to unlock the SIM PIN again after powering up...
+ * We'll call the Modem inteface method of the modem directly */
+ MM_IFACE_MODEM_GET_INTERFACE (MM_IFACE_MODEM (self))->load_unlock_required (MM_IFACE_MODEM (self),
+ (GAsyncReadyCallback)unlock_check_ready,
+ simple);
+ g_object_unref (self);
return FALSE;
}
diff --git a/plugins/sierra/mm-sim-sierra.c b/plugins/sierra/mm-sim-sierra.c
index 1b741de6..de5fa09e 100644
--- a/plugins/sierra/mm-sim-sierra.c
+++ b/plugins/sierra/mm-sim-sierra.c
@@ -33,6 +33,82 @@
G_DEFINE_TYPE (MMSimSierra, mm_sim_sierra, MM_TYPE_SIM);
+struct _MMSimSierraPrivate {
+ gchar *cached_pin;
+};
+
+/*****************************************************************************/
+/* Send SIM PIN */
+
+static gboolean
+send_pin_finish (MMSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+parent_send_pin_ready (MMSim *_self,
+ GAsyncResult *res,
+ GSimpleAsyncResult *simple)
+{
+ MMSimSierra *self = MM_SIM_SIERRA (_self);
+ GError *error = NULL;
+
+ if (!MM_SIM_CLASS (mm_sim_sierra_parent_class)->send_pin_finish (_self, res, &error)) {
+ /* Clear cached PIN if sending PIN fails */
+ g_free (self->priv->cached_pin);
+ self->priv->cached_pin = NULL;
+ g_simple_async_result_take_error (simple, error);
+ } else
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+send_pin (MMSim *_self,
+ const gchar *pin,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMSimSierra *self = MM_SIM_SIERRA (_self);
+ GSimpleAsyncResult *result;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ send_pin);
+
+ /* We allow passing NULL pin, when we want to use the last cached one.
+ * This will only happen when re-unlocking the SIM after powering up
+ * the modem, so it means that if any PIN was introduced already,
+ * it would be the correct one. */
+ if (!pin) {
+ if (!self->priv->cached_pin) {
+ g_simple_async_result_set_error (result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No cached PIN found");
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+ return;
+ }
+ } else {
+ /* Update cached PIN */
+ g_free (self->priv->cached_pin);
+ self->priv->cached_pin = g_strdup (pin);
+ }
+
+ /* Call parent's PIN sending code */
+ MM_SIM_CLASS (mm_sim_sierra_parent_class)->send_pin (_self,
+ self->priv->cached_pin,
+ (GAsyncReadyCallback)parent_send_pin_ready,
+ result);
+}
+
/*****************************************************************************/
/* SIM identifier loading */
@@ -179,13 +255,34 @@ mm_sim_sierra_new (MMBaseModem *modem,
static void
mm_sim_sierra_init (MMSimSierra *self)
{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_SIM_SIERRA,
+ MMSimSierraPrivate);
+}
+
+static void
+finalize (GObject *object)
+{
+ MMSimSierra *self = MM_SIM_SIERRA (object);
+
+ g_free (self->priv->cached_pin);
+
+ G_OBJECT_CLASS (mm_sim_sierra_parent_class)->finalize (object);
}
static void
mm_sim_sierra_class_init (MMSimSierraClass *klass)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
MMSimClass *sim_class = MM_SIM_CLASS (klass);
+ g_type_class_add_private (object_class, sizeof (MMSimSierraPrivate));
+
+ object_class->finalize = finalize;
+
sim_class->load_sim_identifier = load_sim_identifier;
sim_class->load_sim_identifier_finish = load_sim_identifier_finish;
+ sim_class->send_pin = send_pin;
+ sim_class->send_pin_finish = send_pin_finish;
}
diff --git a/plugins/sierra/mm-sim-sierra.h b/plugins/sierra/mm-sim-sierra.h
index 3595bd94..d89df35a 100644
--- a/plugins/sierra/mm-sim-sierra.h
+++ b/plugins/sierra/mm-sim-sierra.h
@@ -32,9 +32,11 @@
typedef struct _MMSimSierra MMSimSierra;
typedef struct _MMSimSierraClass MMSimSierraClass;
+typedef struct _MMSimSierraPrivate MMSimSierraPrivate;
struct _MMSimSierra {
MMSim parent;
+ MMSimSierraPrivate *priv;
};
struct _MMSimSierraClass {