The behavior of GRegex changed in 2.73.2 once it was ported from pcre1 to pcre2. In some cases it was made more strict, which is fine, in other cases it exposed some change in how it behaves on certain matches that is not extremely clear whether it's ok or not. See https://gitlab.gnome.org/GNOME/glib/-/issues/2729 See https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/601 See https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/621 Either way, one thing that was assumed was that initializing all GRegex/GMatchInfo variables to NULL and making sure they're NULL before they're initialized by glib (especially the GMatchInfo) was a good and safer approach. So, whenever possible, g_autoptr() is used to cleanup the allocated GMatchInfo/GRegex variables, and otherwise, g_clear_pointer() is used to ensure that no free/unref is attempted unless the given variable is not NULL, and also so that the variable is reseted to NULL after being disposed.
2032 lines
74 KiB
C
2032 lines
74 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details:
|
|
*
|
|
* Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
|
|
*/
|
|
|
|
#include <glib.h>
|
|
#include <string.h>
|
|
|
|
#include "mm-log.h"
|
|
#include "mm-modem-helpers.h"
|
|
#include "mm-modem-helpers-ublox.h"
|
|
|
|
/*****************************************************************************/
|
|
/* +UPINCNT response parser */
|
|
|
|
gboolean
|
|
mm_ublox_parse_upincnt_response (const gchar *response,
|
|
guint *out_pin_attempts,
|
|
guint *out_pin2_attempts,
|
|
guint *out_puk_attempts,
|
|
guint *out_puk2_attempts,
|
|
GError **error)
|
|
{
|
|
g_autoptr(GRegex) r = NULL;
|
|
g_autoptr(GMatchInfo) match_info = NULL;
|
|
GError *inner_error = NULL;
|
|
guint pin_attempts = 0;
|
|
guint pin2_attempts = 0;
|
|
guint puk_attempts = 0;
|
|
guint puk2_attempts = 0;
|
|
gboolean success = TRUE;
|
|
|
|
g_assert (out_pin_attempts);
|
|
g_assert (out_pin2_attempts);
|
|
g_assert (out_puk_attempts);
|
|
g_assert (out_puk2_attempts);
|
|
|
|
/* Response may be e.g.:
|
|
* +UPINCNT: 3,3,10,10
|
|
*/
|
|
r = g_regex_new ("\\+UPINCNT: (\\d+),(\\d+),(\\d+),(\\d+)(?:\\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 && g_match_info_matches (match_info)) {
|
|
if (!mm_get_uint_from_match_info (match_info, 1, &pin_attempts)) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
|
"Couldn't parse PIN attempts");
|
|
goto out;
|
|
}
|
|
if (!mm_get_uint_from_match_info (match_info, 2, &pin2_attempts)) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
|
"Couldn't parse PIN2 attempts");
|
|
goto out;
|
|
}
|
|
if (!mm_get_uint_from_match_info (match_info, 3, &puk_attempts)) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
|
"Couldn't parse PUK attempts");
|
|
goto out;
|
|
}
|
|
if (!mm_get_uint_from_match_info (match_info, 4, &puk2_attempts)) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
|
"Couldn't parse PUK2 attempts");
|
|
goto out;
|
|
}
|
|
success = TRUE;
|
|
}
|
|
|
|
out:
|
|
|
|
if (inner_error) {
|
|
g_propagate_error (error, inner_error);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!success) {
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Couldn't parse +UPINCNT response: '%s'", response);
|
|
return FALSE;
|
|
}
|
|
|
|
*out_pin_attempts = pin_attempts;
|
|
*out_pin2_attempts = pin2_attempts;
|
|
*out_puk_attempts = puk_attempts;
|
|
*out_puk2_attempts = puk2_attempts;
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* UUSBCONF? response parser */
|
|
|
|
gboolean
|
|
mm_ublox_parse_uusbconf_response (const gchar *response,
|
|
MMUbloxUsbProfile *out_profile,
|
|
GError **error)
|
|
{
|
|
g_autoptr(GRegex) r = NULL;
|
|
g_autoptr(GMatchInfo) match_info = NULL;
|
|
GError *inner_error = NULL;
|
|
MMUbloxUsbProfile profile = MM_UBLOX_USB_PROFILE_UNKNOWN;
|
|
|
|
g_assert (out_profile != NULL);
|
|
|
|
/* Response may be e.g.:
|
|
* +UUSBCONF: 3,"RNDIS",,"0x1146"
|
|
* +UUSBCONF: 2,"ECM",,"0x1143"
|
|
* +UUSBCONF: 0,"",,"0x1141"
|
|
*
|
|
* Note: we don't rely on the PID; assuming future new modules will
|
|
* have a different PID but they may keep the profile names.
|
|
*/
|
|
r = g_regex_new ("\\+UUSBCONF: (\\d+),([^,]*),([^,]*),([^,]*)(?:\\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 && g_match_info_matches (match_info)) {
|
|
g_autofree gchar *profile_name = NULL;
|
|
|
|
profile_name = mm_get_string_unquoted_from_match_info (match_info, 2);
|
|
if (profile_name && profile_name[0]) {
|
|
if (g_str_equal (profile_name, "RNDIS"))
|
|
profile = MM_UBLOX_USB_PROFILE_RNDIS;
|
|
else if (g_str_equal (profile_name, "ECM"))
|
|
profile = MM_UBLOX_USB_PROFILE_ECM;
|
|
else
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
|
"Unknown USB profile: '%s'", profile_name);
|
|
} else
|
|
profile = MM_UBLOX_USB_PROFILE_BACK_COMPATIBLE;
|
|
}
|
|
|
|
if (inner_error) {
|
|
g_propagate_error (error, inner_error);
|
|
return FALSE;
|
|
}
|
|
|
|
if (profile == MM_UBLOX_USB_PROFILE_UNKNOWN) {
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Couldn't parse profile response");
|
|
return FALSE;
|
|
}
|
|
|
|
*out_profile = profile;
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* UBMCONF? response parser */
|
|
|
|
gboolean
|
|
mm_ublox_parse_ubmconf_response (const gchar *response,
|
|
MMUbloxNetworkingMode *out_mode,
|
|
GError **error)
|
|
{
|
|
g_autoptr(GRegex) r = NULL;
|
|
g_autoptr(GMatchInfo) match_info = NULL;
|
|
GError *inner_error = NULL;
|
|
MMUbloxNetworkingMode mode = MM_UBLOX_NETWORKING_MODE_UNKNOWN;
|
|
|
|
g_assert (out_mode != NULL);
|
|
|
|
/* Response may be e.g.:
|
|
* +UBMCONF: 1
|
|
* +UBMCONF: 2
|
|
*/
|
|
r = g_regex_new ("\\+UBMCONF: (\\d+)(?:\\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 && g_match_info_matches (match_info)) {
|
|
guint mode_id = 0;
|
|
|
|
if (mm_get_uint_from_match_info (match_info, 1, &mode_id)) {
|
|
switch (mode_id) {
|
|
case 1:
|
|
mode = MM_UBLOX_NETWORKING_MODE_ROUTER;
|
|
break;
|
|
case 2:
|
|
mode = MM_UBLOX_NETWORKING_MODE_BRIDGE;
|
|
break;
|
|
default:
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
|
"Unknown mode id: '%u'", mode_id);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (inner_error) {
|
|
g_propagate_error (error, inner_error);
|
|
return FALSE;
|
|
}
|
|
|
|
if (mode == MM_UBLOX_NETWORKING_MODE_UNKNOWN) {
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Couldn't parse networking mode response");
|
|
return FALSE;
|
|
}
|
|
|
|
*out_mode = mode;
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* UIPADDR=N response parser */
|
|
|
|
gboolean
|
|
mm_ublox_parse_uipaddr_response (const gchar *response,
|
|
guint *out_cid,
|
|
gchar **out_if_name,
|
|
gchar **out_ipv4_address,
|
|
gchar **out_ipv4_subnet,
|
|
gchar **out_ipv6_global_address,
|
|
gchar **out_ipv6_link_local_address,
|
|
GError **error)
|
|
{
|
|
g_autoptr(GRegex) r = NULL;
|
|
g_autoptr(GMatchInfo) match_info = NULL;
|
|
GError *inner_error = NULL;
|
|
guint cid = 0;
|
|
gchar *if_name = NULL;
|
|
gchar *ipv4_address = NULL;
|
|
gchar *ipv4_subnet = NULL;
|
|
gchar *ipv6_global_address = NULL;
|
|
gchar *ipv6_link_local_address = NULL;
|
|
|
|
/* Response may be e.g.:
|
|
* +UIPADDR: 1,"ccinet0","5.168.120.13","255.255.255.0","",""
|
|
* +UIPADDR: 2,"ccinet1","","","2001::2:200:FF:FE00:0/64","FE80::200:FF:FE00:0/64"
|
|
* +UIPADDR: 3,"ccinet2","5.10.100.2","255.255.255.0","2001::1:200:FF:FE00:0/64","FE80::200:FF:FE00:0/64"
|
|
*
|
|
* We assume only ONE line is returned; because we request +UIPADDR with a specific N CID.
|
|
*/
|
|
r = g_regex_new ("\\+UIPADDR: (\\d+),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*)(?:\\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_INVALID_ARGS, "Couldn't match +UIPADDR response");
|
|
goto out;
|
|
}
|
|
|
|
if (out_cid && !mm_get_uint_from_match_info (match_info, 1, &cid)) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing cid");
|
|
goto out;
|
|
}
|
|
|
|
if (out_if_name && !(if_name = mm_get_string_unquoted_from_match_info (match_info, 2))) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing interface name");
|
|
goto out;
|
|
}
|
|
|
|
/* Remaining strings are optional */
|
|
|
|
if (out_ipv4_address)
|
|
ipv4_address = mm_get_string_unquoted_from_match_info (match_info, 3);
|
|
|
|
if (out_ipv4_subnet)
|
|
ipv4_subnet = mm_get_string_unquoted_from_match_info (match_info, 4);
|
|
|
|
if (out_ipv6_global_address)
|
|
ipv6_global_address = mm_get_string_unquoted_from_match_info (match_info, 5);
|
|
|
|
if (out_ipv6_link_local_address)
|
|
ipv6_link_local_address = mm_get_string_unquoted_from_match_info (match_info, 6);
|
|
|
|
out:
|
|
if (inner_error) {
|
|
g_free (if_name);
|
|
g_free (ipv4_address);
|
|
g_free (ipv4_subnet);
|
|
g_free (ipv6_global_address);
|
|
g_free (ipv6_link_local_address);
|
|
g_propagate_error (error, inner_error);
|
|
return FALSE;
|
|
}
|
|
|
|
if (out_cid)
|
|
*out_cid = cid;
|
|
if (out_if_name)
|
|
*out_if_name = if_name;
|
|
if (out_ipv4_address)
|
|
*out_ipv4_address = ipv4_address;
|
|
if (out_ipv4_subnet)
|
|
*out_ipv4_subnet = ipv4_subnet;
|
|
if (out_ipv6_global_address)
|
|
*out_ipv6_global_address = ipv6_global_address;
|
|
if (out_ipv6_link_local_address)
|
|
*out_ipv6_link_local_address = ipv6_link_local_address;
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* CFUN? response parser */
|
|
|
|
gboolean
|
|
mm_ublox_parse_cfun_response (const gchar *response,
|
|
MMModemPowerState *out_state,
|
|
GError **error)
|
|
{
|
|
guint state;
|
|
|
|
if (!mm_3gpp_parse_cfun_query_response (response, &state, error))
|
|
return FALSE;
|
|
|
|
switch (state) {
|
|
case 1:
|
|
*out_state = MM_MODEM_POWER_STATE_ON;
|
|
return TRUE;
|
|
case 0:
|
|
/* minimum functionality */
|
|
case 4:
|
|
/* airplane mode */
|
|
case 19:
|
|
/* minimum functionality with SIM deactivated */
|
|
*out_state = MM_MODEM_POWER_STATE_LOW;
|
|
return TRUE;
|
|
default:
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Unknown +CFUN state: %u", state);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* URAT=? response parser */
|
|
|
|
/* Index of the array is the ublox-specific value */
|
|
static const MMModemMode ublox_combinations[] = {
|
|
( MM_MODEM_MODE_2G ),
|
|
( MM_MODEM_MODE_2G | MM_MODEM_MODE_3G ),
|
|
( MM_MODEM_MODE_3G ),
|
|
( MM_MODEM_MODE_4G ),
|
|
( MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G ),
|
|
( MM_MODEM_MODE_2G | MM_MODEM_MODE_4G ),
|
|
( MM_MODEM_MODE_3G | MM_MODEM_MODE_4G ),
|
|
( MM_MODEM_MODE_4G ),
|
|
( MM_MODEM_MODE_4G ),
|
|
};
|
|
|
|
GArray *
|
|
mm_ublox_parse_urat_test_response (const gchar *response,
|
|
gpointer log_object,
|
|
GError **error)
|
|
{
|
|
GArray *combinations = NULL;
|
|
GArray *selected = NULL;
|
|
GArray *preferred = NULL;
|
|
gchar **split;
|
|
guint split_len;
|
|
GError *inner_error = NULL;
|
|
guint i;
|
|
|
|
/*
|
|
* E.g.:
|
|
* AT+URAT=?
|
|
* +URAT: (0-6),(0,2,3)
|
|
*/
|
|
response = mm_strip_tag (response, "+URAT:");
|
|
split = mm_split_string_groups (response);
|
|
split_len = g_strv_length (split);
|
|
if (split_len > 2 || split_len < 1) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Unexpected number of groups in +URAT=? response: %u", g_strv_length (split));
|
|
goto out;
|
|
}
|
|
|
|
/* The selected list must have values */
|
|
selected = mm_parse_uint_list (split[0], &inner_error);
|
|
if (inner_error)
|
|
goto out;
|
|
|
|
if (!selected) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"No selected RAT values given in +URAT=? response");
|
|
goto out;
|
|
}
|
|
|
|
/* For our purposes, the preferred list may be empty */
|
|
preferred = mm_parse_uint_list (split[1], &inner_error);
|
|
if (inner_error)
|
|
goto out;
|
|
|
|
/* Build array of combinations */
|
|
combinations = g_array_new (FALSE, FALSE, sizeof (MMModemModeCombination));
|
|
|
|
for (i = 0; i < selected->len; i++) {
|
|
guint selected_value;
|
|
MMModemModeCombination combination;
|
|
guint j;
|
|
|
|
selected_value = g_array_index (selected, guint, i);
|
|
if (selected_value >= G_N_ELEMENTS (ublox_combinations)) {
|
|
mm_obj_warn (log_object, "unexpected AcT value: %u", selected_value);
|
|
continue;
|
|
}
|
|
|
|
/* Combination without any preferred */
|
|
combination.allowed = ublox_combinations[selected_value];
|
|
combination.preferred = MM_MODEM_MODE_NONE;
|
|
g_array_append_val (combinations, combination);
|
|
|
|
if (mm_count_bits_set (combination.allowed) == 1)
|
|
continue;
|
|
|
|
if (!preferred)
|
|
continue;
|
|
|
|
for (j = 0; j < preferred->len; j++) {
|
|
guint preferred_value;
|
|
|
|
preferred_value = g_array_index (preferred, guint, j);
|
|
if (preferred_value >= G_N_ELEMENTS (ublox_combinations)) {
|
|
mm_obj_warn (log_object, "unexpected AcT preferred value: %u", preferred_value);
|
|
continue;
|
|
}
|
|
combination.preferred = ublox_combinations[preferred_value];
|
|
if (mm_count_bits_set (combination.preferred) != 1) {
|
|
mm_obj_warn (log_object, "AcT preferred value should be a single AcT: %u", preferred_value);
|
|
continue;
|
|
}
|
|
if (!(combination.allowed & combination.preferred))
|
|
continue;
|
|
g_array_append_val (combinations, combination);
|
|
}
|
|
}
|
|
|
|
if (combinations->len == 0) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"No combinations built from +URAT=? response");
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
g_strfreev (split);
|
|
if (selected)
|
|
g_array_unref (selected);
|
|
if (preferred)
|
|
g_array_unref (preferred);
|
|
|
|
if (inner_error) {
|
|
if (combinations)
|
|
g_array_unref (combinations);
|
|
g_propagate_error (error, inner_error);
|
|
return NULL;
|
|
}
|
|
|
|
return combinations;
|
|
}
|
|
|
|
typedef struct {
|
|
const gchar *model;
|
|
SettingsUpdateMethod method;
|
|
FeatureSupport uact;
|
|
FeatureSupport ubandsel;
|
|
MMModemMode mode;
|
|
MMModemBand bands_2g[4];
|
|
MMModemBand bands_3g[6];
|
|
MMModemBand bands_4g[12];
|
|
} BandConfiguration;
|
|
|
|
static const BandConfiguration band_configuration[] = {
|
|
{
|
|
.model = "SARA-G300",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G,
|
|
.bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS }
|
|
},
|
|
{
|
|
.model = "SARA-G310",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }
|
|
},
|
|
{
|
|
.model = "SARA-G340",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G,
|
|
.bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS }
|
|
},
|
|
{
|
|
.model = "SARA-G350",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }
|
|
},
|
|
{
|
|
.model = "SARA-G450",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }
|
|
},
|
|
{
|
|
.model = "LISA-U200",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8,
|
|
MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 }
|
|
},
|
|
{
|
|
.model = "LISA-U201",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8,
|
|
MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 }
|
|
},
|
|
{
|
|
.model = "LISA-U230",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8,
|
|
MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 }
|
|
},
|
|
{
|
|
.model = "LISA-U260",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 }
|
|
},
|
|
{
|
|
.model = "LISA-U270",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 }
|
|
},
|
|
{
|
|
.model = "SARA-U201",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8,
|
|
MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 }
|
|
},
|
|
{
|
|
.model = "SARA-U260",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_PCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 }
|
|
},
|
|
{
|
|
.model = "SARA-U270",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
|
|
.bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 }
|
|
},
|
|
{
|
|
.model = "SARA-U280",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_3G,
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 }
|
|
},
|
|
{
|
|
.model = "MPCI-L201",
|
|
.method = SETTINGS_UPDATE_METHOD_CFUN,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
|
|
MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_17 }
|
|
},
|
|
{
|
|
.model = "MPCI-L200",
|
|
.method = SETTINGS_UPDATE_METHOD_CFUN,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_4,
|
|
MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
|
|
MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_17 }
|
|
},
|
|
{
|
|
.model = "MPCI-L210",
|
|
.method = SETTINGS_UPDATE_METHOD_CFUN,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2,
|
|
MM_MODEM_BAND_UTRAN_1 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5,
|
|
MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20 }
|
|
},
|
|
{
|
|
.model = "MPCI-L220",
|
|
.method = SETTINGS_UPDATE_METHOD_CFUN,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5,
|
|
MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_19 }
|
|
},
|
|
{
|
|
.model = "MPCI-L280",
|
|
.method = SETTINGS_UPDATE_METHOD_CFUN,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2,
|
|
MM_MODEM_BAND_UTRAN_1 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5,
|
|
MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_28 }
|
|
},
|
|
{
|
|
.model = "TOBY-L200",
|
|
.method = SETTINGS_UPDATE_METHOD_CFUN,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_4,
|
|
MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
|
|
MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_17 }
|
|
},
|
|
{
|
|
.model = "TOBY-L201",
|
|
.method = SETTINGS_UPDATE_METHOD_CFUN,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
|
|
MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_17 }
|
|
},
|
|
{
|
|
.model = "TOBY-L210",
|
|
.method = SETTINGS_UPDATE_METHOD_CFUN,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2,
|
|
MM_MODEM_BAND_UTRAN_1 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5,
|
|
MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20 }
|
|
},
|
|
{
|
|
.model = "TOBY-L220",
|
|
.method = SETTINGS_UPDATE_METHOD_CFUN,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2,
|
|
MM_MODEM_BAND_UTRAN_1 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5,
|
|
MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_19 }
|
|
},
|
|
{
|
|
.model = "TOBY-L280",
|
|
.method = SETTINGS_UPDATE_METHOD_CFUN,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2,
|
|
MM_MODEM_BAND_UTRAN_1 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5,
|
|
MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_28 }
|
|
},
|
|
{
|
|
.model = "TOBY-L4006",
|
|
.method = SETTINGS_UPDATE_METHOD_CFUN,
|
|
.uact = FEATURE_SUPPORTED,
|
|
.ubandsel = FEATURE_UNSUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_PCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_2 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
|
|
MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13,
|
|
MM_MODEM_BAND_EUTRAN_29 }
|
|
},
|
|
{
|
|
.model = "TOBY-L4106",
|
|
.method = SETTINGS_UPDATE_METHOD_CFUN,
|
|
.uact = FEATURE_SUPPORTED,
|
|
.ubandsel = FEATURE_UNSUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_7,
|
|
MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_38 }
|
|
},
|
|
{
|
|
.model = "TOBY-L4206",
|
|
.method = SETTINGS_UPDATE_METHOD_CFUN,
|
|
.uact = FEATURE_SUPPORTED,
|
|
.ubandsel = FEATURE_UNSUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5,
|
|
MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_9,
|
|
MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_28 }
|
|
},
|
|
{
|
|
.model = "TOBY-L4906",
|
|
.method = SETTINGS_UPDATE_METHOD_CFUN,
|
|
.uact = FEATURE_SUPPORTED,
|
|
.ubandsel = FEATURE_UNSUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_39,
|
|
MM_MODEM_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_41 }
|
|
},
|
|
{
|
|
.model = "TOBY-R200",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2,
|
|
MM_MODEM_BAND_UTRAN_1 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
|
|
MM_MODEM_BAND_EUTRAN_12 }
|
|
},
|
|
{
|
|
.model = "TOBY-R202",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
|
|
MM_MODEM_BAND_EUTRAN_12 }
|
|
},
|
|
{
|
|
.model = "LARA-R202",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
|
|
MM_MODEM_BAND_EUTRAN_12 }
|
|
},
|
|
{
|
|
.model = "LARA-R203",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_4G,
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_12 }
|
|
},
|
|
{
|
|
.model = "LARA-R204",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_4G,
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_13 }
|
|
},
|
|
{
|
|
.model = "LARA-R211",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G,
|
|
.bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_20 }
|
|
},
|
|
{
|
|
.model = "LARA-R280",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
|
|
.bands_3g = { MM_MODEM_BAND_UTRAN_1 },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_28 }
|
|
},
|
|
{
|
|
.model = "LARA-R3121",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_4G,
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_20 }
|
|
},
|
|
{
|
|
.model = "SARA-N200",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_4G,
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_8 }
|
|
},
|
|
{
|
|
.model = "SARA-N201",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_4G,
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_5 }
|
|
},
|
|
{
|
|
.model = "SARA-N210",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_4G,
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_20 }
|
|
},
|
|
{
|
|
.model = "SARA-N211",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_4G,
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20 }
|
|
},
|
|
{
|
|
.model = "SARA-N280",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_SUPPORTED,
|
|
.mode = MM_MODEM_MODE_4G,
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_28 }
|
|
},
|
|
{
|
|
.model = "SARA-R410M-52B",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_UNSUPPORTED,
|
|
.mode = MM_MODEM_MODE_4G,
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
|
|
MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13 }
|
|
},
|
|
{
|
|
.model = "SARA-R410M-02B",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_UNSUPPORTED,
|
|
.mode = MM_MODEM_MODE_4G,
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3,
|
|
MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_8,
|
|
MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_19,
|
|
MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_39 }
|
|
},
|
|
{
|
|
.model = "SARA-R412M-02B",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_UNSUPPORTED,
|
|
.mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G,
|
|
.bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3,
|
|
MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_8,
|
|
MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_19,
|
|
MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_39 }
|
|
},
|
|
{
|
|
.model = "SARA-N410-02B",
|
|
.method = SETTINGS_UPDATE_METHOD_COPS,
|
|
.uact = FEATURE_UNSUPPORTED,
|
|
.ubandsel = FEATURE_UNSUPPORTED,
|
|
.mode = MM_MODEM_MODE_4G,
|
|
.bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3,
|
|
MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_8,
|
|
MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_19,
|
|
MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_28 }
|
|
},
|
|
};
|
|
|
|
gboolean
|
|
mm_ublox_get_support_config (const gchar *model,
|
|
UbloxSupportConfig *config,
|
|
GError **error)
|
|
{
|
|
guint i;
|
|
|
|
if (!model) {
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Support configuration unknown for unknown model");
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (band_configuration); i++) {
|
|
/* NOTE: matching by prefix! */
|
|
if (g_str_has_prefix (model, band_configuration[i].model)) {
|
|
config->loaded = TRUE;
|
|
config->method = band_configuration[i].method;
|
|
config->uact = band_configuration[i].uact;
|
|
config->ubandsel = band_configuration[i].ubandsel;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"No support configuration found for modem: %s", model);
|
|
return FALSE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Supported modes loading */
|
|
|
|
static MMModemMode
|
|
supported_modes_per_model (const gchar *model)
|
|
{
|
|
MMModemMode mode;
|
|
guint i;
|
|
|
|
mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G;
|
|
|
|
if (model) {
|
|
for (i = 0; i < G_N_ELEMENTS (band_configuration); i++)
|
|
if (g_str_has_prefix (model, band_configuration[i].model)) {
|
|
mode = band_configuration[i].mode;
|
|
return mode;;
|
|
}
|
|
}
|
|
|
|
return mode;
|
|
}
|
|
|
|
GArray *
|
|
mm_ublox_filter_supported_modes (const gchar *model,
|
|
GArray *combinations,
|
|
gpointer logger,
|
|
GError **error)
|
|
{
|
|
MMModemModeCombination mode;
|
|
GArray *all;
|
|
GArray *filtered;
|
|
|
|
/* Model not specified? */
|
|
if (!model)
|
|
return combinations;
|
|
|
|
/* AT+URAT=? lies; we need an extra per-device filtering, thanks u-blox.
|
|
* Don't know all PIDs for all devices, so model string based filtering... */
|
|
|
|
mode.allowed = supported_modes_per_model (model);
|
|
mode.preferred = MM_MODEM_MODE_NONE;
|
|
|
|
/* Nothing filtered? */
|
|
if (mode.allowed == supported_modes_per_model (NULL))
|
|
return combinations;
|
|
|
|
all = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
|
|
g_array_append_val (all, mode);
|
|
filtered = mm_filter_supported_modes (all, combinations, logger);
|
|
g_array_unref (all);
|
|
g_array_unref (combinations);
|
|
|
|
/* Error if nothing left */
|
|
if (filtered->len == 0) {
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"No valid mode combinations built after filtering (model %s)", model);
|
|
g_array_unref (filtered);
|
|
return NULL;
|
|
}
|
|
|
|
return filtered;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Supported bands loading */
|
|
|
|
GArray *
|
|
mm_ublox_get_supported_bands (const gchar *model,
|
|
gpointer log_object,
|
|
GError **error)
|
|
{
|
|
MMModemMode mode;
|
|
GArray *bands;
|
|
guint i, j;
|
|
|
|
mode = supported_modes_per_model (model);
|
|
bands = g_array_new (FALSE, FALSE, sizeof (MMModemBand));
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (band_configuration); i++) {
|
|
if (g_str_has_prefix (model, band_configuration[i].model)) {
|
|
mm_obj_dbg (log_object, "known supported bands found for model: %s", band_configuration[i].model);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == G_N_ELEMENTS (band_configuration)) {
|
|
mm_obj_warn (log_object, "unknown model name given when looking for supported bands: %s", model);
|
|
return NULL;
|
|
}
|
|
|
|
mode = band_configuration[i].mode;
|
|
|
|
if (mode & MM_MODEM_MODE_2G) {
|
|
for (j = 0; j < G_N_ELEMENTS (band_configuration[i].bands_2g) && band_configuration[i].bands_2g[j]; j++) {
|
|
bands = g_array_append_val (bands, band_configuration[i].bands_2g[j]);
|
|
}
|
|
}
|
|
|
|
if (mode & MM_MODEM_MODE_3G) {
|
|
for (j = 0; j < G_N_ELEMENTS (band_configuration[i].bands_3g) && band_configuration[i].bands_3g[j]; j++) {
|
|
bands = g_array_append_val (bands, band_configuration[i].bands_3g[j]);
|
|
}
|
|
}
|
|
|
|
if (mode & MM_MODEM_MODE_4G) {
|
|
for (j = 0; j < G_N_ELEMENTS (band_configuration[i].bands_4g) && band_configuration[i].bands_4g[j]; j++) {
|
|
bands = g_array_append_val (bands, band_configuration[i].bands_4g[j]);
|
|
}
|
|
}
|
|
|
|
if (bands->len == 0) {
|
|
g_array_unref (bands);
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"No valid supported bands loaded");
|
|
return NULL;
|
|
}
|
|
|
|
return bands;
|
|
}
|
|
|
|
typedef struct {
|
|
guint num;
|
|
MMModemBand band[4];
|
|
} NumToBand;
|
|
|
|
/* 2G GSM Band Frequencies */
|
|
static const NumToBand num_bands_2g [] = {
|
|
{ .num = 850, .band = { MM_MODEM_BAND_G850 } },
|
|
{ .num = 900, .band = { MM_MODEM_BAND_EGSM } },
|
|
{ .num = 1900, .band = { MM_MODEM_BAND_PCS } },
|
|
{ .num = 1800, .band = { MM_MODEM_BAND_DCS } },
|
|
};
|
|
|
|
/* 3G UMTS Band Frequencies */
|
|
static const NumToBand num_bands_3g [] = {
|
|
{ .num = 800, .band = { MM_MODEM_BAND_UTRAN_6 } },
|
|
{ .num = 850, .band = { MM_MODEM_BAND_UTRAN_5 } },
|
|
{ .num = 900, .band = { MM_MODEM_BAND_UTRAN_8 } },
|
|
{ .num = 1700, .band = { MM_MODEM_BAND_UTRAN_4 } },
|
|
{ .num = 1900, .band = { MM_MODEM_BAND_UTRAN_2 } },
|
|
{ .num = 2100, .band = { MM_MODEM_BAND_UTRAN_1 } },
|
|
};
|
|
|
|
/* 4G LTE Band Frequencies */
|
|
static const NumToBand num_bands_4g [] = {
|
|
{ .num = 700, .band = { MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_17 } },
|
|
{ .num = 800, .band = { MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_27 } },
|
|
{ .num = 850, .band = { MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_26 } },
|
|
{ .num = 900, .band = { MM_MODEM_BAND_EUTRAN_8 } },
|
|
{ .num = 1700, .band = { MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_10 } },
|
|
{ .num = 1800, .band = { MM_MODEM_BAND_EUTRAN_3 } },
|
|
{ .num = 1900, .band = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_39 } },
|
|
{ .num = 2100, .band = { MM_MODEM_BAND_EUTRAN_1 } },
|
|
{ .num = 2300, .band = { MM_MODEM_BAND_EUTRAN_40 } },
|
|
{ .num = 2500, .band = { MM_MODEM_BAND_EUTRAN_41 } },
|
|
{ .num = 2600, .band = { MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_38 } },
|
|
};
|
|
/*****************************************************************************/
|
|
/* +UBANDSEL? response parser */
|
|
|
|
static MMModemBand
|
|
num_to_band_2g (guint num)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (num_bands_2g); i++) {
|
|
if (num == num_bands_2g[i].num)
|
|
return num_bands_2g[i].band[0];
|
|
}
|
|
return MM_MODEM_BAND_UNKNOWN;
|
|
}
|
|
|
|
static MMModemBand
|
|
num_to_band_3g (guint num)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (num_bands_3g); i++) {
|
|
if (num == num_bands_3g[i].num)
|
|
return num_bands_3g[i].band[0];
|
|
}
|
|
return MM_MODEM_BAND_UNKNOWN;
|
|
}
|
|
|
|
static guint
|
|
band_to_num (MMModemBand band)
|
|
{
|
|
guint i, j;
|
|
|
|
/* Search 2G list */
|
|
for (i = 0; i < G_N_ELEMENTS (num_bands_2g); i++) {
|
|
for (j = 0; j < G_N_ELEMENTS (num_bands_2g[i].band) && num_bands_2g[i].band[j]; j++) {
|
|
if (band == num_bands_2g[i].band[j])
|
|
return num_bands_2g[i].num;
|
|
}
|
|
}
|
|
|
|
/* Search 3G list */
|
|
for (i = 0; i < G_N_ELEMENTS (num_bands_3g); i++) {
|
|
for (j = 0; j < G_N_ELEMENTS (num_bands_3g[i].band) && num_bands_3g[i].band[j]; j++) {
|
|
if (band == num_bands_3g[i].band[j])
|
|
return num_bands_3g[i].num;
|
|
}
|
|
}
|
|
|
|
/* Search 4G list */
|
|
for (i = 0; i < G_N_ELEMENTS (num_bands_4g); i++) {
|
|
for (j = 0; j < G_N_ELEMENTS (num_bands_4g[i].band) && num_bands_4g[i].band[j]; j++) {
|
|
if (band == num_bands_4g[i].band[j])
|
|
return num_bands_4g[i].num;
|
|
}
|
|
}
|
|
|
|
/* Should never happen */
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
append_bands (GArray *bands,
|
|
guint ubandsel_value,
|
|
MMModemMode mode,
|
|
const gchar *model,
|
|
gpointer log_object)
|
|
{
|
|
guint i, j, k, x;
|
|
MMModemBand band;
|
|
|
|
/* Find Modem Model Index in band_configuration */
|
|
for (i = 0; i < G_N_ELEMENTS (band_configuration); i++) {
|
|
if (g_str_has_prefix (model, band_configuration[i].model)) {
|
|
mm_obj_dbg (log_object, "known bands found for model: %s", band_configuration[i].model);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == G_N_ELEMENTS (band_configuration)) {
|
|
mm_obj_warn (log_object, "unknown model name given when looking for bands: %s", model);
|
|
return;
|
|
}
|
|
|
|
if (mode & MM_MODEM_MODE_2G) {
|
|
band = num_to_band_2g (ubandsel_value);
|
|
if (band != MM_MODEM_BAND_UNKNOWN) {
|
|
for (x = 0; x < G_N_ELEMENTS (band_configuration[i].bands_2g); x++) {
|
|
if (band_configuration[i].bands_2g[x] == band) {
|
|
g_array_append_val (bands, band);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mode & MM_MODEM_MODE_3G) {
|
|
band = num_to_band_3g (ubandsel_value);
|
|
if (band != MM_MODEM_BAND_UNKNOWN) {
|
|
for (x = 0; x < G_N_ELEMENTS (band_configuration[i].bands_3g); x++) {
|
|
if (band_configuration[i].bands_3g[x] == band) {
|
|
g_array_append_val (bands, band);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Note: The weird code segment below is to separate out specific LTE bands since
|
|
* UBANDSEL? reports back the frequency of the band and not the band itself.
|
|
*/
|
|
|
|
if (mode & MM_MODEM_MODE_4G) {
|
|
for (j = 0; j < G_N_ELEMENTS (num_bands_4g); j++) {
|
|
if (ubandsel_value == num_bands_4g[j].num) {
|
|
for (k = 0; k < G_N_ELEMENTS (num_bands_4g[j].band); k++) {
|
|
band = num_bands_4g[j].band[k];
|
|
if (band != MM_MODEM_BAND_UNKNOWN) {
|
|
for (x = 0; x < G_N_ELEMENTS (band_configuration[i].bands_4g); x++) {
|
|
if (band_configuration[i].bands_4g[x] == band) {
|
|
g_array_append_val (bands, band);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
GArray *
|
|
mm_ublox_parse_ubandsel_response (const gchar *response,
|
|
const gchar *model,
|
|
gpointer log_object,
|
|
GError **error)
|
|
{
|
|
GArray *array_values = NULL;
|
|
GArray *array = NULL;
|
|
gchar *dupstr = NULL;
|
|
GError *inner_error = NULL;
|
|
guint i;
|
|
MMModemMode mode;
|
|
|
|
if (!g_str_has_prefix (response, "+UBANDSEL")) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Couldn't parse +UBANDSEL response: '%s'", response);
|
|
goto out;
|
|
}
|
|
|
|
/* Response may be e.g.:
|
|
* +UBANDSEL: 850,900,1800,1900
|
|
*/
|
|
dupstr = g_strchomp (g_strdup (mm_strip_tag (response, "+UBANDSEL:")));
|
|
|
|
array_values = mm_parse_uint_list (dupstr, &inner_error);
|
|
if (!array_values)
|
|
goto out;
|
|
|
|
/* Convert list of ubandsel numbers to MMModemBand values */
|
|
mode = supported_modes_per_model (model);
|
|
array = g_array_new (FALSE, FALSE, sizeof (MMModemBand));
|
|
for (i = 0; i < array_values->len; i++)
|
|
append_bands (array, g_array_index (array_values, guint, i), mode, model, log_object);
|
|
|
|
if (!array->len) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"No known band selection values matched in +UBANDSEL response: '%s'", response);
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (inner_error) {
|
|
g_propagate_error (error, inner_error);
|
|
g_clear_pointer (&array, g_array_unref);
|
|
}
|
|
g_clear_pointer (&array_values, g_array_unref);
|
|
g_free (dupstr);
|
|
return array;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* UBANDSEL=X command builder */
|
|
|
|
static gint
|
|
ubandsel_num_cmp (const guint *a, const guint *b)
|
|
{
|
|
return (*a - *b);
|
|
}
|
|
|
|
gchar *
|
|
mm_ublox_build_ubandsel_set_command (GArray *bands,
|
|
const gchar *model,
|
|
GError **error)
|
|
{
|
|
GString *command = NULL;
|
|
GArray *ubandsel_nums;
|
|
guint num;
|
|
guint i, j, k;
|
|
|
|
if (bands->len == 1 && g_array_index (bands, MMModemBand, 0) == MM_MODEM_BAND_ANY)
|
|
return g_strdup ("+UBANDSEL=0");
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (band_configuration); i++) {
|
|
if (g_str_has_prefix (model, band_configuration[i].model))
|
|
break;
|
|
}
|
|
|
|
if (i == G_N_ELEMENTS (band_configuration)) {
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Unknown modem model %s", model);
|
|
return NULL;
|
|
}
|
|
|
|
ubandsel_nums = g_array_sized_new (FALSE, FALSE, sizeof (guint), bands->len);
|
|
|
|
for (j = 0; j < bands->len; j++) {
|
|
MMModemBand band;
|
|
gboolean found = FALSE;
|
|
|
|
band = g_array_index (bands, MMModemBand, j);
|
|
|
|
/* Check to see if band is supported by the model */
|
|
for (k = 0; !found && k < G_N_ELEMENTS (band_configuration[i].bands_2g) && band_configuration[i].bands_2g[k]; k++) {
|
|
if (band == band_configuration[i].bands_2g[k])
|
|
found = TRUE;
|
|
}
|
|
|
|
for (k = 0; !found && k < G_N_ELEMENTS (band_configuration[i].bands_3g) && band_configuration[i].bands_3g[k]; k++) {
|
|
if (band == band_configuration[i].bands_3g[k])
|
|
found = TRUE;
|
|
}
|
|
|
|
for (k = 0; !found && k < G_N_ELEMENTS (band_configuration[i].bands_4g) && band_configuration[i].bands_4g[k]; k++) {
|
|
if (band == band_configuration[i].bands_4g[k])
|
|
found = TRUE;
|
|
}
|
|
|
|
if (found) {
|
|
num = band_to_num (band);
|
|
g_assert (num != 0);
|
|
g_array_append_val (ubandsel_nums, num);
|
|
}
|
|
}
|
|
|
|
if (ubandsel_nums->len == 0) {
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
|
"Given band combination is unsupported");
|
|
g_array_unref (ubandsel_nums);
|
|
return NULL;
|
|
}
|
|
|
|
if (ubandsel_nums->len > 1)
|
|
g_array_sort (ubandsel_nums, (GCompareFunc) ubandsel_num_cmp);
|
|
|
|
/* Build command */
|
|
command = g_string_new ("+UBANDSEL=");
|
|
for (i = 0; i < ubandsel_nums->len; i++)
|
|
g_string_append_printf (command, "%s%u", i == 0 ? "" : ",", g_array_index (ubandsel_nums, guint, i));
|
|
|
|
return g_string_free (command, FALSE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Get mode to apply when ANY */
|
|
|
|
MMModemMode
|
|
mm_ublox_get_modem_mode_any (const GArray *combinations)
|
|
{
|
|
guint i;
|
|
MMModemMode any = MM_MODEM_MODE_NONE;
|
|
guint any_bits_set = 0;
|
|
|
|
for (i = 0; i < combinations->len; i++) {
|
|
MMModemModeCombination *combination;
|
|
guint bits_set;
|
|
|
|
combination = &g_array_index (combinations, MMModemModeCombination, i);
|
|
if (combination->preferred != MM_MODEM_MODE_NONE)
|
|
continue;
|
|
bits_set = mm_count_bits_set (combination->allowed);
|
|
if (bits_set > any_bits_set) {
|
|
any_bits_set = bits_set;
|
|
any = combination->allowed;
|
|
}
|
|
}
|
|
|
|
/* If combinations were processed via mm_ublox_parse_urat_test_response(),
|
|
* we're sure that there will be at least one combination with preferred
|
|
* 'none', so there must be some valid combination as result */
|
|
g_assert (any != MM_MODEM_MODE_NONE);
|
|
return any;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* UACT common config */
|
|
|
|
typedef struct {
|
|
guint num;
|
|
MMModemBand band;
|
|
} UactBandConfig;
|
|
|
|
static const UactBandConfig uact_band_config[] = {
|
|
/* GSM bands */
|
|
{ .num = 900, .band = MM_MODEM_BAND_EGSM },
|
|
{ .num = 1800, .band = MM_MODEM_BAND_DCS },
|
|
{ .num = 1900, .band = MM_MODEM_BAND_PCS },
|
|
{ .num = 850, .band = MM_MODEM_BAND_G850 },
|
|
{ .num = 450, .band = MM_MODEM_BAND_G450 },
|
|
{ .num = 480, .band = MM_MODEM_BAND_G480 },
|
|
{ .num = 750, .band = MM_MODEM_BAND_G750 },
|
|
{ .num = 380, .band = MM_MODEM_BAND_G380 },
|
|
{ .num = 410, .band = MM_MODEM_BAND_G410 },
|
|
{ .num = 710, .band = MM_MODEM_BAND_G710 },
|
|
{ .num = 810, .band = MM_MODEM_BAND_G810 },
|
|
/* UMTS bands */
|
|
{ .num = 1, .band = MM_MODEM_BAND_UTRAN_1 },
|
|
{ .num = 2, .band = MM_MODEM_BAND_UTRAN_2 },
|
|
{ .num = 3, .band = MM_MODEM_BAND_UTRAN_3 },
|
|
{ .num = 4, .band = MM_MODEM_BAND_UTRAN_4 },
|
|
{ .num = 5, .band = MM_MODEM_BAND_UTRAN_5 },
|
|
{ .num = 6, .band = MM_MODEM_BAND_UTRAN_6 },
|
|
{ .num = 7, .band = MM_MODEM_BAND_UTRAN_7 },
|
|
{ .num = 8, .band = MM_MODEM_BAND_UTRAN_8 },
|
|
{ .num = 9, .band = MM_MODEM_BAND_UTRAN_9 },
|
|
{ .num = 10, .band = MM_MODEM_BAND_UTRAN_10 },
|
|
{ .num = 11, .band = MM_MODEM_BAND_UTRAN_11 },
|
|
{ .num = 12, .band = MM_MODEM_BAND_UTRAN_12 },
|
|
{ .num = 13, .band = MM_MODEM_BAND_UTRAN_13 },
|
|
{ .num = 14, .band = MM_MODEM_BAND_UTRAN_14 },
|
|
{ .num = 19, .band = MM_MODEM_BAND_UTRAN_19 },
|
|
{ .num = 20, .band = MM_MODEM_BAND_UTRAN_20 },
|
|
{ .num = 21, .band = MM_MODEM_BAND_UTRAN_21 },
|
|
{ .num = 22, .band = MM_MODEM_BAND_UTRAN_22 },
|
|
{ .num = 25, .band = MM_MODEM_BAND_UTRAN_25 },
|
|
/* LTE bands */
|
|
{ .num = 101, .band = MM_MODEM_BAND_EUTRAN_1 },
|
|
{ .num = 102, .band = MM_MODEM_BAND_EUTRAN_2 },
|
|
{ .num = 103, .band = MM_MODEM_BAND_EUTRAN_3 },
|
|
{ .num = 104, .band = MM_MODEM_BAND_EUTRAN_4 },
|
|
{ .num = 105, .band = MM_MODEM_BAND_EUTRAN_5 },
|
|
{ .num = 106, .band = MM_MODEM_BAND_EUTRAN_6 },
|
|
{ .num = 107, .band = MM_MODEM_BAND_EUTRAN_7 },
|
|
{ .num = 108, .band = MM_MODEM_BAND_EUTRAN_8 },
|
|
{ .num = 109, .band = MM_MODEM_BAND_EUTRAN_9 },
|
|
{ .num = 110, .band = MM_MODEM_BAND_EUTRAN_10 },
|
|
{ .num = 111, .band = MM_MODEM_BAND_EUTRAN_11 },
|
|
{ .num = 112, .band = MM_MODEM_BAND_EUTRAN_12 },
|
|
{ .num = 113, .band = MM_MODEM_BAND_EUTRAN_13 },
|
|
{ .num = 114, .band = MM_MODEM_BAND_EUTRAN_14 },
|
|
{ .num = 117, .band = MM_MODEM_BAND_EUTRAN_17 },
|
|
{ .num = 118, .band = MM_MODEM_BAND_EUTRAN_18 },
|
|
{ .num = 119, .band = MM_MODEM_BAND_EUTRAN_19 },
|
|
{ .num = 120, .band = MM_MODEM_BAND_EUTRAN_20 },
|
|
{ .num = 121, .band = MM_MODEM_BAND_EUTRAN_21 },
|
|
{ .num = 122, .band = MM_MODEM_BAND_EUTRAN_22 },
|
|
{ .num = 123, .band = MM_MODEM_BAND_EUTRAN_23 },
|
|
{ .num = 124, .band = MM_MODEM_BAND_EUTRAN_24 },
|
|
{ .num = 125, .band = MM_MODEM_BAND_EUTRAN_25 },
|
|
{ .num = 126, .band = MM_MODEM_BAND_EUTRAN_26 },
|
|
{ .num = 127, .band = MM_MODEM_BAND_EUTRAN_27 },
|
|
{ .num = 128, .band = MM_MODEM_BAND_EUTRAN_28 },
|
|
{ .num = 129, .band = MM_MODEM_BAND_EUTRAN_29 },
|
|
{ .num = 130, .band = MM_MODEM_BAND_EUTRAN_30 },
|
|
{ .num = 131, .band = MM_MODEM_BAND_EUTRAN_31 },
|
|
{ .num = 132, .band = MM_MODEM_BAND_EUTRAN_32 },
|
|
{ .num = 133, .band = MM_MODEM_BAND_EUTRAN_33 },
|
|
{ .num = 134, .band = MM_MODEM_BAND_EUTRAN_34 },
|
|
{ .num = 135, .band = MM_MODEM_BAND_EUTRAN_35 },
|
|
{ .num = 136, .band = MM_MODEM_BAND_EUTRAN_36 },
|
|
{ .num = 137, .band = MM_MODEM_BAND_EUTRAN_37 },
|
|
{ .num = 138, .band = MM_MODEM_BAND_EUTRAN_38 },
|
|
{ .num = 139, .band = MM_MODEM_BAND_EUTRAN_39 },
|
|
{ .num = 140, .band = MM_MODEM_BAND_EUTRAN_40 },
|
|
{ .num = 141, .band = MM_MODEM_BAND_EUTRAN_41 },
|
|
{ .num = 142, .band = MM_MODEM_BAND_EUTRAN_42 },
|
|
{ .num = 143, .band = MM_MODEM_BAND_EUTRAN_43 },
|
|
{ .num = 144, .band = MM_MODEM_BAND_EUTRAN_44 },
|
|
{ .num = 145, .band = MM_MODEM_BAND_EUTRAN_45 },
|
|
{ .num = 146, .band = MM_MODEM_BAND_EUTRAN_46 },
|
|
{ .num = 147, .band = MM_MODEM_BAND_EUTRAN_47 },
|
|
{ .num = 148, .band = MM_MODEM_BAND_EUTRAN_48 },
|
|
};
|
|
|
|
static MMModemBand
|
|
uact_num_to_band (guint num)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (uact_band_config); i++) {
|
|
if (num == uact_band_config[i].num)
|
|
return uact_band_config[i].band;
|
|
}
|
|
return MM_MODEM_BAND_UNKNOWN;
|
|
}
|
|
|
|
static guint
|
|
uact_band_to_num (MMModemBand band)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (uact_band_config); i++) {
|
|
if (band == uact_band_config[i].band)
|
|
return uact_band_config[i].num;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* UACT? response parser */
|
|
|
|
static GArray *
|
|
uact_num_array_to_band_array (GArray *nums)
|
|
{
|
|
GArray *bands = NULL;
|
|
guint i;
|
|
|
|
if (!nums)
|
|
return NULL;
|
|
|
|
bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), nums->len);
|
|
for (i = 0; i < nums->len; i++) {
|
|
MMModemBand band;
|
|
|
|
band = uact_num_to_band (g_array_index (nums, guint, i));
|
|
g_array_append_val (bands, band);
|
|
}
|
|
|
|
return bands;
|
|
}
|
|
|
|
GArray *
|
|
mm_ublox_parse_uact_response (const gchar *response,
|
|
GError **error)
|
|
{
|
|
g_autoptr(GRegex) r = NULL;
|
|
g_autoptr(GMatchInfo) match_info = NULL;
|
|
GError *inner_error = NULL;
|
|
GArray *nums = NULL;
|
|
GArray *bands = NULL;
|
|
|
|
/*
|
|
* AT+UACT?
|
|
* +UACT: ,,,900,1800,1,8,101,103,107,108,120,138
|
|
*/
|
|
r = g_regex_new ("\\+UACT: ([^,]*),([^,]*),([^,]*),(.*)(?:\\r\\n)?",
|
|
G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL);
|
|
g_assert (r != NULL);
|
|
|
|
g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
|
|
if (!inner_error && g_match_info_matches (match_info)) {
|
|
g_autofree gchar *bandstr = NULL;
|
|
|
|
bandstr = mm_get_string_unquoted_from_match_info (match_info, 4);
|
|
nums = mm_parse_uint_list (bandstr, &inner_error);
|
|
}
|
|
|
|
if (inner_error) {
|
|
g_propagate_error (error, inner_error);
|
|
return NULL;
|
|
}
|
|
|
|
/* Convert to MMModemBand values */
|
|
if (nums) {
|
|
bands = uact_num_array_to_band_array (nums);
|
|
g_array_unref (nums);
|
|
}
|
|
|
|
if (!bands)
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"No known band selection values matched in +UACT response: '%s'", response);
|
|
|
|
return bands;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* UACT=? response parser */
|
|
|
|
static GArray *
|
|
parse_bands_from_string (const gchar *str,
|
|
const gchar *group,
|
|
gpointer log_object)
|
|
{
|
|
GArray *bands = NULL;
|
|
GError *inner_error = NULL;
|
|
GArray *nums;
|
|
|
|
nums = mm_parse_uint_list (str, &inner_error);
|
|
if (nums) {
|
|
gchar *tmpstr;
|
|
|
|
bands = uact_num_array_to_band_array (nums);
|
|
tmpstr = mm_common_build_bands_string ((MMModemBand *)(gpointer)(bands->data), bands->len);
|
|
mm_obj_dbg (log_object, "modem reports support for %s bands: %s", group, tmpstr);
|
|
g_free (tmpstr);
|
|
|
|
g_array_unref (nums);
|
|
} else if (inner_error) {
|
|
mm_obj_warn (log_object, "couldn't parse list of supported %s bands: %s", group, inner_error->message);
|
|
g_clear_error (&inner_error);
|
|
}
|
|
|
|
return bands;
|
|
}
|
|
|
|
gboolean
|
|
mm_ublox_parse_uact_test (const gchar *response,
|
|
gpointer log_object,
|
|
GArray **bands2g_out,
|
|
GArray **bands3g_out,
|
|
GArray **bands4g_out,
|
|
GError **error)
|
|
{
|
|
g_autoptr(GRegex) r = NULL;
|
|
g_autoptr(GMatchInfo) match_info = NULL;
|
|
g_auto(GStrv) split = NULL;
|
|
GError *inner_error = NULL;
|
|
const gchar *bands2g_str = NULL;
|
|
const gchar *bands3g_str = NULL;
|
|
const gchar *bands4g_str = NULL;
|
|
GArray *bands2g = NULL;
|
|
GArray *bands3g = NULL;
|
|
GArray *bands4g = NULL;
|
|
|
|
g_assert (bands2g_out && bands3g_out && bands4g_out);
|
|
|
|
/*
|
|
* AT+UACT=?
|
|
* +UACT: ,,,(900,1800),(1,8),(101,103,107,108,120),(138)
|
|
*/
|
|
r = g_regex_new ("\\+UACT: ([^,]*),([^,]*),([^,]*),(.*)(?:\\r\\n)?",
|
|
G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 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)) {
|
|
g_autofree gchar *aux = NULL;
|
|
guint n_groups;
|
|
|
|
aux = mm_get_string_unquoted_from_match_info (match_info, 4);
|
|
split = mm_split_string_groups (aux);
|
|
n_groups = g_strv_length (split);
|
|
if (n_groups >= 1)
|
|
bands2g_str = split[0];
|
|
if (n_groups >= 2)
|
|
bands3g_str = split[1];
|
|
if (n_groups >= 3)
|
|
bands4g_str = split[2];
|
|
}
|
|
|
|
if (!bands2g_str && !bands3g_str && !bands4g_str) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"frequency groups not found: %s", response);
|
|
goto out;
|
|
}
|
|
|
|
bands2g = parse_bands_from_string (bands2g_str, "2G", log_object);
|
|
bands3g = parse_bands_from_string (bands3g_str, "3G", log_object);
|
|
bands4g = parse_bands_from_string (bands4g_str, "4G", log_object);
|
|
|
|
if (!bands2g->len && !bands3g->len && !bands4g->len) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"no supported frequencies reported: %s", response);
|
|
goto out;
|
|
}
|
|
|
|
/* success */
|
|
|
|
out:
|
|
if (inner_error) {
|
|
if (bands2g)
|
|
g_array_unref (bands2g);
|
|
if (bands3g)
|
|
g_array_unref (bands3g);
|
|
if (bands4g)
|
|
g_array_unref (bands4g);
|
|
g_propagate_error (error, inner_error);
|
|
return FALSE;
|
|
}
|
|
|
|
*bands2g_out = bands2g;
|
|
*bands3g_out = bands3g;
|
|
*bands4g_out = bands4g;
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* UACT=X command builder */
|
|
|
|
gchar *
|
|
mm_ublox_build_uact_set_command (GArray *bands,
|
|
GError **error)
|
|
{
|
|
GString *command;
|
|
|
|
/* Build command */
|
|
command = g_string_new ("+UACT=,,,");
|
|
|
|
if (bands->len == 1 && g_array_index (bands, MMModemBand, 0) == MM_MODEM_BAND_ANY)
|
|
g_string_append (command, "0");
|
|
else {
|
|
guint i;
|
|
|
|
for (i = 0; i < bands->len; i++) {
|
|
MMModemBand band;
|
|
guint num;
|
|
|
|
band = g_array_index (bands, MMModemBand, i);
|
|
num = uact_band_to_num (band);
|
|
if (!num) {
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
|
"Band unsupported by this plugin: %s", mm_modem_band_get_string (band));
|
|
g_string_free (command, TRUE);
|
|
return NULL;
|
|
}
|
|
|
|
g_string_append_printf (command, "%s%u", i == 0 ? "" : ",", num);
|
|
}
|
|
}
|
|
|
|
return g_string_free (command, FALSE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* URAT? response parser */
|
|
|
|
gboolean
|
|
mm_ublox_parse_urat_read_response (const gchar *response,
|
|
gpointer log_object,
|
|
MMModemMode *out_allowed,
|
|
MMModemMode *out_preferred,
|
|
GError **error)
|
|
{
|
|
g_autoptr(GRegex) r = NULL;
|
|
g_autoptr(GMatchInfo) match_info = NULL;
|
|
GError *inner_error = NULL;
|
|
MMModemMode allowed = MM_MODEM_MODE_NONE;
|
|
MMModemMode preferred = MM_MODEM_MODE_NONE;
|
|
g_autofree gchar *allowed_str = NULL;
|
|
g_autofree gchar *preferred_str = NULL;
|
|
|
|
g_assert (out_allowed != NULL && out_preferred != NULL);
|
|
|
|
/* Response may be e.g.:
|
|
* +URAT: 1,2
|
|
* +URAT: 1
|
|
*/
|
|
r = g_regex_new ("\\+URAT: (\\d+)(?:,(\\d+))?(?:\\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 && g_match_info_matches (match_info)) {
|
|
guint value = 0;
|
|
|
|
/* Selected item is mandatory */
|
|
if (!mm_get_uint_from_match_info (match_info, 1, &value)) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Couldn't read AcT selected value");
|
|
goto out;
|
|
}
|
|
if (value >= G_N_ELEMENTS (ublox_combinations)) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Unexpected AcT selected value: %u", value);
|
|
goto out;
|
|
}
|
|
allowed = ublox_combinations[value];
|
|
allowed_str = mm_modem_mode_build_string_from_mask (allowed);
|
|
mm_obj_dbg (log_object, "current allowed modes retrieved: %s", allowed_str);
|
|
|
|
/* Preferred item is optional */
|
|
if (mm_get_uint_from_match_info (match_info, 2, &value)) {
|
|
if (value >= G_N_ELEMENTS (ublox_combinations)) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Unexpected AcT preferred value: %u", value);
|
|
goto out;
|
|
}
|
|
preferred = ublox_combinations[value];
|
|
preferred_str = mm_modem_mode_build_string_from_mask (preferred);
|
|
mm_obj_dbg (log_object, "current preferred modes retrieved: %s", preferred_str);
|
|
if (mm_count_bits_set (preferred) != 1) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"AcT preferred value should be a single AcT: %s", preferred_str);
|
|
goto out;
|
|
}
|
|
if (!(allowed & preferred)) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"AcT preferred value (%s) not a subset of the allowed value (%s)",
|
|
preferred_str, allowed_str);
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (inner_error) {
|
|
g_propagate_error (error, inner_error);
|
|
return FALSE;
|
|
}
|
|
|
|
if (allowed == MM_MODEM_MODE_NONE) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Couldn't parse +URAT response: %s", response);
|
|
return FALSE;
|
|
}
|
|
|
|
*out_allowed = allowed;
|
|
*out_preferred = preferred;
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* URAT=X command builder */
|
|
|
|
static gboolean
|
|
append_rat_value (GString *str,
|
|
MMModemMode mode,
|
|
GError **error)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (ublox_combinations); i++) {
|
|
if (ublox_combinations[i] == mode) {
|
|
g_string_append_printf (str, "%u", i);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"No AcT value matches requested mode");
|
|
return FALSE;
|
|
}
|
|
|
|
gchar *
|
|
mm_ublox_build_urat_set_command (MMModemMode allowed,
|
|
MMModemMode preferred,
|
|
GError **error)
|
|
{
|
|
GString *command;
|
|
|
|
command = g_string_new ("+URAT=");
|
|
if (!append_rat_value (command, allowed, error)) {
|
|
g_string_free (command, TRUE);
|
|
return NULL;
|
|
}
|
|
|
|
if (preferred != MM_MODEM_MODE_NONE) {
|
|
g_string_append (command, ",");
|
|
if (!append_rat_value (command, preferred, error)) {
|
|
g_string_free (command, TRUE);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return g_string_free (command, FALSE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* +UAUTHREQ=? test parser */
|
|
|
|
MMUbloxBearerAllowedAuth
|
|
mm_ublox_parse_uauthreq_test (const char *response,
|
|
gpointer log_object,
|
|
GError **error)
|
|
{
|
|
MMUbloxBearerAllowedAuth mask = MM_UBLOX_BEARER_ALLOWED_AUTH_UNKNOWN;
|
|
GError *inner_error = NULL;
|
|
GArray *allowed_auths = NULL;
|
|
gchar **split;
|
|
guint split_len;
|
|
|
|
/*
|
|
* Response may be like:
|
|
* AT+UAUTHREQ=?
|
|
* +UAUTHREQ: (1-4),(0-2),,
|
|
*/
|
|
response = mm_strip_tag (response, "+UAUTHREQ:");
|
|
split = mm_split_string_groups (response);
|
|
split_len = g_strv_length (split);
|
|
if (split_len < 2) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Unexpected number of groups in +UAUTHREQ=? response: %u", g_strv_length (split));
|
|
goto out;
|
|
}
|
|
|
|
allowed_auths = mm_parse_uint_list (split[1], &inner_error);
|
|
if (inner_error)
|
|
goto out;
|
|
|
|
if (allowed_auths) {
|
|
guint i;
|
|
|
|
for (i = 0; i < allowed_auths->len; i++) {
|
|
guint val;
|
|
|
|
val = g_array_index (allowed_auths, guint, i);
|
|
switch (val) {
|
|
case 0:
|
|
mask |= MM_UBLOX_BEARER_ALLOWED_AUTH_NONE;
|
|
break;
|
|
case 1:
|
|
mask |= MM_UBLOX_BEARER_ALLOWED_AUTH_PAP;
|
|
break;
|
|
case 2:
|
|
mask |= MM_UBLOX_BEARER_ALLOWED_AUTH_CHAP;
|
|
break;
|
|
case 3:
|
|
mask |= MM_UBLOX_BEARER_ALLOWED_AUTH_AUTO;
|
|
break;
|
|
default:
|
|
mm_obj_warn (log_object, "unexpected +UAUTHREQ value: %u", val);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!mask) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"No supported authentication methods in +UAUTHREQ=? response");
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
g_strfreev (split);
|
|
|
|
if (allowed_auths)
|
|
g_array_unref (allowed_auths);
|
|
|
|
if (inner_error) {
|
|
g_propagate_error (error, inner_error);
|
|
return MM_UBLOX_BEARER_ALLOWED_AUTH_UNKNOWN;
|
|
}
|
|
|
|
return mask;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* +UGCNTRD response parser */
|
|
|
|
gboolean
|
|
mm_ublox_parse_ugcntrd_response_for_cid (const gchar *response,
|
|
guint in_cid,
|
|
guint64 *out_session_tx_bytes,
|
|
guint64 *out_session_rx_bytes,
|
|
guint64 *out_total_tx_bytes,
|
|
guint64 *out_total_rx_bytes,
|
|
GError **error)
|
|
{
|
|
g_autoptr(GRegex) r = NULL;
|
|
g_autoptr(GMatchInfo) match_info = NULL;
|
|
GError *inner_error = NULL;
|
|
guint64 session_tx_bytes = 0;
|
|
guint64 session_rx_bytes = 0;
|
|
guint64 total_tx_bytes = 0;
|
|
guint64 total_rx_bytes = 0;
|
|
gboolean matched = FALSE;
|
|
|
|
/* Response may be e.g.:
|
|
* +UGCNTRD: 31,2704,1819,2724,1839
|
|
* We assume only ONE line is returned.
|
|
*/
|
|
r = g_regex_new ("\\+UGCNTRD:\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)",
|
|
G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL);
|
|
g_assert (r != NULL);
|
|
|
|
/* Report invalid CID given */
|
|
if (!in_cid) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid CID given");
|
|
goto out;
|
|
}
|
|
|
|
g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
|
|
while (!inner_error && g_match_info_matches (match_info)) {
|
|
guint cid = 0;
|
|
|
|
/* Matched CID? */
|
|
if (!mm_get_uint_from_match_info (match_info, 1, &cid) || cid != in_cid) {
|
|
g_match_info_next (match_info, &inner_error);
|
|
continue;
|
|
}
|
|
|
|
if (out_session_tx_bytes && !mm_get_u64_from_match_info (match_info, 2, &session_tx_bytes)) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing session TX bytes");
|
|
goto out;
|
|
}
|
|
|
|
if (out_session_rx_bytes && !mm_get_u64_from_match_info (match_info, 3, &session_rx_bytes)) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing session RX bytes");
|
|
goto out;
|
|
}
|
|
|
|
if (out_total_tx_bytes && !mm_get_u64_from_match_info (match_info, 4, &total_tx_bytes)) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing total TX bytes");
|
|
goto out;
|
|
}
|
|
|
|
if (out_total_rx_bytes && !mm_get_u64_from_match_info (match_info, 5, &total_rx_bytes)) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing total RX bytes");
|
|
goto out;
|
|
}
|
|
|
|
matched = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (!matched) {
|
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No statistics found for CID %u", in_cid);
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (inner_error) {
|
|
g_propagate_error (error, inner_error);
|
|
return FALSE;
|
|
}
|
|
|
|
if (out_session_tx_bytes)
|
|
*out_session_tx_bytes = session_tx_bytes;
|
|
if (out_session_rx_bytes)
|
|
*out_session_rx_bytes = session_rx_bytes;
|
|
if (out_total_tx_bytes)
|
|
*out_total_tx_bytes = total_tx_bytes;
|
|
if (out_total_rx_bytes)
|
|
*out_total_rx_bytes = total_rx_bytes;
|
|
return TRUE;
|
|
}
|