Files
ModemManager/plugins/ublox/mm-modem-helpers-ublox.c
T
Aleksander Morgado 74fc5baca2 core: port GRegex/GMatchInfo to use autoptr()
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.
2022-09-05 17:33:11 +00:00

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;
}