sim: load emergency numbers from EF_ECC
This commit is contained in:
@@ -973,7 +973,84 @@ mm_base_sim_get_path (MMBaseSim *self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* SIM IDENTIFIER */
|
/* Emergency numbers */
|
||||||
|
|
||||||
|
static GStrv
|
||||||
|
parse_emergency_numbers (const gchar *response,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
guint sw1 = 0;
|
||||||
|
guint sw2 = 0;
|
||||||
|
gchar *hex = 0;
|
||||||
|
GStrv ret;
|
||||||
|
|
||||||
|
if (!mm_3gpp_parse_crsm_response (response, &sw1, &sw2, &hex, error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if ((sw1 == 0x90 && sw2 == 0x00) ||
|
||||||
|
(sw1 == 0x91) ||
|
||||||
|
(sw1 == 0x92) ||
|
||||||
|
(sw1 == 0x9f)) {
|
||||||
|
ret = mm_3gpp_parse_emergency_numbers (hex, error);
|
||||||
|
g_free (hex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (hex);
|
||||||
|
g_set_error (error,
|
||||||
|
MM_CORE_ERROR,
|
||||||
|
MM_CORE_ERROR_FAILED,
|
||||||
|
"SIM failed to handle CRSM request (sw1 %d sw2 %d)",
|
||||||
|
sw1, sw2);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GStrv
|
||||||
|
load_emergency_numbers_finish (MMBaseSim *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
gchar *result;
|
||||||
|
GStrv emergency_numbers;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
result = g_task_propagate_pointer (G_TASK (res), error);
|
||||||
|
if (!result)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
emergency_numbers = parse_emergency_numbers (result, error);
|
||||||
|
g_free (result);
|
||||||
|
|
||||||
|
if (!emergency_numbers)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (i = 0; emergency_numbers[i]; i++)
|
||||||
|
mm_dbg ("loaded emergency number: %s", emergency_numbers[i]);
|
||||||
|
|
||||||
|
return emergency_numbers;
|
||||||
|
}
|
||||||
|
|
||||||
|
STR_REPLY_READY_FN (load_emergency_numbers)
|
||||||
|
|
||||||
|
static void
|
||||||
|
load_emergency_numbers (MMBaseSim *self,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
mm_dbg ("loading emergency numbers...");
|
||||||
|
|
||||||
|
/* READ BINARY of EF_ECC (Emergency Call Codes) ETSI TS 51.011 section 10.3.27 */
|
||||||
|
mm_base_modem_at_command (
|
||||||
|
self->priv->modem,
|
||||||
|
"+CRSM=176,28599,0,0,15",
|
||||||
|
20,
|
||||||
|
FALSE,
|
||||||
|
(GAsyncReadyCallback)load_emergency_numbers_command_ready,
|
||||||
|
g_task_new (self, NULL, callback, user_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* ICCID */
|
||||||
|
|
||||||
static gchar *
|
static gchar *
|
||||||
parse_iccid (const gchar *response,
|
parse_iccid (const gchar *response,
|
||||||
@@ -1784,6 +1861,8 @@ mm_base_sim_class_init (MMBaseSimClass *klass)
|
|||||||
klass->load_operator_identifier_finish = load_operator_identifier_finish;
|
klass->load_operator_identifier_finish = load_operator_identifier_finish;
|
||||||
klass->load_operator_name = load_operator_name;
|
klass->load_operator_name = load_operator_name;
|
||||||
klass->load_operator_name_finish = load_operator_name_finish;
|
klass->load_operator_name_finish = load_operator_name_finish;
|
||||||
|
klass->load_emergency_numbers = load_emergency_numbers;
|
||||||
|
klass->load_emergency_numbers_finish = load_emergency_numbers_finish;
|
||||||
klass->send_pin = send_pin;
|
klass->send_pin = send_pin;
|
||||||
klass->send_pin_finish = common_send_pin_puk_finish;
|
klass->send_pin_finish = common_send_pin_puk_finish;
|
||||||
klass->send_puk = send_puk;
|
klass->send_puk = send_puk;
|
||||||
|
@@ -4352,6 +4352,70 @@ mm_3gpp_parse_operator_id (const gchar *operator_id,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Emergency numbers (+CRSM output) */
|
||||||
|
|
||||||
|
GStrv
|
||||||
|
mm_3gpp_parse_emergency_numbers (const char *raw, GError **error)
|
||||||
|
{
|
||||||
|
gsize rawlen;
|
||||||
|
guint8 *bin;
|
||||||
|
gsize binlen;
|
||||||
|
gsize max_items;
|
||||||
|
GPtrArray *out;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
/* The emergency call code is of a variable length with a maximum length of
|
||||||
|
* 6 digits. Each emergency call code is coded on three bytes, with each
|
||||||
|
* digit within the code being coded on four bits. If a code of less that 6
|
||||||
|
* digits is chosen, then the unused nibbles shall be set to 'F'. */
|
||||||
|
|
||||||
|
rawlen = strlen (raw);
|
||||||
|
if (!rawlen) {
|
||||||
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
|
||||||
|
"empty emergency numbers list");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawlen % 6 != 0) {
|
||||||
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
|
||||||
|
"invalid raw emergency numbers list length: %" G_GSIZE_FORMAT, rawlen);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bin = (guint8 *) mm_utils_hexstr2bin (raw, &binlen);
|
||||||
|
if (!bin) {
|
||||||
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
|
||||||
|
"invalid raw emergency numbers list contents: %s", raw);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
max_items = binlen / 3;
|
||||||
|
out = g_ptr_array_sized_new (max_items + 1);
|
||||||
|
|
||||||
|
for (i = 0; i < max_items; i++) {
|
||||||
|
gchar *number;
|
||||||
|
|
||||||
|
number = mm_bcd_to_string (&bin[i*3], 3);
|
||||||
|
if (number && number[0])
|
||||||
|
g_ptr_array_add (out, number);
|
||||||
|
else
|
||||||
|
g_free (number);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (bin);
|
||||||
|
|
||||||
|
if (!out->len) {
|
||||||
|
g_ptr_array_unref (out);
|
||||||
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
||||||
|
"uninitialized emergency numbers list");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_ptr_array_add (out, NULL);
|
||||||
|
return (GStrv) g_ptr_array_free (out, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
|
@@ -445,6 +445,8 @@ gboolean mm_3gpp_rsrp_level_to_rsrp (guint rsrp_level,
|
|||||||
gboolean mm_3gpp_rssnr_level_to_rssnr (gint rssnr_level,
|
gboolean mm_3gpp_rssnr_level_to_rssnr (gint rssnr_level,
|
||||||
gdouble *out_rssnr);
|
gdouble *out_rssnr);
|
||||||
|
|
||||||
|
GStrv mm_3gpp_parse_emergency_numbers (const char *raw, GError **error);
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* CDMA specific helpers and utilities */
|
/* CDMA specific helpers and utilities */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@@ -4337,6 +4337,55 @@ test_clcc_response_multiple (void)
|
|||||||
common_test_clcc_response (response, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list));
|
common_test_clcc_response (response, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Test +CRSM EF_ECC read data parsing */
|
||||||
|
|
||||||
|
#define MAX_EMERGENCY_NUMBERS 5
|
||||||
|
typedef struct {
|
||||||
|
const gchar *raw;
|
||||||
|
guint n_numbers;
|
||||||
|
gchar *numbers[MAX_EMERGENCY_NUMBERS];
|
||||||
|
} EmergencyNumbersTest;
|
||||||
|
|
||||||
|
static const EmergencyNumbersTest emergency_numbers_tests[] = {
|
||||||
|
{ "", 0 },
|
||||||
|
{ "FFF", 0 },
|
||||||
|
{ "FFFFFF" "FFFFFF" "FFFFFF" "FFFFFF" "FFFFFF", 0 },
|
||||||
|
{ "00F0FF" "11F2FF" "88F8FF", 3, { "000", "112", "888" } },
|
||||||
|
{ "00F0FF" "11F2FF" "88F8FF" "FFFFFF" "FFFFFF", 3, { "000", "112", "888" } },
|
||||||
|
{ "00F0FF" "11F2FF" "88F8FF" "214365" "08FFFF", 5, { "000", "112", "888", "123456", "80" } },
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_emergency_numbers (void)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS (emergency_numbers_tests); i++) {
|
||||||
|
GStrv numbers;
|
||||||
|
GError *error = NULL;
|
||||||
|
guint j;
|
||||||
|
|
||||||
|
g_debug (" testing %s...", emergency_numbers_tests[i].raw);
|
||||||
|
|
||||||
|
numbers = mm_3gpp_parse_emergency_numbers (emergency_numbers_tests[i].raw, &error);
|
||||||
|
if (!emergency_numbers_tests[i].n_numbers) {
|
||||||
|
g_assert (error);
|
||||||
|
g_assert (!numbers);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert (numbers);
|
||||||
|
|
||||||
|
g_assert_cmpuint (emergency_numbers_tests[i].n_numbers, ==, g_strv_length (numbers));
|
||||||
|
for (j = 0; j < emergency_numbers_tests[i].n_numbers; j++)
|
||||||
|
g_assert_cmpstr (emergency_numbers_tests[i].numbers[j], ==, numbers[j]);
|
||||||
|
|
||||||
|
g_strfreev (numbers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -4659,6 +4708,8 @@ int main (int argc, char **argv)
|
|||||||
g_test_suite_add (suite, TESTCASE (test_clcc_response_single_long, NULL));
|
g_test_suite_add (suite, TESTCASE (test_clcc_response_single_long, NULL));
|
||||||
g_test_suite_add (suite, TESTCASE (test_clcc_response_multiple, NULL));
|
g_test_suite_add (suite, TESTCASE (test_clcc_response_multiple, NULL));
|
||||||
|
|
||||||
|
g_test_suite_add (suite, TESTCASE (test_emergency_numbers, NULL));
|
||||||
|
|
||||||
g_test_suite_add (suite, TESTCASE (test_parse_uint_list, NULL));
|
g_test_suite_add (suite, TESTCASE (test_parse_uint_list, NULL));
|
||||||
|
|
||||||
g_test_suite_add (suite, TESTCASE (test_bcd_to_string, NULL));
|
g_test_suite_add (suite, TESTCASE (test_bcd_to_string, NULL));
|
||||||
|
Reference in New Issue
Block a user