[PATCH] add a method and a property to find out remaining unlock attempts



Hi!

Attached is a patch that adds an GetUnlockRetries to the gsm-card dbus interface 
to find out how many retries that remains for a given lock type (sim-pin, sim-puk etc). 
The corresponding function is added to the gsm-card interface and implemented by the mbm-plugin.

A modem property UnlockRetries is also added which is set if the property UnlockRequired is set and 
it shows the remaining unlock attempts for the currently locked pin type.

I'm attaching the patch as a file as well as inline since I'm having some trouble with corrupt inline patches when sending via exchange. 

Regards
Torgny Johansson

---

diff --git a/introspection/mm-modem-gsm-card.xml b/introspection/mm-modem-gsm-card.xml
index 708d5c9..ad6d833 100644
--- a/introspection/mm-modem-gsm-card.xml
+++ b/introspection/mm-modem-gsm-card.xml
@@ -28,6 +28,24 @@
       </arg>
     </method>
 
+	<method name="GetUnlockRetries">
+      <tp:docstring>
+	Get the remaining PIN (or PUK) attempts.
+      </tp:docstring>
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_get_unlock_retries"/>
+      <arg name="pin_type" type="s" direction="in">
+	<tp:docstring>
+	  The type of PIN.
+	</tp:docstring>
+      </arg>
+	  <arg name="unlock_retries" type="u" direction="out">
+		<tp:docstring>
+	  The number of remaining attempts.
+		</tp:docstring>
+      </arg>
+    </method>
+
     <method name="SendPuk">
       <tp:docstring>
 	Send the PUK and a new PIN to unlock the SIM card.
diff --git a/introspection/mm-modem.xml b/introspection/mm-modem.xml
index da7635b..09512f8 100644
--- a/introspection/mm-modem.xml
+++ b/introspection/mm-modem.xml
@@ -125,6 +125,12 @@
       </tp:docstring>
     </property>
 
+	<property name="UnlockRetries" type="u" access="read">
+      <tp:docstring>
+        The number of unlock retries for the unlock code given by the property UnlockRequired.
+      </tp:docstring>
+    </property>
+
     <property name="IpMethod" type="u" access="read" tp:type="MM_MODEM_IP_METHOD">
       <tp:docstring>
         The IP configuration method.
diff --git a/plugins/mm-modem-mbm.c b/plugins/mm-modem-mbm.c
index 10be0be..7c7b251 100644
--- a/plugins/mm-modem-mbm.c
+++ b/plugins/mm-modem-mbm.c
@@ -28,17 +28,20 @@
 
 #include "mm-modem-mbm.h"
 #include "mm-modem-simple.h"
+#include "mm-modem-gsm-card.h"
 #include "mm-errors.h"
 #include "mm-callback-info.h"
 
 static void modem_init (MMModem *modem_class);
 static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class);
 static void modem_simple_init (MMModemSimple *class);
+static void modem_gsm_card_init (MMModemGsmCard *class);
 
 G_DEFINE_TYPE_EXTENDED (MMModemMbm, mm_modem_mbm, MM_TYPE_GENERIC_GSM, 0,
                         G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)
                         G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init)
-                        G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init))
+                        G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init)
+                        G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init))
 
 #define MM_MODEM_MBM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_MBM, MMModemMbmPrivate))
 
