| /* -*- 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; |
| |
| match_str = g_match_info_fetch_named (match_info, "Bands4G"); |
| 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 */ |
| if (!mm_get_u64_from_str (tokens[1] ? tokens[1] : tokens[0], &value)) { |
| 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, "Bands4G"); |
| 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; |
| } |
| |
| 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_ext_4g_bands, |
| LoadBandsType load_type, |
| gpointer log_object, |
| GError **error) |
| { |
| GError *inner_error = NULL; |
| GArray *bands = NULL; |
| GMatchInfo *match_info = NULL; |
| GRegex *r; |
| |
| if (!modem_ext_4g_bands) { |
| static const gchar *load_bands_regex[] = { |
| [LOAD_BANDS_TYPE_SUPPORTED] = "#BND:\\s*\\((?P<Bands2G>[0-9\\-,]*)\\)(,\\s*\\((?P<Bands3G>[0-9\\-,]*)\\))?(,\\s*\\((?P<Bands4G>[0-9\\-,]*)\\))?", |
| [LOAD_BANDS_TYPE_CURRENT] = "#BND:\\s*(?P<Bands2G>\\d+)(,\\s*(?P<Bands3G>\\d+))?(,\\s*(?P<Bands4G>\\d+))?", |
| }; |
| |
| r = g_regex_new (load_bands_regex[load_type], G_REGEX_RAW, 0, NULL); |
| } else { |
| static const gchar *load_bands_regex_hex[] = { |
| [LOAD_BANDS_TYPE_SUPPORTED] = "#BND:\\s*\\((?P<Bands2G>[0-9\\-,]*)\\)(,\\s*\\((?P<Bands3G>[0-9\\-,]*)\\))?(,\\s*\\((?P<Bands4G>[0-9A-F]+)\\))?(,\\s*\\((?P<Bands4GExt>[0-9A-F]+)\\))?", |
| [LOAD_BANDS_TYPE_CURRENT] = "#BND:\\s*(?P<Bands2G>\\d+)(,\\s*(?P<Bands3G>\\d+))?(,\\s*(?P<Bands4G>[0-9A-F]+))?(,\\s*(?P<Bands4GExt>[0-9A-F]+))?", |
| }; |
| |
| r = g_regex_new (load_bands_regex_hex[load_type], 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 && !modem_ext_4g_bands && !telit_get_4g_mm_bands (match_info, &bands, &inner_error)) |
| goto out; |
| |
| if (modem_is_4g && modem_ext_4g_bands && !telit_get_ext_4g_mm_bands (match_info, &bands, &inner_error)) |
| goto out; |
| |
| out: |
| g_match_info_free (match_info); |
| g_regex_unref (r); |
| |
| 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_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_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_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_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; |
| } |