aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mm-broadband-modem.c81
-rw-r--r--src/mm-modem-helpers.c121
-rw-r--r--src/mm-modem-helpers.h5
-rw-r--r--src/tests/test-modem-helpers.c68
4 files changed, 263 insertions, 12 deletions
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
index 7b34f78c..396ef974 100644
--- a/src/mm-broadband-modem.c
+++ b/src/mm-broadband-modem.c
@@ -750,16 +750,29 @@ modem_load_equipment_identifier_finish (MMIfaceModem *self,
GError **error)
{
GVariant *result;
- gchar *equip_id = NULL, *tmp;
+ gchar *equip_id = NULL, *esn = NULL, *meid = NULL, *imei = NULL;
result = mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, error);
if (result) {
equip_id = sanitize_info_reply (result, "GSN:");
- /* Some CDMA devices prefix the ESN with "0x" */
- if (strncmp (equip_id, "0x", 2) == 0 && strlen (equip_id) == 10) {
- tmp = g_strdup (equip_id + 2);
+
+ /* Modems put all sorts of things into the GSN response; sanitize it */
+ if (mm_parse_gsn (equip_id, &imei, &meid, &esn)) {
g_free (equip_id);
- equip_id = tmp;
+
+ if (imei)
+ equip_id = g_strdup (imei);
+ else if (meid)
+ equip_id = g_strdup (meid);
+ else if (esn)
+ equip_id = g_strdup (esn);
+ g_free (esn);
+ g_free (meid);
+ g_free (imei);
+
+ g_assert (equip_id);
+ } else {
+ /* Leave whatever the modem returned alone */
}
mm_dbg ("loaded equipment identifier: %s", equip_id);
}
@@ -2942,12 +2955,15 @@ modem_3gpp_load_imei_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- gchar *imei;
+ const gchar *result;
+ gchar *imei = NULL;
- imei = g_strdup (mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error));
- if (!imei)
+ result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!result)
return NULL;
+ result = mm_strip_tag (result, "+CGSN:");
+ mm_parse_gsn (result, &imei, NULL, NULL);
mm_dbg ("loaded IMEI: %s", imei);
return imei;
}
@@ -3084,6 +3100,8 @@ clck_test_ready (MMBaseModem *self,
return;
}
+ctx->facilities &= ~MM_MODEM_3GPP_FACILITY_PH_SIM;
+
/* Go on... */
get_next_facility_lock_status (ctx);
}
@@ -5888,12 +5906,15 @@ modem_cdma_load_esn_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
GError **error)
{
- gchar *esn;
+ const gchar *result;
+ gchar *esn = NULL;
- esn = g_strdup (mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error));
- if (!esn)
+ result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!result)
return NULL;
+ result = mm_strip_tag (result, "+GSN:");
+ mm_parse_gsn (result, NULL, NULL, &esn);
mm_dbg ("loaded ESN: %s", esn);
return esn;
}
@@ -5913,6 +5934,42 @@ modem_cdma_load_esn (MMIfaceModemCdma *self,
}
/*****************************************************************************/
+/* MEID loading (CDMA interface) */
+
+static gchar *
+modem_cdma_load_meid_finish (MMIfaceModemCdma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ const gchar *result;
+ gchar *meid = NULL;
+
+ result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!result)
+ return NULL;
+
+ result = mm_strip_tag (result, "+GSN:");
+ mm_parse_gsn (result, NULL, &meid, NULL);
+ mm_dbg ("loaded MEID: %s", meid);
+ return meid;
+}
+
+static void
+modem_cdma_load_meid (MMIfaceModemCdma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ /* Some devices return both the MEID and the ESN in the +GSN response */
+ mm_dbg ("loading MEID...");
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+GSN",
+ 3,
+ TRUE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
/* HDR state check (CDMA interface) */
typedef struct {
@@ -9278,6 +9335,8 @@ iface_modem_cdma_init (MMIfaceModemCdma *iface)
/* Initialization steps */
iface->load_esn = modem_cdma_load_esn;
iface->load_esn_finish = modem_cdma_load_esn_finish;
+ iface->load_meid = modem_cdma_load_meid;
+ iface->load_meid_finish = modem_cdma_load_meid_finish;
/* Registration check steps */
iface->setup_registration_checks = modem_cdma_setup_registration_checks;
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index cedd2b7f..b20ff1c6 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -2100,3 +2100,124 @@ mm_cdma_normalize_band (const gchar *long_band,
/* Unknown/not registered */
return 'Z';
}
+
+/*************************************************************************/
+
+/* Caller must strip any "+GSN:" or "+CGSN" from @gsn */
+gboolean
+mm_parse_gsn (const char *gsn,
+ gchar **out_imei,
+ gchar **out_meid,
+ gchar **out_esn)
+{
+ gchar **items, **iter;
+ gchar *meid = NULL, *esn = NULL, *imei = NULL, *p;
+ gboolean success = FALSE;
+
+ if (!gsn || !gsn[0])
+ return FALSE;
+
+ /* IMEI is 15 numeric digits */
+
+ /* ESNs take one of two formats:
+ * (1) 7 or 8 hexadecimal digits
+ * (2) 10 or 11 decimal digits
+ *
+ * In addition, leading zeros may be present or absent, and hexadecimal
+ * ESNs may or may not be prefixed with "0x".
+ */
+
+ /* MEIDs take one of two formats:
+ * (1) 14 hexadecimal digits, sometimes padded to 16 digits with leading zeros
+ * (2) 18 decimal digits
+ *
+ * As with ESNs, leading zeros may be present or absent, and hexadecimal
+ * MEIDs may or may not be prefixed with "0x".
+ */
+
+ items = g_strsplit_set (gsn, "\r\n\t: ,", 0);
+ for (iter = items; iter && *iter && (!esn || !meid); iter++) {
+ gboolean expect_hex = FALSE, is_hex, is_digit;
+ gchar *s = *iter;
+ guint len = 0;
+
+ if (!s[0])
+ continue;
+
+ if (g_str_has_prefix (s, "0x") || g_str_has_prefix (s, "0X")) {
+ expect_hex = TRUE;
+ s += 2;
+
+ /* Skip any leading zeros */
+ while (*s == '0')
+ s++;
+ }
+
+ /* Check whether all digits are hex or decimal */
+ is_hex = is_digit = TRUE;
+ p = s;
+ while (*p && (is_hex || is_digit)) {
+ if (!g_ascii_isxdigit (*p))
+ is_hex = FALSE;
+ if (!g_ascii_isdigit (*p))
+ is_digit = FALSE;
+ p++, len++;
+ }
+
+ /* Note that some hex strings are also valid digit strings */
+
+ if (is_hex) {
+ if (len == 7 || len == 8) {
+ /* ESN */
+ if (!esn) {
+ if (len == 7)
+ esn = g_strdup_printf ("0%s", s);
+ else
+ esn = g_strdup (s);
+ }
+ } else if (len == 14) {
+ /* MEID */
+ if (!meid)
+ meid = g_strdup (s);
+ }
+ }
+
+ if (is_digit) {
+ if (!is_hex)
+ g_warn_if_fail (expect_hex == FALSE);
+
+ if (len == 15) {
+ if (!imei)
+ imei = g_strdup (s);
+ }
+
+ /* Decimal ESN/MEID unhandled for now; conversion from decimal to
+ * hex isn't a straight dec->hex conversion, as the first 2 digits
+ * of the ESN and first 3 digits of the MEID are the manufacturer
+ * identifier and must be converted separately from serial number
+ * and then concatenated with it.
+ */
+ }
+ }
+ g_strfreev (items);
+
+ success = meid || esn || imei;
+
+ if (out_imei)
+ *out_imei = imei;
+ else
+ g_free (imei);
+
+ if (out_meid)
+ *out_meid = meid;
+ else
+ g_free (meid);
+
+ if (out_esn)
+ *out_esn = esn;
+ else
+ g_free (esn);
+
+ return success;
+}
+
diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h
index 63e53a74..aa646993 100644
--- a/src/mm-modem-helpers.h
+++ b/src/mm-modem-helpers.h
@@ -203,4 +203,9 @@ gint mm_cdma_normalize_class (const gchar *orig_class);
gchar mm_cdma_normalize_band (const gchar *long_band,
gint *out_class);
+gboolean mm_parse_gsn (const char *gsn,
+ gchar **out_imei,
+ gchar **out_meid,
+ gchar **out_esn);
+
#endif /* MM_MODEM_HELPERS_H */
diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c
index 6cfac3db..a6e15db8 100644
--- a/src/tests/test-modem-helpers.c
+++ b/src/tests/test-modem-helpers.c
@@ -1598,6 +1598,71 @@ test_parse_cds (void *f, gpointer d)
"07914356060013F1065A098136395339F6219011700463802190117004638030");
}
+typedef struct {
+ const char *gsn;
+ const char *expected_imei;
+ const char *expected_esn;
+ const char *expected_meid;
+ gboolean expect_success;
+} TestGsnItem;
+
+static void
+test_cdma_parse_gsn (void *f, gpointer d)
+{
+ static const TestGsnItem items[] = {
+ { "0x6744775\r\n", /* leading zeros skipped, no hex digits */
+ NULL,
+ "06744775",
+ NULL,
+ TRUE },
+ { "0x2214A600\r\n",
+ NULL,
+ "2214A600",
+ NULL,
+ TRUE },
+ { "0x80C98A1\r\n", /* leading zeros skipped, some hex digits */
+ NULL,
+ "080C98A1",
+ NULL,
+ TRUE },
+ { "6030C012\r\n", /* no leading 0x */
+ NULL,
+ "6030C012",
+ NULL,
+ TRUE },
+ { "45317471585658170:2161753034\r\n0x00A1000013FB653A:0x80D9BBCA\r\n",
+ NULL,
+ "80D9BBCA",
+ "A1000013FB653A",
+ TRUE },
+ { "354237065082227\r\n", /* GSM IMEI */
+ "354237065082227",
+ NULL, NULL, TRUE },
+ { "356936001568843,NL2A62Z0N5\r\n", /* IMEI + serial number */
+ "356936001568843",
+ NULL, NULL, TRUE },
+ { "adsfasdfasdfasdf", NULL, NULL, FALSE },
+ { "0x6030Cfgh", NULL, NULL, FALSE },
+ { NULL }
+ };
+
+ const TestGsnItem *iter;
+
+ for (iter = &items[0]; iter && iter->gsn; iter++) {
+ char *imei = NULL, *esn = NULL, *meid = NULL;
+ gboolean success;
+
+ success = mm_parse_gsn (iter->gsn, &imei, &meid, &esn);
+ g_assert_cmpint (success, ==, iter->expect_success);
+ g_assert_cmpstr (iter->expected_imei, ==, imei);
+ g_assert_cmpstr (iter->expected_meid, ==, meid);
+ g_assert_cmpstr (iter->expected_esn, ==, esn);
+ g_free (imei);
+ g_free (meid);
+ g_free (esn);
+ }
+}
+
/*****************************************************************************/
void
@@ -1715,9 +1780,10 @@ int main (int argc, char **argv)
g_test_suite_add (suite, TESTCASE (test_parse_operator_id, NULL));
-
g_test_suite_add (suite, TESTCASE (test_parse_cds, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cdma_parse_gsn, NULL));
+
result = g_test_run ();
reg_test_data_free (reg_data);