@@ -736,6 +739,89 @@ mbm_modem_authenticate (MMModemMbm *self,
 
 /*****************************************************************************/
 
+static void
+send_epin_done (MMAtSerialPort *port,
+           GString *response,
+           GError *error,
+           gpointer user_data)
+{
+    MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+    char *pin_type;
+    int attempts_left = 0;
+
+    if (error) {
+        info->error = g_error_copy (error);
+        mm_callback_info_schedule (info);
+        return;
+    }
+
+    pin_type = g_strdup ((char *)mm_callback_info_get_data (info, "pin_type"));
+
+    if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN))
+        sscanf (response->str, "*EPIN: %d", &attempts_left);
+    else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PUK)) 
+        sscanf (response->str, "*EPIN: %*d, %d", &attempts_left);
+    else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN2)) 
+        sscanf (response->str, "*EPIN: %*d, %*d, %d", &attempts_left);
+    else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PUK2)) 
+        sscanf (response->str, "*EPIN: %*d, %*d, %*d, %d", &attempts_left);
+    else {
+        g_debug ("%s unknown pin type: %s", __FUNCTION__, pin_type);
+        
+        info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Unknown PIN type");
+    }
+
+    mm_callback_info_set_result (info, GUINT_TO_POINTER (attempts_left), NULL);
+
+    mm_callback_info_schedule (info);
+    
+    g_free (pin_type);
+}
+
+static void
+mbm_get_unlock_retries (MMModemGsmCard *modem,
+              const char *pin_type,
+              MMModemUIntFn callback,
+              gpointer user_data)
+{
+    MMAtSerialPort *port;
+    char *command;
+    MMCallbackInfo *info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
+
+    g_debug ("%s pin type: %s", __FUNCTION__, pin_type);
+
+    /* Ensure we have a usable port to use for the command */
+    port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+    if (!port) {
+        mm_callback_info_schedule (info);
+        return;
+    }
+
+    /* Modem may not be enabled yet, which sometimes can't be done until
+     * the device has been unlocked.  In this case we have to open the port
+     * ourselves.
+     */
+    if (!mm_serial_port_open (MM_SERIAL_PORT (port), &info->error)) {
+        mm_callback_info_schedule (info);
+        return;
+    }
+
+    /* if the modem have not yet been enabled we need to make sure echoing is turned off */
+    command = g_strdup_printf ("E0");
+    mm_at_serial_port_queue_command (port, command, 3, NULL, NULL);
+    g_free (command);
+
+    mm_callback_info_set_data (info, "pin_type", g_strdup (pin_type), g_free);
+
+    command = g_strdup_printf ("*EPIN?");
+
+    mm_at_serial_port_queue_command (port, command, 3, send_epin_done, info);
+
+    g_free (command);
+}
+
+/*****************************************************************************/
+
 static gboolean
 grab_port (MMModem *modem,
            const char *subsys,
@@ -805,6 +891,12 @@ grab_port (MMModem *modem,
 /*****************************************************************************/
 
 static void
+modem_gsm_card_init (MMModemGsmCard *class)
+{
+    class->get_unlock_retries = mbm_get_unlock_retries;
+}
+
+static void
 modem_gsm_network_init (MMModemGsmNetwork *class)
 {
     class->do_register = do_register;
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
index 6b54442..e028a93 100644
--- a/src/mm-generic-gsm.c
+++ b/src/mm-generic-gsm.c
@@ -135,6 +135,12 @@ static void reg_info_updated (MMGenericGsm *self,
                               gboolean update_name,
                               const char *oper_name);
 
+static void
+get_unlock_retries_cb (MMModem *modem,
+                       guint32 result,
+                       GError *error,
+                       gpointer user_data);
+
 MMModem *
 mm_generic_gsm_new (const char *device,
                     const char *driver,
@@ -221,6 +227,7 @@ pin_check_done (MMAtSerialPort *port,
 
         if (g_str_has_prefix (str, "READY")) {
             mm_modem_base_set_unlock_required (MM_MODEM_BASE (info->modem), NULL);
+            mm_modem_base_set_unlock_retries (MM_MODEM_BASE (info->modem), 0);
             parsed = TRUE;
         } else {
             CPinResult *iter = &unlock_results[0];
@@ -230,6 +237,10 @@ pin_check_done (MMAtSerialPort *port,
                 if (g_str_has_prefix (str, iter->result)) {
                     info->error = mm_mobile_error_for_code (iter->code);
                     mm_modem_base_set_unlock_required (MM_MODEM_BASE (info->modem), iter->normalized);
+                    mm_modem_gsm_card_get_unlock_retries (MM_MODEM_GSM_CARD (info->modem),
+                                                        iter->normalized,
+                                                        get_unlock_retries_cb,
+                                                        NULL);
                     parsed = TRUE;
                     break;
                 }
@@ -241,6 +252,7 @@ pin_check_done (MMAtSerialPort *port,
     if (!parsed) {
         /* Assume unlocked if we don't recognize the pin request result */
         mm_modem_base_set_unlock_required (MM_MODEM_BASE (info->modem), NULL);
+        mm_modem_base_set_unlock_retries (MM_MODEM_BASE (info->modem), 0);
 
         if (!info->error) {
             info->error = g_error_new (MM_MODEM_ERROR,
@@ -268,6 +280,18 @@ check_pin (MMGenericGsm *modem,
     mm_at_serial_port_queue_command (priv->primary, "+CPIN?", 3, pin_check_done, info);
 }
 
+static void
+get_unlock_retries_cb (MMModem *modem,
+                                  guint32 result,
+                                  GError *error,
+                                  gpointer user_data)
+{
+    if (!error)
+        mm_modem_base_set_unlock_retries (MM_MODEM_BASE (modem), result);
+    else
+        mm_modem_base_set_unlock_retries (MM_MODEM_BASE (modem), 0);
+}
+
 /*****************************************************************************/
 
 void
@@ -1275,9 +1299,6 @@ send_pin_done (MMAtSerialPort *port,
 
     if (error) {
         info->error = g_error_copy (error);
-        mm_callback_info_schedule (info);
-        mm_serial_port_close (MM_SERIAL_PORT (port));
-        return;
     }
 
     /* Get latest PIN status */
diff --git a/src/mm-modem-base.c b/src/mm-modem-base.c
index 0a91d3f..611b1a1 100644
--- a/src/mm-modem-base.c
+++ b/src/mm-modem-base.c
@@ -43,6 +43,7 @@ typedef struct {
     char *plugin;
     char *device;
     char *unlock_required;
+    guint32 unlock_retries;
     guint32 ip_method;
     gboolean valid;
     MMModemState state;
@@ -232,6 +233,40 @@ mm_modem_base_set_unlock_required (MMModemBase *self, const char *unlock_require
     g_object_notify (G_OBJECT (self), MM_MODEM_UNLOCK_REQUIRED);
 }
 
+guint32
+mm_modem_base_get_unlock_retries (MMModemBase *self)
+{   
+    g_return_val_if_fail (self != NULL, 0);
+    g_return_val_if_fail (MM_IS_MODEM_BASE (self), 0);
+
+    return MM_MODEM_BASE_GET_PRIVATE (self)->unlock_retries;
+}
+
+void
+mm_modem_base_set_unlock_retries (MMModemBase *self, guint unlock_retries)
+{
+    MMModemBasePrivate *priv;
+    const char *dbus_path;
+
+    g_return_if_fail (self != NULL);
+    g_return_if_fail (MM_IS_MODEM_BASE (self));
+
+    priv = MM_MODEM_BASE_GET_PRIVATE (self);
+
+    /* Only do something if the value changes */
+    if (priv->unlock_retries == unlock_retries)
+        return;
+
+    priv->unlock_retries = unlock_retries;
+
+    dbus_path = (const char *) g_object_get_data (G_OBJECT (self), DBUS_PATH_TAG);
+    if (dbus_path) {
+        g_message ("Modem %s: unlock retries is %d", dbus_path, priv->unlock_retries);
+    }
+
+    g_object_notify (G_OBJECT (self), MM_MODEM_UNLOCK_RETRIES);
+}
+
 const char *
 mm_modem_base_get_manf (MMModemBase *self)
 {
@@ -490,6 +525,9 @@ mm_modem_base_init (MMModemBase *self)
     mm_properties_changed_signal_register_property (G_OBJECT (self),
                                                     MM_MODEM_UNLOCK_REQUIRED,
                                                     MM_MODEM_DBUS_INTERFACE);
+    mm_properties_changed_signal_register_property (G_OBJECT (self),
+                                                    MM_MODEM_UNLOCK_RETRIES,
+                                                    MM_MODEM_DBUS_INTERFACE);
 }
 
 static void
@@ -539,6 +577,7 @@ set_property (GObject *object, guint prop_id,
     case MM_MODEM_PROP_TYPE:
     case MM_MODEM_PROP_ENABLED:
     case MM_MODEM_PROP_UNLOCK_REQUIRED:
+    case MM_MODEM_PROP_UNLOCK_RETRIES:
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -583,6 +622,9 @@ get_property (GObject *object, guint prop_id,
     case MM_MODEM_PROP_UNLOCK_REQUIRED:
         g_value_set_string (value, priv->unlock_required);
         break;
+    case MM_MODEM_PROP_UNLOCK_RETRIES:
+        g_value_set_uint (value, priv->unlock_retries);
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -658,6 +700,10 @@ mm_modem_base_class_init (MMModemBaseClass *klass)
                                       MM_MODEM_PROP_UNLOCK_REQUIRED,
                                       MM_MODEM_UNLOCK_REQUIRED);
 
+    g_object_class_override_property (object_class,
+                                      MM_MODEM_PROP_UNLOCK_RETRIES,
+                                      MM_MODEM_UNLOCK_RETRIES);
+
     mm_properties_changed_signal_new (object_class);
 }
 
diff --git a/src/mm-modem-base.h b/src/mm-modem-base.h
index 516af2e..8eec0e4 100644
--- a/src/mm-modem-base.h
+++ b/src/mm-modem-base.h
@@ -67,6 +67,12 @@ const char *mm_modem_base_get_unlock_required (MMModemBase *self);
 void mm_modem_base_set_unlock_required (MMModemBase *self,
                                         const char *unlock_required);
 
+guint mm_modem_base_get_unlock_retries (MMModemBase *self);
+
+void mm_modem_base_set_unlock_retries (MMModemBase *self,
+                                        guint unlock_retries);
+
+
 const char *mm_modem_base_get_manf (MMModemBase *self);
 void        mm_modem_base_set_manf (MMModemBase *self, const char *manf);
 
diff --git a/src/mm-modem-gsm-card.c b/src/mm-modem-gsm-card.c
index 432a4a3..f5b99f8 100644
--- a/src/mm-modem-gsm-card.c
+++ b/src/mm-modem-gsm-card.c
@@ -28,6 +28,10 @@ static void impl_gsm_modem_get_imei (MMModemGsmCard *modem,
 static void impl_gsm_modem_get_imsi (MMModemGsmCard *modem,
                                      DBusGMethodInvocation *context);
 
+static void impl_gsm_modem_get_unlock_retries (MMModemGsmCard *modem,
+                                             const char *pin_type,
+                                             DBusGMethodInvocation *context);
+
 static void impl_gsm_modem_send_pin (MMModemGsmCard *modem,
                                      const char *pin,
                                      DBusGMethodInvocation *context);
@@ -77,6 +81,30 @@ str_call_not_supported (MMModemGsmCard *self,
 }
 
 static void
+uint_call_done (MMModem *modem, guint32 result, GError *error, gpointer user_data)
+{
+    DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+    if (error)
+        dbus_g_method_return_error (context, error);
+    else
+        dbus_g_method_return (context, result);
+}
+
+static void
+uint_call_not_supported (MMModemGsmCard *self,
+                         MMModemUIntFn callback,
+                         gpointer user_data)
+{
+    MMCallbackInfo *info;
+
+    info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data);
+    info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+                                       "Operation not supported");
+    mm_callback_info_schedule (info);
+}
+
+static void
 async_call_done (MMModem *modem, GError *error, gpointer user_data)
 {
     DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
@@ -130,6 +158,21 @@ mm_modem_gsm_card_get_imsi (MMModemGsmCard *self,
         str_call_not_supported (self, callback, user_data);
 }
 
+void mm_modem_gsm_card_get_unlock_retries (MMModemGsmCard *self,
+                                         const char *pin_type,
+                                         MMModemUIntFn callback,
+                                         gpointer user_data)
+{
+    g_return_if_fail (MM_IS_MODEM_GSM_CARD (self));
+    g_return_if_fail (pin_type != NULL);
+    g_return_if_fail (callback != NULL);
+
+    if (MM_MODEM_GSM_CARD_GET_INTERFACE (self)->get_unlock_retries)
+        MM_MODEM_GSM_CARD_GET_INTERFACE (self)->get_unlock_retries (self, pin_type, callback, user_data);
+    else
+        uint_call_not_supported (self, callback, user_data);
+}
+
 void
 mm_modem_gsm_card_send_puk (MMModemGsmCard *self,
                             const char *puk,
@@ -275,6 +318,53 @@ impl_gsm_modem_get_imsi (MMModemGsmCard *modem, DBusGMethodInvocation *context)
 
 /*****************************************************************************/
 
+char *pin_puk_type;
+
+static void pin_puk_type_destroy (gpointer data)
+{
+    g_free (pin_puk_type);
+}
+
+static void
+get_unlock_retries_auth_cb (MMAuthRequest *req,
+                  GObject *owner,
+                  DBusGMethodInvocation *context,
+                  gpointer user_data)
+{
+    MMModemGsmCard *self = MM_MODEM_GSM_CARD (owner);
+    GError *error = NULL;
+
+    /* Return any authorization error, otherwise get the remaining attempts */
+    if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+        dbus_g_method_return_error (context, error);
+        g_error_free (error);
+    } else
+        mm_modem_gsm_card_get_unlock_retries (self, (char *)user_data, uint_call_done, context);
+}
+
+static void
+impl_gsm_modem_get_unlock_retries (MMModemGsmCard *modem,
+                                             const char *pin_type,
+                                             DBusGMethodInvocation *context)
+{
+    GError *error = NULL;
+    
+    pin_puk_type = g_strdup (pin_type);
+    /* Make sure the caller is authorized to get the pin attempts */
+    if (!mm_modem_auth_request (MM_MODEM (modem),
+                                MM_AUTHORIZATION_DEVICE_CONTROL,
+                                context,
+                                get_unlock_retries_auth_cb,
+                                pin_puk_type,
+                                pin_puk_type_destroy,
+                                &error)) {
+        dbus_g_method_return_error (context, error);
+        g_error_free (error);
+    }
+}
+
+/*****************************************************************************/
+
 typedef struct {
     char *puk;
     char *pin;
diff --git a/src/mm-modem-gsm-card.h b/src/mm-modem-gsm-card.h
index 4d690e6..b011ce8 100644
--- a/src/mm-modem-gsm-card.h
+++ b/src/mm-modem-gsm-card.h
@@ -27,6 +27,11 @@
 #define MM_MODEM_GSM_CARD_SUPPORTED_BANDS "supported-bands"
 #define MM_MODEM_GSM_CARD_SUPPORTED_MODES "supported-modes"
 
+#define MM_MODEM_GSM_CARD_SIM_PIN "sim-pin"
+#define MM_MODEM_GSM_CARD_SIM_PIN2 "sim-pin2"
+#define MM_MODEM_GSM_CARD_SIM_PUK "sim-puk"
+#define MM_MODEM_GSM_CARD_SIM_PUK2 "sim-puk2"
+
 typedef struct _MMModemGsmCard MMModemGsmCard;
 
 struct _MMModemGsmCard {
@@ -41,6 +46,11 @@ struct _MMModemGsmCard {
                       MMModemStringFn callback,
                       gpointer user_data);
 
+    void (*get_unlock_retries) (MMModemGsmCard *self,
+                              const char *pin_type,
+                              MMModemUIntFn callback,
+                              gpointer user_data);
+
     void (*send_puk) (MMModemGsmCard *self,
                       const char *puk,
                       const char *pin,
@@ -75,6 +85,11 @@ void mm_modem_gsm_card_get_imsi (MMModemGsmCard *self,
                                  MMModemStringFn callback,
                                  gpointer user_data);
 
+void mm_modem_gsm_card_get_unlock_retries (MMModemGsmCard *self,
+                                         const char *pin_type,
+                                         MMModemUIntFn callback,
+                                         gpointer user_data);
+
 void mm_modem_gsm_card_send_puk (MMModemGsmCard *self,
                                  const char *puk,
                                  const char *pin,
diff --git a/src/mm-modem.c b/src/mm-modem.c
index 35e3b07..b378fff 100644
--- a/src/mm-modem.c
+++ b/src/mm-modem.c
@@ -812,6 +812,14 @@ mm_modem_init (gpointer g_iface)
                                NULL,
                                G_PARAM_READABLE));
 
+    g_object_interface_install_property
+        (g_iface,
+         g_param_spec_uint (MM_MODEM_UNLOCK_RETRIES,
+                               "UnlockRetries",
+                               "The remaining number of unlock attempts",
+                               0, G_MAXUINT32, 0,
+                               G_PARAM_READABLE));
+
     /* Signals */
     g_signal_new ("state-changed",
                   iface_type,
diff --git a/src/mm-modem.h b/src/mm-modem.h
index 6eeb4de..d2863e4 100644
--- a/src/mm-modem.h
+++ b/src/mm-modem.h
@@ -59,6 +59,7 @@ typedef enum {
 #define MM_MODEM_IP_METHOD     "ip-method"
 #define MM_MODEM_ENABLED       "enabled"
 #define MM_MODEM_UNLOCK_REQUIRED  "unlock-required"
+#define MM_MODEM_UNLOCK_RETRIES  "unlock-retries"
 #define MM_MODEM_VALID         "valid"      /* not exported */
 #define MM_MODEM_PLUGIN        "plugin"     /* not exported */
 #define MM_MODEM_STATE         "state"      /* not exported */
@@ -83,7 +84,8 @@ typedef enum {
     MM_MODEM_PROP_PLUGIN,      /* Not exported */
     MM_MODEM_PROP_STATE,       /* Not exported */
     MM_MODEM_PROP_ENABLED,
-    MM_MODEM_PROP_UNLOCK_REQUIRED
+    MM_MODEM_PROP_UNLOCK_REQUIRED,
+    MM_MODEM_PROP_UNLOCK_RETRIES
 } MMModemProp;
 
 typedef struct _MMModem MMModem;
diff --git a/introspection/mm-modem-gsm-card.xml b/introspection/mm-modem-gsm-card.xml
index 708d5c9..ad6d833 100644
--- a/introspection/mm-modem-gsm-card.xml
+++ b/introspection/mm-modem-gsm-card.xml
@@ -28,6 +28,24 @@
       </arg>
     </method>
 
+	<method name="GetUnlockRetries">
+      <tp:docstring>
+	Get the remaining PIN (or PUK) attempts.
+      </tp:docstring>
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_get_unlock_retries"/>
+      <arg name="pin_type" type="s" direction="in">
+	<tp:docstring>
+	  The type of PIN.
+	</tp:docstring>
+      </arg>
+	  <arg name="unlock_retries" type="u" direction="out">
+		<tp:docstring>
+	  The number of remaining attempts.
+		</tp:docstring>
+      </arg>
+    </method>
+
     <method name="SendPuk">
       <tp:docstring>
 	Send the PUK and a new PIN to unlock the SIM card.
diff --git a/introspection/mm-modem.xml b/introspection/mm-modem.xml
index da7635b..09512f8 100644
--- a/introspection/mm-modem.xml
+++ b/introspection/mm-modem.xml
@@ -125,6 +125,12 @@
       </tp:docstring>
     </property>
 
+	<property name="UnlockRetries" type="u" access="read">
+      <tp:docstring>
+        The number of unlock retries for the unlock code given by the property UnlockRequired.
+      </tp:docstring>
+    </property>
+
     <property name="IpMethod" type="u" access="read" tp:type="MM_MODEM_IP_METHOD">
       <tp:docstring>
         The IP configuration method.
diff --git a/plugins/mm-modem-mbm.c b/plugins/mm-modem-mbm.c
index 10be0be..7c7b251 100644
--- a/plugins/mm-modem-mbm.c
+++ b/plugins/mm-modem-mbm.c
@@ -28,17 +28,20 @@
 
 #include "mm-modem-mbm.h"
 #include "mm-modem-simple.h"
+#include "mm-modem-gsm-card.h"
 #include "mm-errors.h"
 #include "mm-callback-info.h"
 
 static void modem_init (MMModem *modem_class);
 static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class);
 static void modem_simple_init (MMModemSimple *class);
+static void modem_gsm_card_init (MMModemGsmCard *class);
 
 G_DEFINE_TYPE_EXTENDED (MMModemMbm, mm_modem_mbm, MM_TYPE_GENERIC_GSM, 0,
                         G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)
                         G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init)
-                        G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init))
+                        G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init)
+                        G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init))
 
 #define MM_MODEM_MBM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_MBM, MMModemMbmPrivate))
 
@@ -736,6 +739,89 @@ mbm_modem_authenticate (MMModemMbm *self,
 
 /*****************************************************************************/
 
+static void
+send_epin_done (MMAtSerialPort *port,
+           GString *response,
+           GError *error,
+           gpointer user_data)
+{
+    MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+    char *pin_type;
+    int attempts_left = 0;
+
+    if (error) {
+        info->error = g_error_copy (error);
+        mm_callback_info_schedule (info);
+        return;
+    }
+
+    pin_type = g_strdup ((char *)mm_callback_info_get_data (info, "pin_type"));
+
+    if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN))
+        sscanf (response->str, "*EPIN: %d", &attempts_left);
+    else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PUK)) 
+        sscanf (response->str, "*EPIN: %*d, %d", &attempts_left);
+    else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN2)) 
+        sscanf (response->str, "*EPIN: %*d, %*d, %d", &attempts_left);
+    else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PUK2)) 
+        sscanf (response->str, "*EPIN: %*d, %*d, %*d, %d", &attempts_left);
+    else {
+        g_debug ("%s unknown pin type: %s", __FUNCTION__, pin_type);
+        
+        info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Unknown PIN type");
+    }
+
+    mm_callback_info_set_result (info, GUINT_TO_POINTER (attempts_left), NULL);
+
+    mm_callback_info_schedule (info);
+    
+    g_free (pin_type);
+}
+
+static void
+mbm_get_unlock_retries (MMModemGsmCard *modem,
+              const char *pin_type,
+              MMModemUIntFn callback,
+              gpointer user_data)
+{
+    MMAtSerialPort *port;
+    char *command;
+    MMCallbackInfo *info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
+
+    g_debug ("%s pin type: %s", __FUNCTION__, pin_type);
+
+    /* Ensure we have a usable port to use for the command */
+    port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+    if (!port) {
+        mm_callback_info_schedule (info);
+        return;
+    }
+
+    /* Modem may not be enabled yet, which sometimes can't be done until
+     * the device has been unlocked.  In this case we have to open the port
+     * ourselves.
+     */
+    if (!mm_serial_port_open (MM_SERIAL_PORT (port), &info->error)) {
+        mm_callback_info_schedule (info);
+        return;
+    }
+
+    /* if the modem have not yet been enabled we need to make sure echoing is turned off */
+    command = g_strdup_printf ("E0");
+    mm_at_serial_port_queue_command (port, command, 3, NULL, NULL);
+    g_free (command);
+
+    mm_callback_info_set_data (info, "pin_type", g_strdup (pin_type), g_free);
+
+    command = g_strdup_printf ("*EPIN?");
+
+    mm_at_serial_port_queue_command (port, command, 3, send_epin_done, info);
+
+    g_free (command);
+}
+
+/*****************************************************************************/
+
 static gboolean
 grab_port (MMModem *modem,
            const char *subsys,
@@ -805,6 +891,12 @@ grab_port (MMModem *modem,
 /*****************************************************************************/
 
 static void
+modem_gsm_card_init (MMModemGsmCard *class)
+{
+    class->get_unlock_retries = mbm_get_unlock_retries;
+}
+
+static void
 modem_gsm_network_init (MMModemGsmNetwork *class)
 {
     class->do_register = do_register;
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
index 6b54442..e028a93 100644
--- a/src/mm-generic-gsm.c
+++ b/src/mm-generic-gsm.c
@@ -135,6 +135,12 @@ static void reg_info_updated (MMGenericGsm *self,
                               gboolean update_name,
                               const char *oper_name);
 
+static void
+get_unlock_retries_cb (MMModem *modem,
+                       guint32 result,
+                       GError *error,
+                       gpointer user_data);
+
 MMModem *
 mm_generic_gsm_new (const char *device,
                     const char *driver,
@@ -221,6 +227,7 @@ pin_check_done (MMAtSerialPort *port,
 
         if (g_str_has_prefix (str, "READY")) {
             mm_modem_base_set_unlock_required (MM_MODEM_BASE (info->modem), NULL);
+            mm_modem_base_set_unlock_retries (MM_MODEM_BASE (info->modem), 0);
             parsed = TRUE;
         } else {
             CPinResult *iter = &unlock_results[0];
@@ -230,6 +237,10 @@ pin_check_done (MMAtSerialPort *port,
                 if (g_str_has_prefix (str, iter->result)) {
                     info->error = mm_mobile_error_for_code (iter->code);
                     mm_modem_base_set_unlock_required (MM_MODEM_BASE (info->modem), iter->normalized);
+                    mm_modem_gsm_card_get_unlock_retries (MM_MODEM_GSM_CARD (info->modem),
+                                                        iter->normalized,
+                                                        get_unlock_retries_cb,
+                                                        NULL);
                     parsed = TRUE;
                     break;
                 }
@@ -241,6 +252,7 @@ pin_check_done (MMAtSerialPort *port,
     if (!parsed) {
         /* Assume unlocked if we don't recognize the pin request result */
         mm_modem_base_set_unlock_required (MM_MODEM_BASE (info->modem), NULL);
+        mm_modem_base_set_unlock_retries (MM_MODEM_BASE (info->modem), 0);
 
         if (!info->error) {
             info->error = g_error_new (MM_MODEM_ERROR,
@@ -268,6 +280,18 @@ check_pin (MMGenericGsm *modem,
     mm_at_serial_port_queue_command (priv->primary, "+CPIN?", 3, pin_check_done, info);
 }
 
+static void
+get_unlock_retries_cb (MMModem *modem,
+                                  guint32 result,
+                                  GError *error,
+                                  gpointer user_data)
+{
+    if (!error)
+        mm_modem_base_set_unlock_retries (MM_MODEM_BASE (modem), result);
+    else
+        mm_modem_base_set_unlock_retries (MM_MODEM_BASE (modem), 0);
+}
+
 /*****************************************************************************/
 
 void
@@ -1275,9 +1299,6 @@ send_pin_done (MMAtSerialPort *port,
 
     if (error) {
         info->error = g_error_copy (error);
-        mm_callback_info_schedule (info);
-        mm_serial_port_close (MM_SERIAL_PORT (port));
-        return;
     }
 
     /* Get latest PIN status */
diff --git a/src/mm-modem-base.c b/src/mm-modem-base.c
index 0a91d3f..611b1a1 100644
--- a/src/mm-modem-base.c
+++ b/src/mm-modem-base.c
@@ -43,6 +43,7 @@ typedef struct {
     char *plugin;
     char *device;
     char *unlock_required;
+    guint32 unlock_retries;
     guint32 ip_method;
     gboolean valid;
     MMModemState state;
@@ -232,6 +233,40 @@ mm_modem_base_set_unlock_required (MMModemBase *self, const char *unlock_require
     g_object_notify (G_OBJECT (self), MM_MODEM_UNLOCK_REQUIRED);
 }
 
+guint32
+mm_modem_base_get_unlock_retries (MMModemBase *self)
+{   
+    g_return_val_if_fail (self != NULL, 0);
+    g_return_val_if_fail (MM_IS_MODEM_BASE (self), 0);
+
+    return MM_MODEM_BASE_GET_PRIVATE (self)->unlock_retries;
+}
+
+void
+mm_modem_base_set_unlock_retries (MMModemBase *self, guint unlock_retries)
+{
+    MMModemBasePrivate *priv;
+    const char *dbus_path;
+
+    g_return_if_fail (self != NULL);
+    g_return_if_fail (MM_IS_MODEM_BASE (self));
+
+    priv = MM_MODEM_BASE_GET_PRIVATE (self);
+
+    /* Only do something if the value changes */
+    if (priv->unlock_retries == unlock_retries)
+        return;
+
+    priv->unlock_retries = unlock_retries;
+
+    dbus_path = (const char *) g_object_get_data (G_OBJECT (self), DBUS_PATH_TAG);
+    if (dbus_path) {
+        g_message ("Modem %s: unlock retries is %d", dbus_path, priv->unlock_retries);
+    }
+
+    g_object_notify (G_OBJECT (self), MM_MODEM_UNLOCK_RETRIES);
+}
+
 const char *
 mm_modem_base_get_manf (MMModemBase *self)
 {
@@ -490,6 +525,9 @@ mm_modem_base_init (MMModemBase *self)
     mm_properties_changed_signal_register_property (G_OBJECT (self),
                                                     MM_MODEM_UNLOCK_REQUIRED,
                                                     MM_MODEM_DBUS_INTERFACE);
+    mm_properties_changed_signal_register_property (G_OBJECT (self),
+                                                    MM_MODEM_UNLOCK_RETRIES,
+                                                    MM_MODEM_DBUS_INTERFACE);
 }
 
 static void
@@ -539,6 +577,7 @@ set_property (GObject *object, guint prop_id,
     case MM_MODEM_PROP_TYPE:
     case MM_MODEM_PROP_ENABLED:
     case MM_MODEM_PROP_UNLOCK_REQUIRED:
+    case MM_MODEM_PROP_UNLOCK_RETRIES:
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -583,6 +622,9 @@ get_property (GObject *object, guint prop_id,
     case MM_MODEM_PROP_UNLOCK_REQUIRED:
         g_value_set_string (value, priv->unlock_required);
         break;
+    case MM_MODEM_PROP_UNLOCK_RETRIES:
+        g_value_set_uint (value, priv->unlock_retries);
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -658,6 +700,10 @@ mm_modem_base_class_init (MMModemBaseClass *klass)
                                       MM_MODEM_PROP_UNLOCK_REQUIRED,
                                       MM_MODEM_UNLOCK_REQUIRED);
 
+    g_object_class_override_property (object_class,
+                                      MM_MODEM_PROP_UNLOCK_RETRIES,
+                                      MM_MODEM_UNLOCK_RETRIES);
+
     mm_properties_changed_signal_new (object_class);
 }
 
diff --git a/src/mm-modem-base.h b/src/mm-modem-base.h
index 516af2e..8eec0e4 100644
--- a/src/mm-modem-base.h
+++ b/src/mm-modem-base.h
@@ -67,6 +67,12 @@ const char *mm_modem_base_get_unlock_required (MMModemBase *self);
 void mm_modem_base_set_unlock_required (MMModemBase *self,
                                         const char *unlock_required);
 
+guint mm_modem_base_get_unlock_retries (MMModemBase *self);
+
+void mm_modem_base_set_unlock_retries (MMModemBase *self,
+                                        guint unlock_retries);
+
+
 const char *mm_modem_base_get_manf (MMModemBase *self);
 void        mm_modem_base_set_manf (MMModemBase *self, const char *manf);
 
diff --git a/src/mm-modem-gsm-card.c b/src/mm-modem-gsm-card.c
index 432a4a3..f5b99f8 100644
--- a/src/mm-modem-gsm-card.c
+++ b/src/mm-modem-gsm-card.c
@@ -28,6 +28,10 @@ static void impl_gsm_modem_get_imei (MMModemGsmCard *modem,
 static void impl_gsm_modem_get_imsi (MMModemGsmCard *modem,
                                      DBusGMethodInvocation *context);
 
+static void impl_gsm_modem_get_unlock_retries (MMModemGsmCard *modem,
+                                             const char *pin_type,
+                                             DBusGMethodInvocation *context);
+
 static void impl_gsm_modem_send_pin (MMModemGsmCard *modem,
                                      const char *pin,
                                      DBusGMethodInvocation *context);
@@ -77,6 +81,30 @@ str_call_not_supported (MMModemGsmCard *self,
 }
 
 static void
+uint_call_done (MMModem *modem, guint32 result, GError *error, gpointer user_data)
+{
+    DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+    if (error)
+        dbus_g_method_return_error (context, error);
+    else
+        dbus_g_method_return (context, result);
+}
+
+static void
+uint_call_not_supported (MMModemGsmCard *self,
+                         MMModemUIntFn callback,
+                         gpointer user_data)
+{
+    MMCallbackInfo *info;
+
+    info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data);
+    info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+                                       "Operation not supported");
+    mm_callback_info_schedule (info);
+}
+
+static void
 async_call_done (MMModem *modem, GError *error, gpointer user_data)
 {
     DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
@@ -130,6 +158,21 @@ mm_modem_gsm_card_get_imsi (MMModemGsmCard *self,
         str_call_not_supported (self, callback, user_data);
 }
 
+void mm_modem_gsm_card_get_unlock_retries (MMModemGsmCard *self,
+                                         const char *pin_type,
+                                         MMModemUIntFn callback,
+                                         gpointer user_data)
+{
+    g_return_if_fail (MM_IS_MODEM_GSM_CARD (self));
+    g_return_if_fail (pin_type != NULL);
+    g_return_if_fail (callback != NULL);
+
+    if (MM_MODEM_GSM_CARD_GET_INTERFACE (self)->get_unlock_retries)
+        MM_MODEM_GSM_CARD_GET_INTERFACE (self)->get_unlock_retries (self, pin_type, callback, user_data);
+    else
+        uint_call_not_supported (self, callback, user_data);
+}
+
 void
 mm_modem_gsm_card_send_puk (MMModemGsmCard *self,
                             const char *puk,
@@ -275,6 +318,53 @@ impl_gsm_modem_get_imsi (MMModemGsmCard *modem, DBusGMethodInvocation *context)
 
 /*****************************************************************************/
 
+char *pin_puk_type;
+
+static void pin_puk_type_destroy (gpointer data)
+{
+    g_free (pin_puk_type);
+}
+
+static void
+get_unlock_retries_auth_cb (MMAuthRequest *req,
+                  GObject *owner,
+                  DBusGMethodInvocation *context,
+                  gpointer user_data)
+{
+    MMModemGsmCard *self = MM_MODEM_GSM_CARD (owner);
+    GError *error = NULL;
+
+    /* Return any authorization error, otherwise get the remaining attempts */
+    if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+        dbus_g_method_return_error (context, error);
+        g_error_free (error);
+    } else
+        mm_modem_gsm_card_get_unlock_retries (self, (char *)user_data, uint_call_done, context);
+}
+
+static void
+impl_gsm_modem_get_unlock_retries (MMModemGsmCard *modem,
+                                             const char *pin_type,
+                                             DBusGMethodInvocation *context)
+{
+    GError *error = NULL;
+    
+    pin_puk_type = g_strdup (pin_type);
+    /* Make sure the caller is authorized to get the pin attempts */
+    if (!mm_modem_auth_request (MM_MODEM (modem),
+                                MM_AUTHORIZATION_DEVICE_CONTROL,
+                                context,
+                                get_unlock_retries_auth_cb,
+                                pin_puk_type,
+                                pin_puk_type_destroy,
+                                &error)) {
+        dbus_g_method_return_error (context, error);
+        g_error_free (error);
+    }
+}
+
+/*****************************************************************************/
+
 typedef struct {
     char *puk;
     char *pin;
diff --git a/src/mm-modem-gsm-card.h b/src/mm-modem-gsm-card.h
index 4d690e6..b011ce8 100644
--- a/src/mm-modem-gsm-card.h
+++ b/src/mm-modem-gsm-card.h
@@ -27,6 +27,11 @@
 #define MM_MODEM_GSM_CARD_SUPPORTED_BANDS "supported-bands"
 #define MM_MODEM_GSM_CARD_SUPPORTED_MODES "supported-modes"
 
+#define MM_MODEM_GSM_CARD_SIM_PIN "sim-pin"
+#define MM_MODEM_GSM_CARD_SIM_PIN2 "sim-pin2"
+#define MM_MODEM_GSM_CARD_SIM_PUK "sim-puk"
+#define MM_MODEM_GSM_CARD_SIM_PUK2 "sim-puk2"
+
 typedef struct _MMModemGsmCard MMModemGsmCard;
 
 struct _MMModemGsmCard {
@@ -41,6 +46,11 @@ struct _MMModemGsmCard {
                       MMModemStringFn callback,
                       gpointer user_data);
 
+    void (*get_unlock_retries) (MMModemGsmCard *self,
+                              const char *pin_type,
+                              MMModemUIntFn callback,
+                              gpointer user_data);
+
     void (*send_puk) (MMModemGsmCard *self,
                       const char *puk,
                       const char *pin,
@@ -75,6 +85,11 @@ void mm_modem_gsm_card_get_imsi (MMModemGsmCard *self,
                                  MMModemStringFn callback,
                                  gpointer user_data);
 
+void mm_modem_gsm_card_get_unlock_retries (MMModemGsmCard *self,
+                                         const char *pin_type,
+                                         MMModemUIntFn callback,
+                                         gpointer user_data);
+
 void mm_modem_gsm_card_send_puk (MMModemGsmCard *self,
                                  const char *puk,
                                  const char *pin,
diff --git a/src/mm-modem.c b/src/mm-modem.c
index 35e3b07..b378fff 100644
--- a/src/mm-modem.c
+++ b/src/mm-modem.c
@@ -812,6 +812,14 @@ mm_modem_init (gpointer g_iface)
                                NULL,
                                G_PARAM_READABLE));
 
+    g_object_interface_install_property
+        (g_iface,
+         g_param_spec_uint (MM_MODEM_UNLOCK_RETRIES,
+                               "UnlockRetries",
+                               "The remaining number of unlock attempts",
+                               0, G_MAXUINT32, 0,
+                               G_PARAM_READABLE));
+
     /* Signals */
     g_signal_new ("state-changed",
                   iface_type,
diff --git a/src/mm-modem.h b/src/mm-modem.h
index 6eeb4de..d2863e4 100644
--- a/src/mm-modem.h
+++ b/src/mm-modem.h
@@ -59,6 +59,7 @@ typedef enum {
 #define MM_MODEM_IP_METHOD     "ip-method"
 #define MM_MODEM_ENABLED       "enabled"
 #define MM_MODEM_UNLOCK_REQUIRED  "unlock-required"
+#define MM_MODEM_UNLOCK_RETRIES  "unlock-retries"
 #define MM_MODEM_VALID         "valid"      /* not exported */
 #define MM_MODEM_PLUGIN        "plugin"     /* not exported */
 #define MM_MODEM_STATE         "state"      /* not exported */
@@ -83,7 +84,8 @@ typedef enum {
     MM_MODEM_PROP_PLUGIN,      /* Not exported */
     MM_MODEM_PROP_STATE,       /* Not exported */
     MM_MODEM_PROP_ENABLED,
-    MM_MODEM_PROP_UNLOCK_REQUIRED
+    MM_MODEM_PROP_UNLOCK_REQUIRED,
+    MM_MODEM_PROP_UNLOCK_RETRIES
 } MMModemProp;
 
 typedef struct _MMModem MMModem;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]