diff options
-rw-r--r-- | src/mm-base-sim.c | 81 | ||||
-rw-r--r-- | src/mm-modem-helpers.c | 64 | ||||
-rw-r--r-- | src/mm-modem-helpers.h | 2 | ||||
-rw-r--r-- | src/tests/test-modem-helpers.c | 51 |
4 files changed, 197 insertions, 1 deletions
diff --git a/src/mm-base-sim.c b/src/mm-base-sim.c index 1bd6a5c5..acb44125 100644 --- a/src/mm-base-sim.c +++ b/src/mm-base-sim.c @@ -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 * 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_name = load_operator_name; 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_finish = common_send_pin_puk_finish; klass->send_puk = send_puk; diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c index 69e8823e..ec07aada 100644 --- a/src/mm-modem-helpers.c +++ b/src/mm-modem-helpers.c @@ -4353,6 +4353,70 @@ mm_3gpp_parse_operator_id (const gchar *operator_id, } /*************************************************************************/ +/* 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 mm_cdma_parse_spservice_read_response (const gchar *reply, diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h index 73a13ddd..28a69996 100644 --- a/src/mm-modem-helpers.h +++ b/src/mm-modem-helpers.h @@ -445,6 +445,8 @@ gboolean mm_3gpp_rsrp_level_to_rsrp (guint rsrp_level, gboolean mm_3gpp_rssnr_level_to_rssnr (gint rssnr_level, gdouble *out_rssnr); +GStrv mm_3gpp_parse_emergency_numbers (const char *raw, GError **error); + /*****************************************************************************/ /* CDMA specific helpers and utilities */ /*****************************************************************************/ diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c index ea693079..0e63e41f 100644 --- a/src/tests/test-modem-helpers.c +++ b/src/tests/test-modem-helpers.c @@ -4338,6 +4338,55 @@ test_clcc_response_multiple (void) } /*****************************************************************************/ +/* 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 { gchar *str; @@ -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_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_bcd_to_string, NULL)); |