broadband-modem,helpers: implement AT+WS46=? response parser
We want a parser that returns the full list of combinations found.
This commit is contained in:
@@ -378,17 +378,28 @@ current_capabilities_ws46_test_ready (MMBaseModem *self,
|
||||
LoadCapabilitiesContext *ctx)
|
||||
{
|
||||
const gchar *response;
|
||||
GArray *modes;
|
||||
guint i;
|
||||
|
||||
/* Completely ignore errors in AT+WS46=? */
|
||||
response = mm_base_modem_at_command_finish (self, res, NULL);
|
||||
if (response &&
|
||||
(strstr (response, "28") != NULL || /* 4G only */
|
||||
strstr (response, "30") != NULL || /* 2G/4G */
|
||||
strstr (response, "31") != NULL)) { /* 3G/4G */
|
||||
/* Add LTE caps */
|
||||
ctx->caps |= MM_MODEM_CAPABILITY_LTE;
|
||||
}
|
||||
if (!response)
|
||||
goto out;
|
||||
|
||||
modes = mm_3gpp_parse_ws46_test_response (response, NULL);
|
||||
if (!modes)
|
||||
goto out;
|
||||
|
||||
/* Add LTE caps if any of the reported modes supports 4G */
|
||||
for (i = 0; i < modes->len; i++) {
|
||||
if (g_array_index (modes, MMModemMode, i) & MM_MODEM_MODE_4G) {
|
||||
ctx->caps |= MM_MODEM_CAPABILITY_LTE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_array_unref (modes);
|
||||
|
||||
out:
|
||||
g_simple_async_result_set_op_res_gpointer (
|
||||
ctx->result,
|
||||
GUINT_TO_POINTER (ctx->caps),
|
||||
@@ -1430,78 +1441,40 @@ supported_modes_ws46_test_ready (MMBroadbandModem *self,
|
||||
LoadSupportedModesContext *ctx)
|
||||
{
|
||||
const gchar *response;
|
||||
GError *error = NULL;
|
||||
GArray *modes;
|
||||
GError *error = NULL;
|
||||
guint i;
|
||||
|
||||
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
|
||||
if (!error) {
|
||||
MMModemMode mode = MM_MODEM_MODE_NONE;
|
||||
|
||||
/*
|
||||
* More than one numeric ID may appear in the list, that's why
|
||||
* they are checked separately.
|
||||
*
|
||||
* NOTE: Do not skip WS46 prefix; it would break Cinterion handling.
|
||||
*
|
||||
* From 3GPP TS 27.007 v.11.2.0, section 5.9
|
||||
* 12 GSM Digital Cellular Systems (GERAN only)
|
||||
* 22 UTRAN only
|
||||
* 25 3GPP Systems (GERAN, UTRAN and E-UTRAN)
|
||||
* 28 E-UTRAN only
|
||||
* 29 GERAN and UTRAN
|
||||
* 30 GERAN and E-UTRAN
|
||||
* 31 UTRAN and E-UTRAN
|
||||
*/
|
||||
|
||||
if (strstr (response, "12") != NULL) {
|
||||
mm_dbg ("Device allows (3GPP) 2G-only network mode");
|
||||
mode |= MM_MODEM_MODE_2G;
|
||||
}
|
||||
|
||||
if (strstr (response, "22") != NULL) {
|
||||
mm_dbg ("Device allows (3GPP) 3G-only network mode");
|
||||
mode |= MM_MODEM_MODE_3G;
|
||||
}
|
||||
|
||||
if (strstr (response, "28") != NULL) {
|
||||
mm_dbg ("Device allows (3GPP) 4G-only network mode");
|
||||
mode |= MM_MODEM_MODE_4G;
|
||||
}
|
||||
|
||||
if (strstr (response, "29") != NULL) {
|
||||
mm_dbg ("Device allows (3GPP) 2G/3G network mode");
|
||||
mode |= (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
|
||||
}
|
||||
|
||||
if (strstr (response, "30") != NULL) {
|
||||
mm_dbg ("Device allows (3GPP) 2G/4G network mode");
|
||||
mode |= (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G);
|
||||
}
|
||||
|
||||
if (strstr (response, "31") != NULL) {
|
||||
mm_dbg ("Device allows (3GPP) 3G/4G network mode");
|
||||
mode |= (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
|
||||
}
|
||||
|
||||
if (strstr (response, "25") != NULL) {
|
||||
if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) {
|
||||
mm_dbg ("Device allows every supported 3GPP network mode (2G/3G/4G)");
|
||||
mode |= (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
|
||||
} else {
|
||||
mm_dbg ("Device allows every supported 3GPP network mode (2G/3G)");
|
||||
mode |= (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
|
||||
}
|
||||
}
|
||||
|
||||
/* If no expected ID found, log error */
|
||||
if (mode == MM_MODEM_MODE_NONE)
|
||||
mm_dbg ("Invalid list of supported networks reported by WS46=?: '%s'", response);
|
||||
else
|
||||
ctx->mode |= mode;
|
||||
} else {
|
||||
if (error) {
|
||||
mm_dbg ("Generic query of supported 3GPP networks with WS46=? failed: '%s'", error->message);
|
||||
g_error_free (error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
modes = mm_3gpp_parse_ws46_test_response (response, &error);
|
||||
if (!modes) {
|
||||
mm_dbg ("Parsing WS46=? response failed: '%s'", error->message);
|
||||
g_error_free (error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < modes->len; i++) {
|
||||
MMModemMode mode;
|
||||
gchar *str;
|
||||
|
||||
mode = g_array_index (modes, MMModemMode, i);
|
||||
|
||||
ctx->mode |= mode;
|
||||
|
||||
str = mm_modem_mode_build_string_from_mask (mode);
|
||||
mm_dbg ("Device allows (3GPP) mode combination: %s", str);
|
||||
g_free (str);
|
||||
}
|
||||
|
||||
g_array_unref (modes);
|
||||
|
||||
out:
|
||||
/* Now keep on with the loading, we may need CDMA-specific checks */
|
||||
ctx->run_ws46 = FALSE;
|
||||
load_supported_modes_step (ctx);
|
||||
|
@@ -724,6 +724,149 @@ mm_3gpp_cds_regex_get (void)
|
||||
NULL);
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
/* AT+WS46=? response parser
|
||||
*
|
||||
* More than one numeric ID may appear in the list, that's why
|
||||
* they are checked separately.
|
||||
*
|
||||
* NOTE: ignore WS46 prefix or it will break Cinterion handling.
|
||||
*
|
||||
* For the specific case of '25', we will check if any other mode supports
|
||||
* 4G, and if there is none, we'll remove 4G caps from it.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
guint ws46;
|
||||
MMModemMode mode;
|
||||
} Ws46Mode;
|
||||
|
||||
/* 3GPP TS 27.007 r14, section 5.9: select wireless network +WS46 */
|
||||
static const Ws46Mode ws46_modes[] = {
|
||||
/* GSM Digital Cellular Systems (GERAN only) */
|
||||
{ 12, MM_MODEM_MODE_2G },
|
||||
/* UTRAN only */
|
||||
{ 22, MM_MODEM_MODE_3G },
|
||||
/* 3GPP Systems (GERAN, UTRAN and E-UTRAN) */
|
||||
{ 25, MM_MODEM_MODE_ANY },
|
||||
/* E-UTRAN only */
|
||||
{ 28, MM_MODEM_MODE_4G },
|
||||
/* GERAN and UTRAN */
|
||||
{ 29, MM_MODEM_MODE_2G | MM_MODEM_MODE_3G },
|
||||
/* GERAN and E-UTRAN */
|
||||
{ 30, MM_MODEM_MODE_2G | MM_MODEM_MODE_4G },
|
||||
/* UERAN and E-UTRAN */
|
||||
{ 31, MM_MODEM_MODE_3G | MM_MODEM_MODE_4G },
|
||||
};
|
||||
|
||||
GArray *
|
||||
mm_3gpp_parse_ws46_test_response (const gchar *response,
|
||||
GError **error)
|
||||
{
|
||||
GArray *modes = NULL;
|
||||
GRegex *r;
|
||||
GError *inner_error = NULL;
|
||||
GMatchInfo *match_info = NULL;
|
||||
gchar *full_list = NULL;
|
||||
gchar **split;
|
||||
guint i;
|
||||
gboolean supported_4g = FALSE;
|
||||
gboolean supported_3g = FALSE;
|
||||
gboolean supported_2g = FALSE;
|
||||
|
||||
r = g_regex_new ("(?:\\+WS46:)?\\s*\\((.*)\\)(?:\\r\\n)?", 0, 0, NULL);
|
||||
g_assert (r != NULL);
|
||||
|
||||
g_regex_match_full (r, response, strlen (response), 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 response");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(full_list = mm_get_string_unquoted_from_match_info (match_info, 1))) {
|
||||
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing full list string");
|
||||
goto out;
|
||||
}
|
||||
|
||||
split = g_strsplit (full_list, ",", -1);
|
||||
modes = g_array_new (FALSE, FALSE, sizeof (MMModemMode));
|
||||
|
||||
for (i = 0; split && split[i]; i++) {
|
||||
guint val;
|
||||
guint j;
|
||||
|
||||
if (!mm_get_uint_from_str (split[i], &val)) {
|
||||
g_warning ("Invalid +WS46 mode reported: %s", split[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; j < G_N_ELEMENTS (ws46_modes); j++) {
|
||||
if (ws46_modes[j].ws46 == val) {
|
||||
if (val != 25) {
|
||||
if (ws46_modes[j].mode & MM_MODEM_MODE_4G)
|
||||
supported_4g = TRUE;
|
||||
if (ws46_modes[j].mode & MM_MODEM_MODE_3G)
|
||||
supported_3g = TRUE;
|
||||
if (ws46_modes[j].mode & MM_MODEM_MODE_2G)
|
||||
supported_2g = TRUE;
|
||||
}
|
||||
g_array_append_val (modes, ws46_modes[j].mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j == G_N_ELEMENTS (ws46_modes))
|
||||
g_warning ("Unknown +WS46 mode reported: %s", split[i]);
|
||||
}
|
||||
|
||||
g_strfreev (split);
|
||||
|
||||
if (modes->len == 0) {
|
||||
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No valid modes reported");
|
||||
g_clear_pointer (&modes, g_array_unref);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Fixup the ANY value, based on which are the supported modes */
|
||||
for (i = 0; i < modes->len; i++) {
|
||||
MMModemMode *mode;
|
||||
|
||||
mode = &g_array_index (modes, MMModemMode, i);
|
||||
if (*mode == MM_MODEM_MODE_ANY) {
|
||||
*mode = 0;
|
||||
if (supported_2g)
|
||||
*mode |= MM_MODEM_MODE_2G;
|
||||
if (supported_3g)
|
||||
*mode |= MM_MODEM_MODE_3G;
|
||||
if (supported_4g)
|
||||
*mode |= MM_MODEM_MODE_4G;
|
||||
|
||||
if (*mode == 0) {
|
||||
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No way to fixup the ANY value");
|
||||
g_clear_pointer (&modes, g_array_unref);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
g_free (full_list);
|
||||
|
||||
g_clear_pointer (&match_info, g_match_info_free);
|
||||
g_regex_unref (r);
|
||||
|
||||
if (inner_error) {
|
||||
g_propagate_error (error, inner_error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_assert (modes && modes->len);
|
||||
return modes;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
static void
|
||||
|
@@ -106,6 +106,9 @@ GRegex *mm_3gpp_cusd_regex_get (void);
|
||||
GRegex *mm_3gpp_cmti_regex_get (void);
|
||||
GRegex *mm_3gpp_cds_regex_get (void);
|
||||
|
||||
/* AT+WS46=? response parser: returns array of MMModemMode values */
|
||||
GArray *mm_3gpp_parse_ws46_test_response (const gchar *response,
|
||||
GError **error);
|
||||
|
||||
/* AT+COPS=? (network scan) response parser */
|
||||
typedef struct {
|
||||
|
@@ -32,6 +32,102 @@
|
||||
#define g_assert_cmpfloat_tolerance(val1, val2, tolerance) \
|
||||
g_assert_cmpfloat (fabs (val1 - val2), <, tolerance)
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Test WS46=? responses */
|
||||
|
||||
static void
|
||||
test_ws46_response (const gchar *str,
|
||||
const MMModemMode *expected,
|
||||
guint n_expected)
|
||||
{
|
||||
guint i;
|
||||
GArray *modes;
|
||||
GError *error = NULL;
|
||||
|
||||
modes = mm_3gpp_parse_ws46_test_response (str, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert (modes != NULL);
|
||||
g_assert_cmpuint (modes->len, ==, n_expected);
|
||||
|
||||
for (i = 0; i < n_expected; i++) {
|
||||
guint j;
|
||||
|
||||
for (j = 0; j < modes->len; j++) {
|
||||
if (expected[i] == g_array_index (modes, MMModemMode, j))
|
||||
break;
|
||||
}
|
||||
g_assert_cmpuint (j, !=, modes->len);
|
||||
}
|
||||
g_array_unref (modes);
|
||||
}
|
||||
|
||||
static void
|
||||
test_ws46_response_generic_2g3g4g (void)
|
||||
{
|
||||
static const MMModemMode expected[] = {
|
||||
MM_MODEM_MODE_2G,
|
||||
MM_MODEM_MODE_3G,
|
||||
MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
||||
MM_MODEM_MODE_4G,
|
||||
MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
|
||||
};
|
||||
const gchar *str = "+WS46: (12,22,25,28,29)";
|
||||
|
||||
test_ws46_response (str, expected, G_N_ELEMENTS (expected));
|
||||
}
|
||||
|
||||
static void
|
||||
test_ws46_response_generic_2g3g (void)
|
||||
{
|
||||
static const MMModemMode expected[] = {
|
||||
MM_MODEM_MODE_2G,
|
||||
MM_MODEM_MODE_3G,
|
||||
MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
|
||||
};
|
||||
const gchar *str = "+WS46: (12,22,25)";
|
||||
|
||||
test_ws46_response (str, expected, G_N_ELEMENTS (expected));
|
||||
}
|
||||
|
||||
static void
|
||||
test_ws46_response_generic_2g3g_v2 (void)
|
||||
{
|
||||
static const MMModemMode expected[] = {
|
||||
MM_MODEM_MODE_2G,
|
||||
MM_MODEM_MODE_3G,
|
||||
MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
|
||||
};
|
||||
const gchar *str = "+WS46: (12,22,29)";
|
||||
|
||||
test_ws46_response (str, expected, G_N_ELEMENTS (expected));
|
||||
}
|
||||
|
||||
static void
|
||||
test_ws46_response_cinterion (void)
|
||||
{
|
||||
static const MMModemMode expected[] = {
|
||||
MM_MODEM_MODE_2G,
|
||||
MM_MODEM_MODE_3G,
|
||||
MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
||||
MM_MODEM_MODE_4G,
|
||||
MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
|
||||
};
|
||||
const gchar *str = "(12,22,25,28,29)";
|
||||
|
||||
test_ws46_response (str, expected, G_N_ELEMENTS (expected));
|
||||
}
|
||||
|
||||
static void
|
||||
test_ws46_response_telit_le866 (void)
|
||||
{
|
||||
static const MMModemMode expected[] = {
|
||||
MM_MODEM_MODE_4G,
|
||||
};
|
||||
const gchar *str = "(28)";
|
||||
|
||||
test_ws46_response (str, expected, G_N_ELEMENTS (expected));
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Test CMGL responses */
|
||||
|
||||
@@ -3392,6 +3488,12 @@ int main (int argc, char **argv)
|
||||
suite = g_test_get_root ();
|
||||
reg_data = reg_test_data_new ();
|
||||
|
||||
g_test_suite_add (suite, TESTCASE (test_ws46_response_generic_2g3g4g, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_ws46_response_generic_2g3g, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_ws46_response_generic_2g3g_v2, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_ws46_response_cinterion, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_ws46_response_telit_le866, NULL));
|
||||
|
||||
g_test_suite_add (suite, TESTCASE (test_cops_response_tm506, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_cops_response_gt3gplus, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_cops_response_ac881, NULL));
|
||||
|
Reference in New Issue
Block a user