modem-helpers: Add helpers to parse channel lists
`CSCB` has the form ``` CSCB: [0|1],"<channel-list>","<data-coding-scheme>" ``` If the first parameter is `0` this specifies the accepted types if `1` it specifies rejected. So far seen in the wild were CSCB strings with accepted types only and a coding scheme of "" so this is what we handle for the moment. Signed-off-by: Guido Günther <agx@sigxcpu.org>
This commit is contained in:
committed by
Dan Williams
parent
0c20d13b30
commit
d8135be6e2
@@ -2936,6 +2936,110 @@ out:
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
#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 */
|
/* CGATT helpers */
|
||||||
|
|
||||||
@@ -5200,6 +5304,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
|
gboolean
|
||||||
mm_sim_parse_cpol_query_response (const gchar *response,
|
mm_sim_parse_cpol_query_response (const gchar *response,
|
||||||
guint *out_index,
|
guint *out_index,
|
||||||
|
|||||||
@@ -418,6 +418,8 @@ gboolean mm_3gpp_parse_ccwa_service_query_response (const gchar *response,
|
|||||||
gboolean *status,
|
gboolean *status,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
|
GArray *mm_3gpp_parse_cscb_response (const char *raw, GError **error);
|
||||||
|
|
||||||
/* CGATT helpers */
|
/* CGATT helpers */
|
||||||
gchar *mm_3gpp_build_cgatt_set_request (MMModem3gppPacketServiceState state);
|
gchar *mm_3gpp_build_cgatt_set_request (MMModem3gppPacketServiceState state);
|
||||||
|
|
||||||
@@ -550,6 +552,9 @@ gboolean mm_parse_supl_address (const gchar *supl,
|
|||||||
guint16 *out_port,
|
guint16 *out_port,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
|
gboolean mm_validate_cbs_channels (GArray *channels,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* SIM specific helpers and utilities */
|
/* SIM specific helpers and utilities */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|||||||
@@ -4277,6 +4277,100 @@ test_ccwa_response (void)
|
|||||||
common_test_ccwa_response (test_ccwa[i].response, test_ccwa[i].expected_status, test_ccwa[i].expected_error);
|
common_test_ccwa_response (test_ccwa[i].response, test_ccwa[i].expected_status, test_ccwa[i].expected_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* 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 */
|
/* Test +CLCC URCs */
|
||||||
|
|
||||||
@@ -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_indication, NULL));
|
||||||
g_test_suite_add (suite, TESTCASE (test_ccwa_response, 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_empty, NULL));
|
||||||
g_test_suite_add (suite, TESTCASE (test_clcc_response_single, NULL));
|
g_test_suite_add (suite, TESTCASE (test_clcc_response_single, NULL));
|
||||||
g_test_suite_add (suite, TESTCASE (test_clcc_response_single_long, NULL));
|
g_test_suite_add (suite, TESTCASE (test_clcc_response_single_long, NULL));
|
||||||
|
|||||||
Reference in New Issue
Block a user