broadband-modem: parse +GSN response for IMEI, MEID, and ESN (bgo #696596)

+GSN response differs widely between modems.  Some prefix the
MEID and/or ESN with 0x, some have leading zeros, some return the
MEID and the ESN, and some append the serial number to the end of
the IMEI.  Handle that and make the ESN, MEID, IMEI, and
EquipmentIdentifier parsing consistent.
This commit is contained in:
Dan Williams
2013-03-25 13:20:13 -05:00
parent f299a05571
commit 3b3326bdf0
4 changed files with 263 additions and 12 deletions

View File

@@ -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;
}
@@ -5912,6 +5933,42 @@ modem_cdma_load_esn (MMIfaceModemCdma *self,
user_data);
}
/*****************************************************************************/
/* 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) */
@@ -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;

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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);