diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-broadband-modem.c | 81 | ||||
-rw-r--r-- | src/mm-modem-helpers.c | 121 | ||||
-rw-r--r-- | src/mm-modem-helpers.h | 5 | ||||
-rw-r--r-- | src/tests/test-modem-helpers.c | 68 |
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); |