aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2010-10-25 17:41:08 -0500
committerDan Williams <dcbw@redhat.com>2010-10-25 17:41:08 -0500
commit1684d8b1734be69700a93a9995b99fac6ffb9763 (patch)
tree29b0fd07d0b864bddf23d036ab1c223ccf15a625
parent46106660fe6aae1018f6f6c45281ccef837e78f8 (diff)
gsm: add SimIdentifier property
An obfuscated SimIdentifier that may be available before the PIN has been entered, for use in auto-unlocking a device. If this value is present, it should be used in preference to DeviceIdentifier as it is SIM-specific like the PIN code.
-rw-r--r--introspection/mm-modem-gsm-card.xml8
-rw-r--r--plugins/mm-modem-sierra-gsm.c74
-rw-r--r--src/mm-generic-gsm.c198
-rw-r--r--src/mm-generic-gsm.h6
-rw-r--r--src/mm-modem-gsm-card.c8
-rw-r--r--src/mm-modem-gsm-card.h1
-rw-r--r--src/mm-serial-parsers.c7
-rwxr-xr-xtest/info.py15
8 files changed, 312 insertions, 5 deletions
diff --git a/introspection/mm-modem-gsm-card.xml b/introspection/mm-modem-gsm-card.xml
index 9c9fdc19..d4811576 100644
--- a/introspection/mm-modem-gsm-card.xml
+++ b/introspection/mm-modem-gsm-card.xml
@@ -109,6 +109,14 @@
</arg>
</method>
+ <property name="SimIdentifier" type="s" access="read">
+ <tp:docstring>
+ An obfuscated SIM identifier based on the IMSI or the ICCID. This may
+ be available before the PIN has been entered depending on the device
+ itself.
+ </tp:docstring>
+ </property>
+
<property name="SupportedBands" type="u" access="read" tp:type="MM_MODEM_GSM_BAND">
<tp:docstring>
Bands supported by the card. (Note for plugin writers:
diff --git a/plugins/mm-modem-sierra-gsm.c b/plugins/mm-modem-sierra-gsm.c
index c86c1c5e..b33af6b2 100644
--- a/plugins/mm-modem-sierra-gsm.c
+++ b/plugins/mm-modem-sierra-gsm.c
@@ -15,6 +15,7 @@
*/
#include <config.h>
+#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -242,6 +243,78 @@ get_access_technology (MMGenericGsm *modem,
mm_at_serial_port_queue_command (port, "*CNTI=0", 3, get_act_request_done, info);
}
+static void
+get_sim_iccid_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ const char *p;
+ char buf[21];
+ int i;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ goto done;
+ }
+
+ p = mm_strip_tag (response->str, "!ICCID:");
+ if (!p) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Failed to parse !ICCID response");
+ goto done;
+ }
+
+ memset (buf, 0, sizeof (buf));
+ for (i = 0; i < 20; i++) {
+ if (!isdigit (p[i]) && (p[i] != 'F') && (p[i] == 'f')) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "CRSM ICCID response contained invalid character '%c'",
+ p[i]);
+ goto done;
+ }
+ if (p[i] == 'F' || p[i] == 'f') {
+ buf[i] = 0;
+ break;
+ }
+ buf[i] = p[i];
+ }
+
+ if (i == 19 || i == 20)
+ mm_callback_info_set_result (info, g_strdup (buf), g_free);
+ else {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Invalid +CRSM ICCID response size (was %d, expected 19 or 20)",
+ i);
+ }
+
+done:
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_sim_iccid (MMGenericGsm *modem,
+ MMModemStringFn callback,
+ gpointer user_data)
+{
+ MMAtSerialPort *port;
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (modem, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_at_serial_port_queue_command (port, "!ICCID?", 3, get_sim_iccid_done, info);
+}
+
/*****************************************************************************/
/* Modem class override functions */
/*****************************************************************************/
@@ -355,5 +428,6 @@ mm_modem_sierra_gsm_class_init (MMModemSierraGsmClass *klass)
gsm_class->set_allowed_mode = set_allowed_mode;
gsm_class->get_allowed_mode = get_allowed_mode;
gsm_class->get_access_technology = get_access_technology;
+ gsm_class->get_sim_iccid = get_sim_iccid;
}
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
index b7b8e84b..23d6c551 100644
--- a/src/mm-generic-gsm.c
+++ b/src/mm-generic-gsm.c
@@ -69,6 +69,7 @@ typedef struct {
gboolean pin_checked;
guint32 pin_check_tries;
guint pin_check_timeout;
+ char *simid;
MMModemGsmAllowedMode allowed_mode;
@@ -555,6 +556,188 @@ initial_info_check (MMGenericGsm *self)
}
}
+static void
+get_iccid_done (MMModem *modem,
+ const char *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMGenericGsmPrivate *priv;
+ const char *p = response;
+ GChecksum *sum;
+
+ if (error || !response || !strlen (response))
+ return;
+
+ sum = g_checksum_new (G_CHECKSUM_SHA1);
+
+ /* Make sure it looks like an ICCID */
+ while (*p) {
+ if (!isdigit (*p)) {
+ g_warning ("%s: invalid ICCID format (not a digit)", __func__);
+ goto out;
+ }
+ g_checksum_update (sum, (const guchar *) p++, 1);
+ }
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ g_free (priv->simid);
+ priv->simid = g_strdup (g_checksum_get_string (sum));
+
+ if (mm_options_debug ()) {
+ g_debug ("SIM ID source '%s'", response);
+ g_debug ("SIM ID '%s'", priv->simid);
+ }
+
+ g_object_notify (G_OBJECT (modem), MM_MODEM_GSM_CARD_SIM_IDENTIFIER);
+
+out:
+ g_checksum_free (sum);
+}
+
+static void
+real_get_iccid_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ const char *str;
+ int sw1, sw2;
+ gboolean success = FALSE;
+ char buf[21], swapped[21];
+
+ if (error) {
+ info->error = g_error_copy (error);
+ goto done;
+ }
+
+ memset (buf, 0, sizeof (buf));
+ str = mm_strip_tag (response->str, "+CRSM:");
+ if (sscanf (str, "%d,%d,\"%20c\"", &sw1, &sw2, (char *) &buf) == 3)
+ success = TRUE;
+ else {
+ /* May not include quotes... */
+ if (sscanf (str, "%d,%d,%20c", &sw1, &sw2, (char *) &buf) == 3)
+ success = TRUE;
+ }
+
+ if (!success) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Could not parse the CRSM response");
+ goto done;
+ }
+
+ if ((sw1 == 0x90 && sw2 == 0x00) || (sw1 == 0x91) || (sw1 == 0x92) || (sw1 == 0x9f)) {
+ gsize len = 0;
+ int f_pos = -1, i;
+
+ /* Make sure the buffer is only digits or 'F' */
+ for (len = 0; len < sizeof (buf) && buf[len]; len++) {
+ if (isdigit (buf[len]))
+ continue;
+ if (buf[len] == 'F' || buf[len] == 'f') {
+ buf[len] = 'F'; /* canonicalize the F */
+ f_pos = len;
+ continue;
+ }
+ if (buf[len] == '\"') {
+ buf[len] = 0;
+ break;
+ }
+
+ /* Invalid character */
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "CRSM ICCID response contained invalid character '%c'",
+ buf[len]);
+ goto done;
+ }
+
+ /* BCD encoded ICCIDs are 20 digits long */
+ if (len != 20) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Invalid +CRSM ICCID response size (was %zd, expected 20)",
+ len);
+ goto done;
+ }
+
+ /* Ensure if there's an 'F' that it's second-to-last */
+ if ((f_pos >= 0) && (f_pos != len - 2)) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Invalid +CRSM ICCID length (unexpected F)");
+ goto done;
+ }
+
+ /* Swap digits in the EFiccid response to get the actual ICCID, each
+ * group of 2 digits is reversed in the +CRSM response. i.e.:
+ *
+ * 21436587 -> 12345678
+ */
+ memset (swapped, 0, sizeof (swapped));
+ for (i = 0; i < 10; i++) {
+ swapped[i * 2] = buf[(i * 2) + 1];
+ swapped[(i * 2) + 1] = buf[i * 2];
+ }
+
+ /* Zero out the F for 19 digit ICCIDs */
+ if (swapped[len - 1] == 'F')
+ swapped[len - 1] = 0;
+
+ mm_callback_info_set_result (info, g_strdup (swapped), g_free);
+ } else {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
+ sw1, sw2);
+ }
+
+done:
+ mm_callback_info_schedule (info);
+}
+
+static void
+real_get_sim_iccid (MMGenericGsm *self,
+ MMModemStringFn callback,
+ gpointer callback_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+ GError *error = NULL;
+
+ port = mm_generic_gsm_get_best_at_port (self, &error);
+ if (!port) {
+ callback (MM_MODEM (self), NULL, error, callback_data);
+ g_clear_error (&error);
+ return;
+ }
+
+ if (!mm_serial_port_open (MM_SERIAL_PORT (port), &error)) {
+ callback (MM_MODEM (self), NULL, error, callback_data);
+ g_clear_error (&error);
+ return;
+ }
+
+ info = mm_callback_info_string_new (MM_MODEM (self), callback, callback_data);
+
+ /* READ BINARY of EFiccid (ICC Identification) ETSI TS 102.221 section 13.2 */
+ mm_at_serial_port_queue_command (port,
+ "+CRSM=176,12258,0,0,10",
+ 3,
+ real_get_iccid_done,
+ info);
+}
+
+static void
+initial_iccid_check (MMGenericGsm *self)
+{
+ g_assert (MM_GENERIC_GSM_GET_CLASS (self)->get_sim_iccid);
+ MM_GENERIC_GSM_GET_CLASS (self)->get_sim_iccid (self, get_iccid_done, NULL);
+}
+
static gboolean
owns_port (MMModem *modem, const char *subsys, const char *name)
{
@@ -608,9 +791,12 @@ mm_generic_gsm_grab_port (MMGenericGsm *self,
/* Get the modem's general info */
initial_info_check (self);
- /* Get modem's IMEI number */
+ /* Get modem's IMEI */
initial_imei_check (self);
+ /* Try to get the SIM's ICCID */
+ initial_iccid_check (self);
+
/* Get modem's initial lock/unlock state */
initial_pin_check (self);
@@ -4184,6 +4370,7 @@ set_property (GObject *object, guint prop_id,
case MM_GENERIC_GSM_PROP_SUPPORTED_MODES:
case MM_GENERIC_GSM_PROP_ALLOWED_MODE:
case MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY:
+ case MM_GENERIC_GSM_PROP_SIM_IDENTIFIER:
#if LOCATION_API
case MM_GENERIC_GSM_PROP_LOC_CAPABILITIES:
case MM_GENERIC_GSM_PROP_LOC_ENABLED:
@@ -4250,6 +4437,9 @@ get_property (GObject *object, guint prop_id,
else
g_value_set_uint (value, MM_MODEM_GSM_ACCESS_TECH_UNKNOWN);
break;
+ case MM_GENERIC_GSM_PROP_SIM_IDENTIFIER:
+ g_value_set_string (value, priv->simid);
+ break;
#if LOCATION_API
case MM_GENERIC_GSM_PROP_LOC_CAPABILITIES:
g_value_set_uint (value, priv->loc_caps);
@@ -4303,6 +4493,7 @@ finalize (GObject *object)
g_free (priv->oper_code);
g_free (priv->oper_name);
+ g_free (priv->simid);
G_OBJECT_CLASS (mm_generic_gsm_parent_class)->finalize (object);
}
@@ -4323,6 +4514,7 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass)
klass->do_enable = real_do_enable;
klass->do_enable_power_up_done = real_do_enable_power_up_done;
klass->do_disconnect = real_do_disconnect;
+ klass->get_sim_iccid = real_get_sim_iccid;
/* Properties */
g_object_class_override_property (object_class,
@@ -4349,6 +4541,10 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass)
MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY,
MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY);
+ g_object_class_override_property (object_class,
+ MM_GENERIC_GSM_PROP_SIM_IDENTIFIER,
+ MM_MODEM_GSM_CARD_SIM_IDENTIFIER);
+
#if LOCATION_API
g_object_class_override_property (object_class,
MM_GENERIC_GSM_PROP_LOC_CAPABILITIES,
diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h
index be75ceed..175cf9a0 100644
--- a/src/mm-generic-gsm.h
+++ b/src/mm-generic-gsm.h
@@ -55,6 +55,7 @@ typedef enum {
MM_GENERIC_GSM_PROP_LOC_SIGNAL,
MM_GENERIC_GSM_PROP_LOC_LOCATION,
#endif
+ MM_GENERIC_GSM_PROP_SIM_IDENTIFIER,
} MMGenericGsmProp;
typedef enum {
@@ -128,6 +129,11 @@ typedef struct {
void (*loc_get_capabilities) (MMGenericGsm *self,
MMModemUIntFn callback,
gpointer user_data);
+
+ /* Called by the generic class to retrieve the SIM's ICCID */
+ void (*get_sim_iccid) (MMGenericGsm *self,
+ MMModemStringFn callback,
+ gpointer user_data);
} MMGenericGsmClass;
GType mm_generic_gsm_get_type (void);
diff --git a/src/mm-modem-gsm-card.c b/src/mm-modem-gsm-card.c
index e03b964d..d04f0146 100644
--- a/src/mm-modem-gsm-card.c
+++ b/src/mm-modem-gsm-card.c
@@ -581,6 +581,14 @@ mm_modem_gsm_card_init (gpointer g_iface)
g_object_interface_install_property
(g_iface,
+ g_param_spec_string (MM_MODEM_GSM_CARD_SIM_IDENTIFIER,
+ "SimIdentifier",
+ "An obfuscated identifier of the SIM",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_interface_install_property
+ (g_iface,
g_param_spec_uint (MM_MODEM_GSM_CARD_SUPPORTED_BANDS,
"Supported Modes",
"Supported frequency bands of the card",
diff --git a/src/mm-modem-gsm-card.h b/src/mm-modem-gsm-card.h
index 584d7344..e617d8fe 100644
--- a/src/mm-modem-gsm-card.h
+++ b/src/mm-modem-gsm-card.h
@@ -26,6 +26,7 @@
#define MM_MODEM_GSM_CARD_SUPPORTED_BANDS "supported-bands"
#define MM_MODEM_GSM_CARD_SUPPORTED_MODES "supported-modes"
+#define MM_MODEM_GSM_CARD_SIM_IDENTIFIER "sim-identifier"
#define MM_MODEM_GSM_CARD_SIM_PIN "sim-pin"
#define MM_MODEM_GSM_CARD_SIM_PIN2 "sim-pin2"
diff --git a/src/mm-serial-parsers.c b/src/mm-serial-parsers.c
index 00fe9dce..735ebd97 100644
--- a/src/mm-serial-parsers.c
+++ b/src/mm-serial-parsers.c
@@ -33,6 +33,13 @@ response_clean (GString *response)
s -= 2;
}
+ /* Contains duplicate '<CR><CR>' */
+ s = response->str;
+ while ((response->len >= 2) && (*s == '\r') && (*(s + 1) == '\r')) {
+ g_string_erase (response, 0, 1);
+ s = response->str;
+ }
+
/* Starts with one or more '<CR><LF>' */
s = response->str;
while ((response->len >= 2) && (*s == '\r') && (*(s + 1) == '\n')) {
diff --git a/test/info.py b/test/info.py
index 347f8fe8..6659a3a4 100755
--- a/test/info.py
+++ b/test/info.py
@@ -167,6 +167,13 @@ def gsm_inspect(proxy, props):
# Gsm.Card interface
card = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_GSM_CARD)
+ simid = "<unavailable>"
+ try:
+ simid = props.Get(MM_DBUS_INTERFACE_MODEM_GSM_CARD, "SimIdentifier")
+ except dbus.exceptions.DBusException:
+ pass
+ print "SIM ID: %s" % simid
+
imei = "<unavailable>"
try:
imei = card.GetImei()
@@ -229,10 +236,10 @@ if mtype == 1:
elif mtype == 2:
print "Type: CDMA"
-print "Driver: '%s'" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'Driver'))
-print "Modem device: '%s'" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'MasterDevice'))
-print "Data device: '%s'" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'Device'))
-print "Device ID: '%s'" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'DeviceIdentifier'))
+print "Driver: %s" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'Driver'))
+print "Modem device: %s" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'MasterDevice'))
+print "Data device: %s" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'Device'))
+print "Device ID: %s" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'DeviceIdentifier'))
print ""
modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM)