Files
ModemManager/plugins/telit/mm-modem-helpers-telit.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

953 lines
39 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) 2015-2019 Telit.
* Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <ModemManager.h>
#define _LIBMM_INSIDE_MMCLI
#include <libmm-glib.h>
#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-telit.h"
/*****************************************************************************/
/* AT#BND 2G values */
#define MM_MODEM_BAND_TELIT_2G_FIRST MM_MODEM_BAND_EGSM
#define MM_MODEM_BAND_TELIT_2G_LAST MM_MODEM_BAND_G850
#define B2G_FLAG(band) (1 << (band - MM_MODEM_BAND_TELIT_2G_FIRST))
/* Index of the array is the telit 2G band value [0-5]
* The bitmask value here is built from the 2G MMModemBand values right away. */
static const guint32 telit_2g_to_mm_band_mask[] = {
[0] = B2G_FLAG (MM_MODEM_BAND_EGSM) + B2G_FLAG (MM_MODEM_BAND_DCS),
[1] = B2G_FLAG (MM_MODEM_BAND_EGSM) + B2G_FLAG (MM_MODEM_BAND_PCS),
[2] = B2G_FLAG (MM_MODEM_BAND_DCS) + B2G_FLAG (MM_MODEM_BAND_G850),
[3] = B2G_FLAG (MM_MODEM_BAND_PCS) + B2G_FLAG (MM_MODEM_BAND_G850),
[4] = B2G_FLAG (MM_MODEM_BAND_EGSM) + B2G_FLAG (MM_MODEM_BAND_DCS) + B2G_FLAG (MM_MODEM_BAND_PCS),
[5] = B2G_FLAG (MM_MODEM_BAND_EGSM) + B2G_FLAG (MM_MODEM_BAND_DCS) + B2G_FLAG (MM_MODEM_BAND_PCS) + B2G_FLAG (MM_MODEM_BAND_G850),
};
/*****************************************************************************/
/* AT#BND 3G values */
/* NOTE: UTRAN_1 to UTRAN_9 enum values are NOT IN ORDER!
* E.g. numerically UTRAN_7 is after UTRAN_9...
*
* This array allows us to iterate them in a way which is a bit
* more friendly.
*/
static const guint band_utran_index[] = {
[MM_MODEM_BAND_UTRAN_1] = 1,
[MM_MODEM_BAND_UTRAN_2] = 2,
[MM_MODEM_BAND_UTRAN_3] = 3,
[MM_MODEM_BAND_UTRAN_4] = 4,
[MM_MODEM_BAND_UTRAN_5] = 5,
[MM_MODEM_BAND_UTRAN_6] = 6,
[MM_MODEM_BAND_UTRAN_7] = 7,
[MM_MODEM_BAND_UTRAN_8] = 8,
[MM_MODEM_BAND_UTRAN_9] = 9,
[MM_MODEM_BAND_UTRAN_10] = 10,
[MM_MODEM_BAND_UTRAN_11] = 11,
[MM_MODEM_BAND_UTRAN_12] = 12,
[MM_MODEM_BAND_UTRAN_13] = 13,
[MM_MODEM_BAND_UTRAN_14] = 14,
[MM_MODEM_BAND_UTRAN_19] = 19,
[MM_MODEM_BAND_UTRAN_20] = 20,
[MM_MODEM_BAND_UTRAN_21] = 21,
[MM_MODEM_BAND_UTRAN_22] = 22,
[MM_MODEM_BAND_UTRAN_25] = 25,
[MM_MODEM_BAND_UTRAN_26] = 26,
[MM_MODEM_BAND_UTRAN_32] = 32,
};
#define MM_MODEM_BAND_TELIT_3G_FIRST MM_MODEM_BAND_UTRAN_1
#define MM_MODEM_BAND_TELIT_3G_LAST MM_MODEM_BAND_UTRAN_19
#define B3G_NUM(band) band_utran_index[band]
#define B3G_FLAG(band) ((B3G_NUM (band) > 0) ? (1LL << (B3G_NUM (band) - B3G_NUM (MM_MODEM_BAND_TELIT_3G_FIRST))) : 0)
/* Index of the arrays is the telit 3G band value.
* The bitmask value here is built from the 3G MMModemBand values right away.
*
* We have 2 different sets of bands that are different for values >=12, because
* the LM940/960 models support a different set of 3G bands.
*/
#define TELIT_3G_TO_MM_BAND_MASK_DEFAULT_N_ELEMENTS 27
static guint64 telit_3g_to_mm_band_mask_default[TELIT_3G_TO_MM_BAND_MASK_DEFAULT_N_ELEMENTS];
#define TELIT_3G_TO_MM_BAND_MASK_ALTERNATE_N_ELEMENTS 20
static guint64 telit_3g_to_mm_band_mask_alternate[TELIT_3G_TO_MM_BAND_MASK_ALTERNATE_N_ELEMENTS];
static void
initialize_telit_3g_to_mm_band_masks (void)
{
static gboolean initialized = FALSE;
/* We need to initialize the arrays in runtime because gcc < 8 doesn't like initializing
* with operations that are using the band_utran_index constant array elements */
if (initialized)
return;
telit_3g_to_mm_band_mask_default[0] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1);
telit_3g_to_mm_band_mask_default[1] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2);
telit_3g_to_mm_band_mask_default[2] = B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
telit_3g_to_mm_band_mask_default[3] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
telit_3g_to_mm_band_mask_default[4] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
telit_3g_to_mm_band_mask_default[5] = B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
telit_3g_to_mm_band_mask_default[6] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
telit_3g_to_mm_band_mask_default[7] = B3G_FLAG (MM_MODEM_BAND_UTRAN_4);
telit_3g_to_mm_band_mask_default[8] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
telit_3g_to_mm_band_mask_default[9] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
telit_3g_to_mm_band_mask_default[10] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
telit_3g_to_mm_band_mask_default[11] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
telit_3g_to_mm_band_mask_default[12] = B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
telit_3g_to_mm_band_mask_default[13] = B3G_FLAG (MM_MODEM_BAND_UTRAN_3);
telit_3g_to_mm_band_mask_default[14] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
telit_3g_to_mm_band_mask_default[15] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
telit_3g_to_mm_band_mask_default[16] = B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
telit_3g_to_mm_band_mask_default[17] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) +
B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
telit_3g_to_mm_band_mask_default[18] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6) +
B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
telit_3g_to_mm_band_mask_default[19] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
telit_3g_to_mm_band_mask_default[20] = B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
telit_3g_to_mm_band_mask_default[21] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
telit_3g_to_mm_band_mask_default[22] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) +
B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
telit_3g_to_mm_band_mask_default[23] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3);
telit_3g_to_mm_band_mask_default[24] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
telit_3g_to_mm_band_mask_default[25] = B3G_FLAG (MM_MODEM_BAND_UTRAN_19);
telit_3g_to_mm_band_mask_default[26] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6) +
B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_19);
/* Initialize alternate 3G band set */
telit_3g_to_mm_band_mask_alternate[0] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1);
telit_3g_to_mm_band_mask_alternate[1] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2);
telit_3g_to_mm_band_mask_alternate[2] = B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
telit_3g_to_mm_band_mask_alternate[3] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
telit_3g_to_mm_band_mask_alternate[4] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
telit_3g_to_mm_band_mask_alternate[5] = B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
telit_3g_to_mm_band_mask_alternate[6] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
telit_3g_to_mm_band_mask_alternate[7] = B3G_FLAG (MM_MODEM_BAND_UTRAN_4);
telit_3g_to_mm_band_mask_alternate[8] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
telit_3g_to_mm_band_mask_alternate[9] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
telit_3g_to_mm_band_mask_alternate[10] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
telit_3g_to_mm_band_mask_alternate[11] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
telit_3g_to_mm_band_mask_alternate[12] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) +
B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
telit_3g_to_mm_band_mask_alternate[13] = B3G_FLAG (MM_MODEM_BAND_UTRAN_3);
telit_3g_to_mm_band_mask_alternate[14] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
telit_3g_to_mm_band_mask_alternate[15] = B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
telit_3g_to_mm_band_mask_alternate[16] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) +
B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
telit_3g_to_mm_band_mask_alternate[17] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
telit_3g_to_mm_band_mask_alternate[18] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_9) +
B3G_FLAG (MM_MODEM_BAND_UTRAN_19);
telit_3g_to_mm_band_mask_alternate[19] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) +
B3G_FLAG (MM_MODEM_BAND_UTRAN_9) + B3G_FLAG (MM_MODEM_BAND_UTRAN_19);
}
/*****************************************************************************/
/* AT#BND 4G values
*
* The Telit-specific value for 4G bands is a bitmask of the band values, given
* in hexadecimal or decimal format.
*/
#define MM_MODEM_BAND_TELIT_4G_FIRST MM_MODEM_BAND_EUTRAN_1
#define MM_MODEM_BAND_TELIT_4G_LAST MM_MODEM_BAND_EUTRAN_64
#define B4G_FLAG(band) (((guint64) 1) << (band - MM_MODEM_BAND_TELIT_4G_FIRST))
#define MM_MODEM_BAND_TELIT_EXT_4G_FIRST MM_MODEM_BAND_EUTRAN_65
#define MM_MODEM_BAND_TELIT_EXT_4G_LAST MM_MODEM_BAND_EUTRAN_71
#define B4G_FLAG_EXT(band) (((guint64) 1) << (band - MM_MODEM_BAND_TELIT_EXT_4G_FIRST))
/*****************************************************************************/
/* Set current bands helpers */
gchar *
mm_telit_build_bnd_request (GArray *bands_array,
gboolean modem_is_2g,
gboolean modem_is_3g,
gboolean modem_is_4g,
gboolean modem_alternate_3g_bands,
gboolean modem_ext_4g_bands,
GError **error)
{
guint32 mask2g = 0;
guint64 mask3g = 0;
guint64 mask4g = 0;
guint64 mask4gext = 0;
guint i;
gint flag2g = -1;
gint64 flag3g = -1;
gint64 flag4g = -1;
gchar *cmd;
const guint64 *telit_3g_to_mm_band_mask;
guint telit_3g_to_mm_band_mask_n_elements;
initialize_telit_3g_to_mm_band_masks ();
/* Select correct 3G band mask */
if (modem_alternate_3g_bands) {
telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_alternate;
telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_alternate);
} else {
telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_default;
telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_default);
}
for (i = 0; i < bands_array->len; i++) {
MMModemBand band;
band = g_array_index (bands_array, MMModemBand, i);
/* Convert 2G bands into a bitmask, to match against telit_2g_to_mm_band_mask. */
if (modem_is_2g && mm_common_band_is_gsm (band) &&
(band >= MM_MODEM_BAND_TELIT_2G_FIRST) && (band <= MM_MODEM_BAND_TELIT_2G_LAST))
mask2g += B2G_FLAG (band);
/* Convert 3G bands into a bitmask, to match against telit_3g_to_mm_band_mask. We use
* a 64-bit explicit bitmask so that all values fit correctly. */
if (modem_is_3g && mm_common_band_is_utran (band) &&
(B3G_NUM (band) >= B3G_NUM (MM_MODEM_BAND_TELIT_3G_FIRST)) && (B3G_NUM (band) <= B3G_NUM (MM_MODEM_BAND_TELIT_3G_LAST)))
mask3g += B3G_FLAG (band);
/* Convert 4G bands into a bitmask. We use a 64bit explicit bitmask so that
* all values fit correctly. */
if (modem_is_4g && mm_common_band_is_eutran (band)) {
if (band >= MM_MODEM_BAND_TELIT_4G_FIRST && band <= MM_MODEM_BAND_TELIT_4G_LAST)
mask4g += B4G_FLAG (band);
else if (band >= MM_MODEM_BAND_TELIT_EXT_4G_FIRST && band <= MM_MODEM_BAND_TELIT_EXT_4G_LAST)
mask4gext += B4G_FLAG_EXT (band);
}
}
/* Get 2G-specific telit value */
if (mask2g) {
for (i = 0; i < G_N_ELEMENTS (telit_2g_to_mm_band_mask); i++) {
if (mask2g == telit_2g_to_mm_band_mask[i]) {
flag2g = i;
break;
}
}
if (flag2g == -1) {
gchar *bands_str;
bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)(bands_array->data), bands_array->len);
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Couldn't find matching 2G bands Telit value for band combination: '%s'", bands_str);
g_free (bands_str);
return NULL;
}
}
/* Get 3G-specific telit value */
if (mask3g) {
for (i = 0; i < telit_3g_to_mm_band_mask_n_elements; i++) {
if (mask3g == telit_3g_to_mm_band_mask[i]) {
flag3g = i;
break;
}
}
if (flag3g == -1) {
gchar *bands_str;
bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)(bands_array->data), bands_array->len);
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Couldn't find matching 3G bands Telit value for band combination: '%s'", bands_str);
g_free (bands_str);
return NULL;
}
}
/* Get 4G-specific telit band bitmask */
flag4g = (mask4g != 0) ? ((gint64)mask4g) : -1;
/* If the modem supports a given access tech, we must always give band settings
* for the specific tech */
if (modem_is_2g && flag2g == -1) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
"None or invalid 2G bands combination in the provided list");
return NULL;
}
if (modem_is_3g && flag3g == -1) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
"None or invalid 3G bands combination in the provided list");
return NULL;
}
if (modem_is_4g && mask4g == 0 && mask4gext == 0) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
"None or invalid 4G bands combination in the provided list");
return NULL;
}
if (modem_is_2g && !modem_is_3g && !modem_is_4g)
cmd = g_strdup_printf ("#BND=%d", flag2g);
else if (!modem_is_2g && modem_is_3g && !modem_is_4g)
cmd = g_strdup_printf ("#BND=0,%" G_GINT64_FORMAT, flag3g);
else if (modem_is_2g && modem_is_3g && !modem_is_4g)
cmd = g_strdup_printf ("#BND=%d,%" G_GINT64_FORMAT, flag2g, flag3g);
else if (!modem_is_2g && !modem_is_3g && modem_is_4g) {
if (!modem_ext_4g_bands)
cmd = g_strdup_printf ("#BND=0,0,%" G_GINT64_FORMAT, flag4g);
else
cmd = g_strdup_printf ("#BND=0,0,%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", mask4g, mask4gext);
} else if (!modem_is_2g && modem_is_3g && modem_is_4g) {
if (!modem_ext_4g_bands)
cmd = g_strdup_printf ("#BND=0,%" G_GINT64_FORMAT ",%" G_GINT64_FORMAT, flag3g, flag4g);
else
cmd = g_strdup_printf ("#BND=0,%" G_GINT64_FORMAT ",%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", flag3g, mask4g, mask4gext);
} else if (modem_is_2g && !modem_is_3g && modem_is_4g) {
if (!modem_ext_4g_bands)
cmd = g_strdup_printf ("#BND=%d,0,%" G_GINT64_FORMAT, flag2g, flag4g);
else
cmd = g_strdup_printf ("#BND=%d,0,%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", flag2g, mask4g, mask4gext);
} else if (modem_is_2g && modem_is_3g && modem_is_4g) {
if (!modem_ext_4g_bands)
cmd = g_strdup_printf ("#BND=%d,%" G_GINT64_FORMAT ",%" G_GINT64_FORMAT, flag2g, flag3g, flag4g);
else
cmd = g_strdup_printf ("#BND=%d,%" G_GINT64_FORMAT ",%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", flag2g, flag3g, mask4g, mask4gext);
} else
g_assert_not_reached ();
return cmd;
}
/*****************************************************************************/
/* #BND response parser
*
* AT#BND=?
* #BND: <2G band flags range>,<3G band flags range>[, <4G band flags range>]
*
* where "band flags" is a list of numbers defining the supported bands.
* Note that the one Telit band flag may represent more than one MM band.
*
* e.g.
*
* #BND: (0-2),(3,4)
*
* (0,2) = 2G band flag 0 is EGSM + DCS
* = 2G band flag 1 is EGSM + PCS
* = 2G band flag 2 is DCS + G850
* (3,4) = 3G band flag 3 is U2100 + U1900 + U850
* = 3G band flag 4 is U1900 + U850
*
* Modems that supports 4G bands, return a range value(X-Y) where
* X: represent the lower supported band, such as X = 2^(B-1), being B = B1, B2,..., B32
* Y: is a 32 bit number resulting from a mask of all the supported bands:
* 1 - B1
* 2 - B2
* 4 - B3
* 8 - B4
* ...
* i - B(2exp(i-1))
* ...
* 2147483648 - B32
*
* e.g.
* (2-4106)
* 2 = 2^1 --> lower supported band B2
* 4106 = 2^1 + 2^3 + 2^12 --> the supported bands are B2, B4, B13
*
*
* AT#BND?
* #BND: <2G band flags>,<3G band flags>[, <4G band flags>]
*
* where "band flags" is a number defining the current bands.
* Note that the one Telit band flag may represent more than one MM band.
*
* e.g.
*
* #BND: 0,4
*
* 0 = 2G band flag 0 is EGSM + DCS
* 4 = 3G band flag 4 is U1900 + U850
*
* ----------------
*
* For modems such as LN920 the #BND configuration/response for LTE bands is different
* from what is explained above:
*
* AT#BND=<GSM_band>[,<UMTS_band>[,<LTE_band>[,<LTE_band_ext>]]]
*
* <LTE_band>: hex: Indicates the LTE supported bands expressed as the sum of Band number (1+2+8 ...) calculated as shown in the table (mask of 64 bits):
*
* Band number(Hex) Band i
* 0 disable
* 1 B1
* 2 B2
* 4 B3
* 8 B4
* ...
* ...
* 80000000 B32
* ...
* ...
* 800000000000 B48
*
* It can take value, 0 - 87A03B0F38DF: range of the sum of Band number (1+2+4 ...)
*
* <LTE_band_ext>: hex: Indicates the LTE supported bands from B65 expressed as the sum of Band number (1+2+8 ...) calculated as shown in the table (mask of 64 bits):
*
* Band number(Hex) Band i
* 0 disable
* 2 B66
* 40 B71
*
* It can take value, 0 - 42: range of the sum of Band number (2+40)
*
* Note: LTE_band and LTE_band_ext cannot be 0 at the same time
*
* Example output:
*
* AT#BND=?
* #BND: (0),(0-11,17,18),(87A03B0F38DF),(42)
*
* AT#BND?
* #BND: 0,18,87A03B0F38DF,42
*
*/
static gboolean
telit_get_2g_mm_bands (GMatchInfo *match_info,
gpointer log_object,
GArray **bands,
GError **error)
{
GError *inner_error = NULL;
GArray *values = NULL;
gchar *match_str = NULL;
guint i;
match_str = g_match_info_fetch_named (match_info, "Bands2G");
if (!match_str || match_str[0] == '\0') {
g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Could not find 2G band values from response");
goto out;
}
values = mm_parse_uint_list (match_str, &inner_error);
if (!values)
goto out;
for (i = 0; i < values->len; i++) {
guint value;
value = g_array_index (values, guint, i);
if (value < G_N_ELEMENTS (telit_2g_to_mm_band_mask)) {
guint j;
for (j = MM_MODEM_BAND_TELIT_2G_FIRST; j <= MM_MODEM_BAND_TELIT_2G_LAST; j++) {
if ((telit_2g_to_mm_band_mask[value] & B2G_FLAG (j)) && !mm_common_bands_garray_lookup (*bands, j))
*bands = g_array_append_val (*bands, j);
}
} else
mm_obj_dbg (log_object, "unhandled telit 2G band value configuration: %u", value);
}
out:
g_free (match_str);
g_clear_pointer (&values, g_array_unref);
if (inner_error) {
g_propagate_error (error, inner_error);
return FALSE;
}
return TRUE;
}
static gboolean
telit_get_3g_mm_bands (GMatchInfo *match_info,
gpointer log_object,
gboolean modem_alternate_3g_bands,
GArray **bands,
GError **error)
{
GError *inner_error = NULL;
GArray *values = NULL;
gchar *match_str = NULL;
guint i;
const guint64 *telit_3g_to_mm_band_mask;
guint telit_3g_to_mm_band_mask_n_elements;
initialize_telit_3g_to_mm_band_masks ();
/* Select correct 3G band mask */
if (modem_alternate_3g_bands) {
telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_alternate;
telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_alternate);
} else {
telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_default;
telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_default);
}
match_str = g_match_info_fetch_named (match_info, "Bands3G");
if (!match_str || match_str[0] == '\0') {
g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Could not find 3G band values from response");
goto out;
}
values = mm_parse_uint_list (match_str, &inner_error);
if (!values)
goto out;
for (i = 0; i < values->len; i++) {
guint value;
value = g_array_index (values, guint, i);
if (value < telit_3g_to_mm_band_mask_n_elements) {
guint j;
for (j = 0; j < G_N_ELEMENTS (band_utran_index); j++) {
/* ignore non-3G bands */
if (band_utran_index[j] == 0)
continue;
if ((telit_3g_to_mm_band_mask[value] & B3G_FLAG (j)) && !mm_common_bands_garray_lookup (*bands, j))
*bands = g_array_append_val (*bands, j);
}
} else
mm_obj_dbg (log_object, "unhandled telit 3G band value configuration: %u", value);
}
out:
g_free (match_str);
g_clear_pointer (&values, g_array_unref);
if (inner_error) {
g_propagate_error (error, inner_error);
return FALSE;
}
return TRUE;
}
static gboolean
telit_get_4g_mm_bands (GMatchInfo *match_info,
GArray **bands,
GError **error)
{
GError *inner_error = NULL;
MMModemBand band;
gchar *match_str = NULL;
guint64 value;
gchar **tokens = NULL;
gboolean hex_format = FALSE;
gboolean ok;
match_str = g_match_info_fetch_named (match_info, "Bands4GDec");
if (!match_str) {
match_str = g_match_info_fetch_named (match_info, "Bands4GHex");
hex_format = match_str != NULL;
}
if (!match_str || match_str[0] == '\0') {
g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Could not find 4G band flags from response");
goto out;
}
/* splitting will never return NULL as string is not empty */
tokens = g_strsplit (match_str, "-", -1);
/* If this is a range, get upper threshold, which contains the total supported mask */
ok = hex_format?
mm_get_u64_from_hex_str (tokens[1] ? tokens[1] : tokens[0], &value):
mm_get_u64_from_str (tokens[1] ? tokens[1] : tokens[0], &value);
if (!ok) {
g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Could not parse 4G band mask from string: '%s'", match_str);
goto out;
}
for (band = MM_MODEM_BAND_TELIT_4G_FIRST; band <= MM_MODEM_BAND_TELIT_4G_LAST; band++) {
if ((value & B4G_FLAG (band)) && !mm_common_bands_garray_lookup (*bands, band))
g_array_append_val (*bands, band);
}
out:
g_strfreev (tokens);
g_free (match_str);
if (inner_error) {
g_propagate_error (error, inner_error);
return FALSE;
}
return TRUE;
}
static gboolean
telit_get_ext_4g_mm_bands (GMatchInfo *match_info,
GArray **bands,
GError **error)
{
GError *inner_error = NULL;
MMModemBand band;
gchar *match_str = NULL;
gchar *match_str_ext = NULL;
guint64 value;
match_str = g_match_info_fetch_named (match_info, "Bands4GHex");
if (!match_str || match_str[0] == '\0') {
g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Could not find 4G band hex mask flag from response");
goto out;
}
if (!mm_get_u64_from_hex_str (match_str, &value)) {
g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Could not parse 4G band hex mask from string: '%s'", match_str);
goto out;
}
for (band = MM_MODEM_BAND_TELIT_4G_FIRST; band <= MM_MODEM_BAND_TELIT_4G_LAST; band++) {
if ((value & B4G_FLAG (band)) && !mm_common_bands_garray_lookup (*bands, band))
g_array_append_val (*bands, band);
}
/* extended bands */
match_str_ext = g_match_info_fetch_named (match_info, "Bands4GExt");
if (match_str_ext) {
if (!mm_get_u64_from_hex_str (match_str_ext, &value)) {
g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Could not parse 4G ext band mask from string: '%s'", match_str_ext);
goto out;
}
for (band = MM_MODEM_BAND_TELIT_EXT_4G_FIRST; band <= MM_MODEM_BAND_TELIT_EXT_4G_LAST; band++) {
if ((value & B4G_FLAG_EXT (band)) && !mm_common_bands_garray_lookup (*bands, band))
g_array_append_val (*bands, band);
}
}
out:
g_free (match_str);
g_free (match_str_ext);
if (inner_error) {
g_propagate_error (error, inner_error);
return FALSE;
}
return TRUE;
}
typedef enum {
LOAD_BANDS_TYPE_SUPPORTED,
LOAD_BANDS_TYPE_CURRENT,
} LoadBandsType;
static gboolean
bnd_response_has_ext_4g_bands (const gchar *response)
{
g_auto(GStrv) tokens = NULL;
tokens = mm_split_string_groups (response);
return g_strv_length (tokens) == 4;
}
/* Regex tokens for #BND parsing */
#define MM_SUPPORTED_BANDS_2G "\\s*\\((?P<Bands2G>[0-9\\-,]*)\\)"
#define MM_SUPPORTED_BANDS_3G "(,\\s*\\((?P<Bands3G>[0-9\\-,]*)\\))?"
#define MM_SUPPORTED_BANDS_4G_HEX "(,\\s*\\((?P<Bands4GHex>[0-9A-F\\-,]*)\\))?"
#define MM_SUPPORTED_BANDS_4G_DEC "(,\\s*\\((?P<Bands4GDec>[0-9\\-,]*)\\))?"
#define MM_SUPPORTED_BANDS_4G_EXT "(,\\s*\\((?P<Bands4GHex>[0-9A-F]+)\\))?(,\\s*\\((?P<Bands4GExt>[0-9A-F]+)\\))?"
#define MM_CURRENT_BANDS_2G "\\s*(?P<Bands2G>\\d+)"
#define MM_CURRENT_BANDS_3G "(,\\s*(?P<Bands3G>\\d+))?"
#define MM_CURRENT_BANDS_4G_HEX "(,\\s*(?P<Bands4GHex>[0-9A-F]+))?"
#define MM_CURRENT_BANDS_4G_DEC "(,\\s*(?P<Bands4GDec>\\d+))?"
#define MM_CURRENT_BANDS_4G_EXT "(,\\s*(?P<Bands4GHex>[0-9A-F]+))?(,\\s*(?P<Bands4GExt>[0-9A-F]+))?"
static GArray *
common_parse_bnd_response (const gchar *response,
gboolean modem_is_2g,
gboolean modem_is_3g,
gboolean modem_is_4g,
gboolean modem_alternate_3g_bands,
gboolean modem_has_hex_format_4g_bands,
gboolean modem_ext_4g_bands,
LoadBandsType load_type,
gpointer log_object,
GError **error)
{
g_autoptr(GMatchInfo) match_info = NULL;
g_autoptr(GRegex) r = NULL;
GError *inner_error = NULL;
GArray *bands = NULL;
const gchar *load_bands_regex = NULL;
static const gchar *load_bands_regex_4g_hex[] = {
[LOAD_BANDS_TYPE_SUPPORTED] = "#BND:"MM_SUPPORTED_BANDS_2G MM_SUPPORTED_BANDS_3G MM_SUPPORTED_BANDS_4G_HEX,
[LOAD_BANDS_TYPE_CURRENT] = "#BND:"MM_CURRENT_BANDS_2G MM_CURRENT_BANDS_3G MM_CURRENT_BANDS_4G_HEX,
};
static const gchar *load_bands_regex_4g_dec[] = {
[LOAD_BANDS_TYPE_SUPPORTED] = "#BND:"MM_SUPPORTED_BANDS_2G MM_SUPPORTED_BANDS_3G MM_SUPPORTED_BANDS_4G_DEC,
[LOAD_BANDS_TYPE_CURRENT] = "#BND:"MM_CURRENT_BANDS_2G MM_CURRENT_BANDS_3G MM_CURRENT_BANDS_4G_DEC,
};
static const gchar *load_bands_regex_4g_ext[] = {
[LOAD_BANDS_TYPE_SUPPORTED] = "#BND:"MM_SUPPORTED_BANDS_2G MM_SUPPORTED_BANDS_3G MM_SUPPORTED_BANDS_4G_EXT,
[LOAD_BANDS_TYPE_CURRENT] = "#BND:"MM_CURRENT_BANDS_2G MM_CURRENT_BANDS_3G MM_CURRENT_BANDS_4G_EXT,
};
if (modem_ext_4g_bands)
load_bands_regex = load_bands_regex_4g_ext[load_type];
else if (modem_has_hex_format_4g_bands)
load_bands_regex = load_bands_regex_4g_hex[load_type];
else
load_bands_regex = load_bands_regex_4g_dec[load_type];
r = g_regex_new (load_bands_regex, G_REGEX_RAW, 0, NULL);
g_assert (r);
if (!g_regex_match (r, response, 0, &match_info)) {
g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Could not parse response '%s'", response);
goto out;
}
if (!g_match_info_matches (match_info)) {
g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Could not find matches in response '%s'", response);
goto out;
}
bands = g_array_new (TRUE, TRUE, sizeof (MMModemBand));
if (modem_is_2g && !telit_get_2g_mm_bands (match_info, log_object, &bands, &inner_error))
goto out;
if (modem_is_3g && !telit_get_3g_mm_bands (match_info, log_object, modem_alternate_3g_bands, &bands, &inner_error))
goto out;
if (modem_is_4g) {
gboolean ok;
ok = modem_ext_4g_bands?
telit_get_ext_4g_mm_bands (match_info, &bands, &inner_error) :
telit_get_4g_mm_bands (match_info, &bands, &inner_error);
if (!ok)
goto out;
}
out:
if (inner_error) {
g_propagate_error (error, inner_error);
g_clear_pointer (&bands, g_array_unref);
return NULL;
}
return bands;
}
GArray *
mm_telit_parse_bnd_query_response (const gchar *response,
gboolean modem_is_2g,
gboolean modem_is_3g,
gboolean modem_is_4g,
gboolean modem_alternate_3g_bands,
gboolean modem_has_hex_format_4g_bands,
gboolean modem_ext_4g_bands,
gpointer log_object,
GError **error)
{
return common_parse_bnd_response (response,
modem_is_2g, modem_is_3g, modem_is_4g,
modem_alternate_3g_bands,
modem_has_hex_format_4g_bands,
modem_ext_4g_bands,
LOAD_BANDS_TYPE_CURRENT,
log_object,
error);
}
GArray *
mm_telit_parse_bnd_test_response (const gchar *response,
gboolean modem_is_2g,
gboolean modem_is_3g,
gboolean modem_is_4g,
gboolean modem_alternate_3g_bands,
gboolean modem_has_hex_format_4g_bands,
gboolean *modem_ext_4g_bands,
gpointer log_object,
GError **error)
{
*modem_ext_4g_bands = bnd_response_has_ext_4g_bands (response);
return common_parse_bnd_response (response,
modem_is_2g, modem_is_3g, modem_is_4g,
modem_alternate_3g_bands,
modem_has_hex_format_4g_bands,
*modem_ext_4g_bands,
LOAD_BANDS_TYPE_SUPPORTED,
log_object,
error);
}
/*****************************************************************************/
/* #QSS? response parser */
MMTelitQssStatus
mm_telit_parse_qss_query (const gchar *response,
GError **error)
{
gint qss_status;
gint qss_mode;
qss_status = QSS_STATUS_UNKNOWN;
if (sscanf (response, "#QSS: %d,%d", &qss_mode, &qss_status) != 2) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Could not parse \"#QSS?\" response: %s", response);
return QSS_STATUS_UNKNOWN;
}
switch (qss_status) {
case QSS_STATUS_SIM_REMOVED:
case QSS_STATUS_SIM_INSERTED:
case QSS_STATUS_SIM_INSERTED_AND_UNLOCKED:
case QSS_STATUS_SIM_INSERTED_AND_READY:
return (MMTelitQssStatus) qss_status;
default:
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Unknown QSS status value given: %d", qss_status);
return QSS_STATUS_UNKNOWN;
}
}
/*****************************************************************************/
/* Supported modes list helper */
GArray *
mm_telit_build_modes_list (void)
{
GArray *combinations;
MMModemModeCombination mode;
/* Build list of combinations for 3GPP devices */
combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 7);
/* 2G only */
mode.allowed = MM_MODEM_MODE_2G;
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
/* 3G only */
mode.allowed = MM_MODEM_MODE_3G;
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
/* 2G and 3G */
mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
/* 4G only */
mode.allowed = MM_MODEM_MODE_4G;
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
/* 2G and 4G */
mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G);
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
/* 3G and 4G */
mode.allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
/* 2G, 3G and 4G */
mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
return combinations;
}
/*****************************************************************************/
/* Software Package version response parser */
gchar *
mm_telit_parse_swpkgv_response (const gchar *response)
{
gchar *version = NULL;
g_autofree gchar *base_version = NULL;
g_autoptr(GRegex) r = NULL;
g_autoptr(GMatchInfo) match_info = NULL;
guint matches;
/* We are interested only in the first line of the response */
r = g_regex_new ("(?P<Base>\\d{2}.\\d{2}.*)",
G_REGEX_RAW | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF,
G_REGEX_MATCH_NEWLINE_CR,
NULL);
g_assert (r != NULL);
if (!g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, NULL)) {
return NULL;
}
matches = g_match_info_get_match_count (match_info);
if (matches < 2 || matches > 4) {
return NULL;
}
base_version = g_match_info_fetch_named (match_info, "Base");
if (base_version)
version = g_strdup (base_version);
return version;
}
/*****************************************************************************/
/* MM Telit Model from revision string */
MMTelitModel
mm_telit_model_from_revision (const gchar *revision)
{
guint i;
static const struct {
const gchar *revision_prefix;
MMTelitModel model;
} revision_to_model_map [] = {
{"24.01", MM_TELIT_MODEL_LM940},
{"25.", MM_TELIT_MODEL_LE910C1},
{"32.", MM_TELIT_MODEL_LM960},
{"38.", MM_TELIT_MODEL_FN980},
{"40.", MM_TELIT_MODEL_LN920}
};
if (!revision)
return MM_TELIT_MODEL_DEFAULT;
for (i = 0; i < G_N_ELEMENTS (revision_to_model_map); ++i) {
if (g_str_has_prefix (revision, revision_to_model_map[i].revision_prefix))
return revision_to_model_map[i].model;
}
return MM_TELIT_MODEL_DEFAULT;
}