diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-modem-helpers.c | 136 | ||||
-rw-r--r-- | src/mm-modem-helpers.h | 5 | ||||
-rw-r--r-- | src/tests/test-modem-helpers.c | 96 |
3 files changed, 237 insertions, 0 deletions
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c index 772a2de8..4f0c6248 100644 --- a/src/mm-modem-helpers.c +++ b/src/mm-modem-helpers.c @@ -2937,6 +2937,110 @@ out: } /*************************************************************************/ + +#define CBS_MAX_CHANNEL G_MAXUINT16 + +GArray * +mm_3gpp_parse_cscb_response (const char *response, GError **error) +{ + g_autoptr(GRegex) r = NULL; + g_autoptr(GMatchInfo) match_info = NULL; + GError *inner_error = NULL; + gsize len; + g_autoptr (GArray) array = g_array_new (FALSE, FALSE, sizeof (MMCellBroadcastChannels)); + g_autofree char *str = NULL; + g_auto (GStrv) intervals = NULL; + int i; + + len = strlen (response); + if (!len) { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "empty channel list"); + goto out; + } + + /* + * AT+CSCB=[0|1],"<channels>","<coding-scheme>" + */ + r = g_regex_new ("\\+CSCB:\\s*" + "(\\d),\\s*" /* [0|1] */ + "\"([\\d,\\-]*)\"," /* channel list */ + "\"\"", /* encodings */ + G_REGEX_NEWLINE_CRLF, + 0, + NULL); + g_assert (r != NULL); + + g_regex_match_full (r, response, -1, 0, 0, &match_info, &inner_error); + if (inner_error) + goto out; + + if (!g_match_info_matches (match_info)) { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match CSCB response"); + goto out; + } + + str = g_match_info_fetch (match_info, 1); + if (!g_str_equal (str, "0")) { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't match type of CSCB response: '%s'", str); + goto out; + } + + str = g_match_info_fetch (match_info, 2); + intervals = g_strsplit (str, ",", -1); + for (i = 0; intervals[i]; i++) { + gchar *interval_separator; + + g_strstrip (intervals[i]); + interval_separator = strstr (intervals[i], "-"); + if (interval_separator) { + /* Add an interval */ + gchar *end; + g_autofree gchar *start = NULL; + MMCellBroadcastChannels channels; + + start = g_strdup (intervals[i]); + interval_separator = strstr (start, "-"); + *(interval_separator++) = '\0'; + end = interval_separator; + + if (mm_get_uint_from_str (start, &channels.start) && + mm_get_uint_from_str (end, &channels.end) && + channels.start <= channels.end && + channels.end <= CBS_MAX_CHANNEL) + g_array_append_val (array, channels); + else { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't parse CSCB interval '%s'", intervals[i]); + goto out; + } + } else { + guint channel; + + /* Add single value */ + if (mm_get_uint_from_str (intervals[i], &channel)) { + MMCellBroadcastChannels channels = { + .start = channel, + .end = channel + }; + g_array_append_val (array, channels); + } else { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't parse CSCB value '%s'", intervals[i]); + goto out; + } + } + } + + return g_steal_pointer (&array); + + out: + g_assert (inner_error); + g_propagate_error (error, inner_error); + return NULL; +} + +/*************************************************************************/ /* CGATT helpers */ gchar * @@ -5201,6 +5305,38 @@ out: /*****************************************************************************/ gboolean +mm_validate_cbs_channels (GArray *channels, GError **error) +{ + guint i; + + for (i = 0; i < channels->len; i++) { + MMCellBroadcastChannels ch = g_array_index (channels, MMCellBroadcastChannels, i); + + if (ch.end < ch.start) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, + "Invalid channels: End channel smaller than start channel"); + return FALSE; + } + + if (ch.start > CBS_MAX_CHANNEL) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, + "Invalid channels: Start channel %u too large", ch.start); + return FALSE; + } + + if (ch.end > CBS_MAX_CHANNEL) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, + "Invalid channels: End channel %u too large", ch.end); + return FALSE; + } + } + + return TRUE; +} + +/*****************************************************************************/ + +gboolean mm_sim_parse_cpol_query_response (const gchar *response, guint *out_index, gchar **out_operator_code, diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h index f4b2ae8b..133c7006 100644 --- a/src/mm-modem-helpers.h +++ b/src/mm-modem-helpers.h @@ -418,6 +418,8 @@ gboolean mm_3gpp_parse_ccwa_service_query_response (const gchar *response, gboolean *status, GError **error); +GArray *mm_3gpp_parse_cscb_response (const char *raw, GError **error); + /* CGATT helpers */ gchar *mm_3gpp_build_cgatt_set_request (MMModem3gppPacketServiceState state); @@ -550,6 +552,9 @@ gboolean mm_parse_supl_address (const gchar *supl, guint16 *out_port, GError **error); +gboolean mm_validate_cbs_channels (GArray *channels, + GError **error); + /*****************************************************************************/ /* SIM specific helpers and utilities */ /*****************************************************************************/ diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c index 538aedfe..6692e56d 100644 --- a/src/tests/test-modem-helpers.c +++ b/src/tests/test-modem-helpers.c @@ -4278,6 +4278,100 @@ test_ccwa_response (void) } /*****************************************************************************/ +/* Test +CSCB channel lists */ + +typedef struct { + const gchar *response; + const MMCellBroadcastChannels *channels; + guint len; + gboolean error; +} TestCscb; + +static void +common_test_cscb_response (const gchar *response, TestCscb *expected) +{ + GError *error = NULL; + GArray *result; + + g_debug ("Testing '%s'", response); + result = mm_3gpp_parse_cscb_response (response, &error); + + if (expected->error) { + g_assert (!result); + g_assert_nonnull (error); + g_error_free (error); + } else { + guint i; + + g_assert_no_error (error); + g_assert (result); + g_assert_cmpint (result->len, ==, expected->len); + for (i = 0; i < expected->len; i++) { + MMCellBroadcastChannels ch = g_array_index (result, MMCellBroadcastChannels, i); + + g_assert_cmpuint (ch.start, ==, expected->channels[i].start); + g_assert_cmpuint (ch.end, ==, expected->channels[i].end); + } + } +} + +static const MMCellBroadcastChannels cscb_one_channel[] = { + { .start = 0, .end = 0 }, +}; + +static const MMCellBroadcastChannels cscb_all_channels[] = { + { .start = 0, .end = 65535 }, +}; + +static const MMCellBroadcastChannels cscb_interval_channels[] = { + { .start = 0, .end = 1 }, + { .start = 100, .end = 200 }, +}; + +static const MMCellBroadcastChannels cscb_dell5821e_channels[] = { + { .start = 4383, .end = 4383 }, + { .start = 4400, .end = 4400 }, + { .start = 4370, .end = 4370 }, + { .start = 4371, .end = 4378 }, + { .start = 4384, .end = 4391 }, + { .start = 4396, .end = 4397 }, +}; + +static TestCscb test_cscb[] = { + { "\r\n+CSCB: 0, \"0\",\"\"\r\n\r\nOK\r\n", /* one channel */ + cscb_one_channel, + G_N_ELEMENTS (cscb_all_channels), + FALSE }, + { "+CSCB: 0,\"0-65535\",\"\"\r\n", /* all channels */ + cscb_all_channels, + G_N_ELEMENTS (cscb_all_channels), + FALSE }, + { "+CSCB: 0,\"0-1,100-200\",\"\"\r\n", /* intervals */ + cscb_interval_channels, + G_N_ELEMENTS (cscb_interval_channels), + FALSE }, + /* Dell 5821e/T77W968 defaults */ + { "+CSCB: 0, \"4383,4400,4370,4371-4378,4384-4391,4396-4397\",\"\"", + cscb_dell5821e_channels, + G_N_ELEMENTS (cscb_dell5821e_channels), + FALSE }, + { "+CSCB: 0,\"0-\",\"\"\r\n", /* broken interval */ + NULL, + 0, + TRUE }, +}; + +static void +test_cscb_response (void) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (test_cscb); i++) + common_test_cscb_response (test_cscb[i].response, &test_cscb[i]); +} + + +/*****************************************************************************/ /* Test +CLCC URCs */ static void @@ -4994,6 +5088,8 @@ int main (int argc, char **argv) g_test_suite_add (suite, TESTCASE (test_ccwa_indication, NULL)); g_test_suite_add (suite, TESTCASE (test_ccwa_response, NULL)); + g_test_suite_add (suite, TESTCASE (test_cscb_response, NULL)); + g_test_suite_add (suite, TESTCASE (test_clcc_response_empty, NULL)); g_test_suite_add (suite, TESTCASE (test_clcc_response_single, NULL)); g_test_suite_add (suite, TESTCASE (test_clcc_response_single_long, NULL)); |