diff options
Diffstat (limited to 'plugins/huawei/mm-modem-helpers-huawei.c')
-rw-r--r-- | plugins/huawei/mm-modem-helpers-huawei.c | 1546 |
1 files changed, 0 insertions, 1546 deletions
diff --git a/plugins/huawei/mm-modem-helpers-huawei.c b/plugins/huawei/mm-modem-helpers-huawei.c deleted file mode 100644 index 67bb7089..00000000 --- a/plugins/huawei/mm-modem-helpers-huawei.c +++ /dev/null @@ -1,1546 +0,0 @@ -/* -*- 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) 2013 Huawei Technologies Co., Ltd - * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org> - */ - -#include <string.h> -#include <stdlib.h> -#include <stdio.h> - -#include <ModemManager.h> -#define _LIBMM_INSIDE_MM -#include <libmm-glib.h> - -#include "mm-log-object.h" -#include "mm-common-helpers.h" -#include "mm-modem-helpers.h" -#include "mm-modem-helpers-huawei.h" -#include "mm-huawei-enums-types.h" - -/*****************************************************************************/ -/* ^NDISSTAT / ^NDISSTATQRY response parser */ - -gboolean -mm_huawei_parse_ndisstatqry_response (const gchar *response, - gboolean *ipv4_available, - gboolean *ipv4_connected, - gboolean *ipv6_available, - gboolean *ipv6_connected, - GError **error) -{ - GError *inner_error = NULL; - - if (!response || - !(g_ascii_strncasecmp (response, "^NDISSTAT:", strlen ("^NDISSTAT:")) == 0 || - g_ascii_strncasecmp (response, "^NDISSTATQRY:", strlen ("^NDISSTATQRY:")) == 0)) { - g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing ^NDISSTAT / ^NDISSTATQRY prefix"); - return FALSE; - } - - *ipv4_available = FALSE; - *ipv6_available = FALSE; - - /* The response maybe as: - * ^NDISSTAT: 1,,,IPV4 - * ^NDISSTAT: 0,33,,IPV6 - * ^NDISSTATQRY: 1,,,IPV4 - * ^NDISSTATQRY: 0,33,,IPV6 - * OK - * - * Or, in newer firmwares: - * ^NDISSTATQRY:0,,,"IPV4",0,,,"IPV6" - * OK - * - * Or, even (handled separately): - * ^NDISSTATQry:1 - * OK - */ - - /* If multiple fields available, try first parsing method */ - if (strchr (response, ',')) { - g_autoptr(GRegex) r = NULL; - g_autoptr(GMatchInfo) match_info = NULL; - - r = g_regex_new ("\\^NDISSTAT(?:QRY)?(?:Qry)?:\\s*(\\d),([^,]*),([^,]*),([^,\\r\\n]*)(?:\\r\\n)?" - "(?:\\^NDISSTAT:|\\^NDISSTATQRY:)?\\s*,?(\\d)?,?([^,]*)?,?([^,]*)?,?([^,\\r\\n]*)?(?:\\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)) { - guint ip_type_field = 4; - - /* IPv4 and IPv6 are fields 4 and (if available) 8 */ - - while (!inner_error && ip_type_field <= 8) { - gchar *ip_type_str; - guint connected; - - ip_type_str = mm_get_string_unquoted_from_match_info (match_info, ip_type_field); - if (!ip_type_str) - break; - - if (!mm_get_uint_from_match_info (match_info, (ip_type_field - 3), &connected) || - (connected != 0 && connected != 1)) { - inner_error = g_error_new (MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't parse ^NDISSTAT / ^NDISSTATQRY fields"); - } else if (g_ascii_strcasecmp (ip_type_str, "IPV4") == 0) { - *ipv4_available = TRUE; - *ipv4_connected = (gboolean)connected; - } else if (g_ascii_strcasecmp (ip_type_str, "IPV6") == 0) { - *ipv6_available = TRUE; - *ipv6_connected = (gboolean)connected; - } - - g_free (ip_type_str); - ip_type_field += 4; - } - } - } - /* No separate IPv4/IPv6 info given just connected/not connected */ - else { - g_autoptr(GRegex) r = NULL; - g_autoptr(GMatchInfo) match_info = NULL; - - r = g_regex_new ("\\^NDISSTAT(?:QRY)?(?:Qry)?:\\s*(\\d)(?:\\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)) { - guint connected; - - if (!mm_get_uint_from_match_info (match_info, 1, &connected) || - (connected != 0 && connected != 1)) { - inner_error = g_error_new (MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't parse ^NDISSTAT / ^NDISSTATQRY fields"); - } else { - /* We'll assume IPv4 */ - *ipv4_available = TRUE; - *ipv4_connected = (gboolean)connected; - } - } - } - - if (!ipv4_available && !ipv6_available) { - inner_error = g_error_new (MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't find IPv4 or IPv6 info in ^NDISSTAT / ^NDISSTATQRY response"); - } - - if (inner_error) { - g_propagate_error (error, inner_error); - return FALSE; - } - - return TRUE; -} - -/*****************************************************************************/ -/* ^DHCP response parser */ - -static gboolean -match_info_to_ip4_addr (GMatchInfo *match_info, - guint match_index, - guint *out_addr) -{ - g_autofree gchar *s = NULL; - g_autofree guint8 *bin = NULL; - gchar buf[9]; - gsize len; - gsize bin_len; - guint32 aux; - - s = g_match_info_fetch (match_info, match_index); - g_return_val_if_fail (s != NULL, FALSE); - - len = strlen (s); - if (len == 1 && s[0] == '0') { - *out_addr = 0; - return TRUE; - } - - if (len < 7 || len > 8) - return FALSE; - - /* Handle possibly missing leading zero */ - memset (buf, 0, sizeof (buf)); - if (len == 7) { - strcpy (&buf[1], s); - buf[0] = '0'; - } else if (len == 8) - strcpy (buf, s); - else - g_assert_not_reached (); - - bin = mm_utils_hexstr2bin (buf, -1, &bin_len, NULL); - if (!bin || bin_len != 4) - return FALSE; - - memcpy (&aux, bin, 4); - *out_addr = GUINT32_SWAP_LE_BE (aux); - return TRUE; -} - -gboolean -mm_huawei_parse_dhcp_response (const char *reply, - guint *out_address, - guint *out_prefix, - guint *out_gateway, - guint *out_dns1, - guint *out_dns2, - GError **error) -{ - g_autoptr(GRegex) r = NULL; - g_autoptr(GMatchInfo) match_info = NULL; - gboolean matched; - GError *match_error = NULL; - - g_assert (reply != NULL); - g_assert (out_address != NULL); - g_assert (out_prefix != NULL); - g_assert (out_gateway != NULL); - g_assert (out_dns1 != NULL); - g_assert (out_dns2 != NULL); - - /* Format: - * - * ^DHCP: <address>,<netmask>,<gateway>,<?>,<dns1>,<dns2>,<uplink>,<downlink> - * - * All numbers are hexadecimal representations of IPv4 addresses, with - * least-significant byte first. eg, 192.168.50.32 is expressed as - * "2032A8C0". Sometimes leading zeros are stripped, so "1010A0A" is - * actually 10.10.1.1. - */ - - r = g_regex_new ("\\^DHCP:\\s*(?:0[xX])?([0-9a-fA-F]+),(?:0[xX])?([0-9a-fA-F]+),(?:0[xX])?([0-9a-fA-F]+),(?:0[xX])?([0-9a-fA-F]+),(?:0[xX])?([0-9a-fA-F]+),(?:0[xX])?([0-9a-fA-F]+),.*$", 0, 0, NULL); - g_assert (r != NULL); - - matched = g_regex_match_full (r, reply, -1, 0, 0, &match_info, &match_error); - if (!matched) { - if (match_error) { - g_propagate_error (error, match_error); - g_prefix_error (error, "Could not parse ^DHCP results: "); - } else { - g_set_error_literal (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't match ^DHCP reply"); - } - } else { - guint netmask; - - if (match_info_to_ip4_addr (match_info, 1, out_address) && - match_info_to_ip4_addr (match_info, 2, &netmask) && - match_info_to_ip4_addr (match_info, 3, out_gateway) && - match_info_to_ip4_addr (match_info, 5, out_dns1) && - match_info_to_ip4_addr (match_info, 6, out_dns2)) { - *out_prefix = mm_count_bits_set (netmask); - matched = TRUE; - } - } - - return matched; -} - -/*****************************************************************************/ -/* ^SYSINFO response parser */ - -gboolean -mm_huawei_parse_sysinfo_response (const char *reply, - guint *out_srv_status, - guint *out_srv_domain, - guint *out_roam_status, - guint *out_sys_mode, - guint *out_sim_state, - gboolean *out_sys_submode_valid, - guint *out_sys_submode, - GError **error) -{ - g_autoptr(GRegex) r = NULL; - g_autoptr(GMatchInfo) match_info = NULL; - gboolean matched; - GError *match_error = NULL; - - g_assert (out_srv_status != NULL); - g_assert (out_srv_domain != NULL); - g_assert (out_roam_status != NULL); - g_assert (out_sys_mode != NULL); - g_assert (out_sim_state != NULL); - g_assert (out_sys_submode_valid != NULL); - g_assert (out_sys_submode != NULL); - - /* Format: - * - * ^SYSINFO: <srv_status>,<srv_domain>,<roam_status>,<sys_mode>,<sim_state>[,<reserved>,<sys_submode>] - */ - - /* Can't just use \d here since sometimes you get "^SYSINFO:2,1,0,3,1,,3" */ - r = g_regex_new ("\\^SYSINFO:\\s*(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),?(\\d+)?,?(\\d+)?$", 0, 0, NULL); - g_assert (r != NULL); - - matched = g_regex_match_full (r, reply, -1, 0, 0, &match_info, &match_error); - if (!matched) { - if (match_error) { - g_propagate_error (error, match_error); - g_prefix_error (error, "Could not parse ^SYSINFO results: "); - } else { - g_set_error_literal (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't match ^SYSINFO reply"); - } - } else { - mm_get_uint_from_match_info (match_info, 1, out_srv_status); - mm_get_uint_from_match_info (match_info, 2, out_srv_domain); - mm_get_uint_from_match_info (match_info, 3, out_roam_status); - mm_get_uint_from_match_info (match_info, 4, out_sys_mode); - mm_get_uint_from_match_info (match_info, 5, out_sim_state); - - /* Remember that g_match_info_get_match_count() includes match #0 */ - if (g_match_info_get_match_count (match_info) >= 8) { - *out_sys_submode_valid = TRUE; - mm_get_uint_from_match_info (match_info, 7, out_sys_submode); - } - } - - return matched; -} - -/*****************************************************************************/ -/* ^SYSINFOEX response parser */ - -gboolean -mm_huawei_parse_sysinfoex_response (const char *reply, - guint *out_srv_status, - guint *out_srv_domain, - guint *out_roam_status, - guint *out_sim_state, - guint *out_sys_mode, - guint *out_sys_submode, - GError **error) -{ - g_autoptr(GRegex) r = NULL; - g_autoptr(GMatchInfo) match_info = NULL; - gboolean matched; - GError *match_error = NULL; - - g_assert (out_srv_status != NULL); - g_assert (out_srv_domain != NULL); - g_assert (out_roam_status != NULL); - g_assert (out_sim_state != NULL); - g_assert (out_sys_mode != NULL); - g_assert (out_sys_submode != NULL); - - /* Format: - * - * ^SYSINFOEX: <srv_status>,<srv_domain>,<roam_status>,<sim_state>,<reserved>,<sysmode>,<sysmode_name>,<submode>,<submode_name> - * - * <sysmode_name> and <submode_name> may not be quoted on some Huawei modems (e.g. E303). - */ - - /* ^SYSINFOEX:2,3,0,1,,3,"WCDMA",41,"HSPA+" */ - - r = g_regex_new ("\\^SYSINFOEX:\\s*(\\d+),(\\d+),(\\d+),(\\d+),?(\\d*),(\\d+),\"?([^\"]*)\"?,(\\d+),\"?([^\"]*)\"?$", 0, 0, NULL); - g_assert (r != NULL); - - matched = g_regex_match_full (r, reply, -1, 0, 0, &match_info, &match_error); - if (!matched) { - if (match_error) { - g_propagate_error (error, match_error); - g_prefix_error (error, "Could not parse ^SYSINFOEX results: "); - } else { - g_set_error_literal (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't match ^SYSINFOEX reply"); - } - } else { - mm_get_uint_from_match_info (match_info, 1, out_srv_status); - mm_get_uint_from_match_info (match_info, 2, out_srv_domain); - mm_get_uint_from_match_info (match_info, 3, out_roam_status); - mm_get_uint_from_match_info (match_info, 4, out_sim_state); - - /* We just ignore the sysmode and submode name strings */ - mm_get_uint_from_match_info (match_info, 6, out_sys_mode); - mm_get_uint_from_match_info (match_info, 8, out_sys_submode); - } - - return matched; -} - -/*****************************************************************************/ -/* ^PREFMODE test parser - * - * AT^PREFMODE=? - * ^PREFMODE:(2,4,8) - */ - -static gboolean -mode_from_prefmode (guint huawei_mode, - MMModemMode *modem_mode, - GError **error) -{ - g_assert (modem_mode != NULL); - - *modem_mode = MM_MODEM_MODE_NONE; - switch (huawei_mode) { - case 2: - *modem_mode = MM_MODEM_MODE_2G; - break; - case 4: - *modem_mode = MM_MODEM_MODE_3G; - break; - case 8: - *modem_mode = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); - break; - default: - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "No translation from huawei prefmode '%u' to mode", - huawei_mode); - } - - return *modem_mode != MM_MODEM_MODE_NONE ? TRUE : FALSE; -} - -GArray * -mm_huawei_parse_prefmode_test (const gchar *response, - gpointer log_object, - GError **error) -{ - gchar **split; - guint i; - MMModemMode all = MM_MODEM_MODE_NONE; - GArray *out; - - response = mm_strip_tag (response, "^PREFMODE:"); - split = g_strsplit_set (response, " (,)\r\n", -1); - if (!split) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Unexpected ^PREFMODE format output"); - return NULL; - } - - out = g_array_sized_new (FALSE, - FALSE, - sizeof (MMHuaweiPrefmodeCombination), - 3); - for (i = 0; split[i]; i++) { - guint val; - MMModemMode preferred = MM_MODEM_MODE_NONE; - GError *inner_error = NULL; - MMHuaweiPrefmodeCombination combination; - - if (split[i][0] == '\0') - continue; - - if (!mm_get_uint_from_str (split[i], &val)) { - mm_obj_dbg (log_object, "error parsing ^PREFMODE value '%s'", split[i]); - continue; - } - - if (!mode_from_prefmode (val, &preferred, &inner_error)) { - mm_obj_dbg (log_object, "unhandled ^PREFMODE value: %s", inner_error->message); - g_error_free (inner_error); - continue; - } - - combination.prefmode = val; - combination.allowed = MM_MODEM_MODE_NONE; /* reset it later */ - combination.preferred = preferred; - - all |= preferred; - - g_array_append_val (out, combination); - } - g_strfreev (split); - - /* No value */ - if (out->len == 0) { - g_array_unref (out); - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "^PREFMODE response contains no valid values"); - return NULL; - } - - /* Single value listed; PREFERRED=NONE... */ - if (out->len == 1) { - MMHuaweiPrefmodeCombination *combination; - - combination = &g_array_index (out, MMHuaweiPrefmodeCombination, 0); - combination->allowed = all; - combination->preferred = MM_MODEM_MODE_NONE; - } else { - /* Multiple values, reset ALLOWED */ - for (i = 0; i < out->len; i++) { - MMHuaweiPrefmodeCombination *combination; - - combination = &g_array_index (out, MMHuaweiPrefmodeCombination, i); - combination->allowed = all; - if (combination->preferred == all) - combination->preferred = MM_MODEM_MODE_NONE; - } - } - - return out; -} - -/*****************************************************************************/ -/* ^PREFMODE response parser */ - -const MMHuaweiPrefmodeCombination * -mm_huawei_parse_prefmode_response (const gchar *response, - const GArray *supported_mode_combinations, - GError **error) -{ - guint mode; - guint i; - - /* Format: - * - * ^PREFMODE: <mode> - */ - - response = mm_strip_tag (response, "^PREFMODE:"); - if (!mm_get_uint_from_str (response, &mode)) { - /* Dump error to upper layer */ - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Unexpected PREFMODE response: '%s'", - response); - return NULL; - } - - /* Look for current modes among the supported ones */ - for (i = 0; i < supported_mode_combinations->len; i++) { - const MMHuaweiPrefmodeCombination *combination; - - combination = &g_array_index (supported_mode_combinations, - MMHuaweiPrefmodeCombination, - i); - if (mode == combination->prefmode) - return combination; - } - - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "No PREFMODE combination found matching the current one (%d)", - mode); - return NULL; -} - -/*****************************************************************************/ -/* ^SYSCFG test parser */ - -static gchar ** -split_groups (const gchar *str, - GError **error) -{ - const gchar *p = str; - GPtrArray *out; - guint groups = 0; - - /* - * Split string: (a),((b1),(b2)),,(d),((e1),(e2)) - * Into: - * - a - * - (b1),(b2) - * - - * - d - * - (e1),(e2) - */ - - out = g_ptr_array_new_with_free_func (g_free); - - while (TRUE) { - const gchar *start; - guint inner_groups; - - /* Skip whitespaces */ - while (*p == ' ' || *p == '\r' || *p == '\n') - p++; - - /* We're done, return */ - if (*p == '\0') { - g_ptr_array_set_size (out, out->len + 1); - return (gchar **) g_ptr_array_free (out, FALSE); - } - - /* Group separators */ - if (groups > 0) { - if (*p != ',') { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Unexpected group separator"); - g_ptr_array_unref (out); - return NULL; - } - p++; - } - - /* Skip whitespaces */ - while (*p == ' ' || *p == '\r' || *p == '\n') - p++; - - /* New group */ - groups++; - - /* Empty group? */ - if (*p == ',' || *p == '\0') { - g_ptr_array_add (out, g_strdup ("")); - continue; - } - - /* No group start? */ - if (*p != '(') { - /* Error */ - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Expected '(' not found"); - g_ptr_array_unref (out); - return NULL; - } - p++; - - inner_groups = 0; - start = p; - while (TRUE) { - if (*p == '(') { - inner_groups++; - p++; - continue; - } - - if (*p == '\0') { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Early end of string found, unfinished group"); - g_ptr_array_unref (out); - return NULL; - } - - if (*p == ')') { - gchar *group; - - if (inner_groups > 0) { - inner_groups--; - p++; - continue; - } - - group = g_strndup (start, p - start); - g_ptr_array_add (out, group); - p++; - break; - } - - /* keep on */ - p++; - } - } - - g_assert_not_reached (); -} - -static gboolean -mode_from_syscfg (guint huawei_mode, - MMModemMode *modem_mode, - GError **error) -{ - g_assert (modem_mode != NULL); - - *modem_mode = MM_MODEM_MODE_NONE; - switch (huawei_mode) { - case 2: - *modem_mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G; - break; - case 13: - *modem_mode = MM_MODEM_MODE_2G; - break; - case 14: - *modem_mode = MM_MODEM_MODE_3G; - break; - case 16: - /* ignore */ - break; - default: - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "No translation from huawei prefmode '%u' to mode", - huawei_mode); - } - - return *modem_mode != MM_MODEM_MODE_NONE ? TRUE : FALSE; -} - -static GArray * -parse_syscfg_modes (const gchar *modes_str, - const gchar *acqorder_str, - gpointer log_object, - GError **error) -{ - GArray *out; - gchar **split; - guint i; - gint min_acqorder = 0; - gint max_acqorder = 0; - - /* Start parsing acquisition order */ - if (!sscanf (acqorder_str, "%d-%d", &min_acqorder, &max_acqorder)) - mm_obj_dbg (log_object, "error parsing ^SYSCFG acquisition order range '%s'", acqorder_str); - - /* Just in case, we default to supporting only auto */ - if (max_acqorder < min_acqorder) { - min_acqorder = 0; - max_acqorder = 0; - } - - /* Now parse modes */ - split = g_strsplit (modes_str, ",", -1); - out = g_array_sized_new (FALSE, - FALSE, - sizeof (MMHuaweiSyscfgCombination), - g_strv_length (split)); - for (i = 0; split[i]; i++) { - guint val; - guint allowed = MM_MODEM_MODE_NONE; - GError *inner_error = NULL; - MMHuaweiSyscfgCombination combination; - - if (!mm_get_uint_from_str (mm_strip_quotes (split[i]), &val)) { - mm_obj_dbg (log_object, "error parsing ^SYSCFG mode value: %s", split[i]); - continue; - } - - if (!mode_from_syscfg (val, &allowed, &inner_error)) { - if (inner_error) { - mm_obj_dbg (log_object, "unhandled ^SYSCFG: %s", inner_error->message); - g_error_free (inner_error); - } - continue; - } - - switch (allowed) { - case MM_MODEM_MODE_2G: - case MM_MODEM_MODE_3G: - /* single mode */ - combination.allowed = allowed; - combination.preferred = MM_MODEM_MODE_NONE; - combination.mode = val; - combination.acqorder = 0; - g_array_append_val (out, combination); - break; - case (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G): - /* 2G and 3G; auto */ - combination.allowed = allowed; - combination.mode = val; - if (min_acqorder == 0) { - combination.preferred = MM_MODEM_MODE_NONE; - combination.acqorder = 0; - g_array_append_val (out, combination); - } - /* 2G and 3G; 2G preferred */ - if (min_acqorder <= 1 && max_acqorder >= 1) { - combination.preferred = MM_MODEM_MODE_2G; - combination.acqorder = 1; - g_array_append_val (out, combination); - } - /* 2G and 3G; 3G preferred */ - if (min_acqorder <= 2 && max_acqorder >= 2) { - combination.preferred = MM_MODEM_MODE_3G; - combination.acqorder = 2; - g_array_append_val (out, combination); - } - break; - default: - g_assert_not_reached (); - } - } - - g_strfreev (split); - - /* If we didn't build a valid array of combinations, return an error */ - if (out->len == 0) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Cannot parse list of allowed mode combinations: '%s,%s'", - modes_str, - acqorder_str); - g_array_unref (out); - return NULL; - } - - return out; -} - -GArray * -mm_huawei_parse_syscfg_test (const gchar *response, - gpointer log_object, - GError **error) -{ - gchar **split; - GError *inner_error = NULL; - GArray *out; - - if (!response || !g_str_has_prefix (response, "^SYSCFG:")) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Missing ^SYSCFG prefix"); - return NULL; - } - - /* Examples: - * - * ^SYSCFG:(2,13,14,16), - * (0-3), - * ((400000,"WCDMA2100")), - * (0-2), - * (0-4) - */ - split = split_groups (mm_strip_tag (response, "^SYSCFG:"), error); - if (!split) - return NULL; - - /* We expect 5 string chunks */ - if (g_strv_length (split) < 5) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Unexpected ^SYSCFG format"); - g_strfreev (split); - return FALSE; - } - - /* Parse supported mode combinations */ - out = parse_syscfg_modes (split[0], split[1], log_object, &inner_error); - - g_strfreev (split); - - if (inner_error) { - g_propagate_error (error, inner_error); - return NULL; - } - - return out; -} - -/*****************************************************************************/ -/* ^SYSCFG response parser */ - -const MMHuaweiSyscfgCombination * -mm_huawei_parse_syscfg_response (const gchar *response, - const GArray *supported_mode_combinations, - GError **error) -{ - gchar **split; - guint mode; - guint acqorder; - guint i; - - if (!response || !g_str_has_prefix (response, "^SYSCFG:")) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Missing ^SYSCFG prefix"); - return NULL; - } - - /* Format: - * - * ^SYSCFG: <mode>,<acqorder>,<band>,<roam>,<srvdomain> - */ - - response = mm_strip_tag (response, "^SYSCFG:"); - split = g_strsplit (response, ",", -1); - - /* We expect 5 string chunks */ - if (g_strv_length (split) < 5 || - !mm_get_uint_from_str (split[0], &mode) || - !mm_get_uint_from_str (split[1], &acqorder)) { - /* Dump error to upper layer */ - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Unexpected ^SYSCFG response: '%s'", - response); - g_strfreev (split); - return NULL; - } - - /* Fix invalid modes with non-sensical acquisition orders */ - if (mode == 14 && acqorder != 0) /* WCDMA only but acqorder != "Automatic" */ - acqorder = 0; - else if (mode == 13 && acqorder != 0) /* GSM only but acqorder != "Automatic" */ - acqorder = 0; - - /* Look for current modes among the supported ones */ - for (i = 0; i < supported_mode_combinations->len; i++) { - const MMHuaweiSyscfgCombination *combination; - - combination = &g_array_index (supported_mode_combinations, - MMHuaweiSyscfgCombination, - i); - if (mode == combination->mode && acqorder == combination->acqorder) { - g_strfreev (split); - return combination; - } - } - - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "No SYSCFG combination found matching the current one (%d,%d)", - mode, - acqorder); - g_strfreev (split); - return NULL; -} - -/*****************************************************************************/ -/* ^SYSCFGEX test parser */ - -static void -huawei_syscfgex_combination_free (MMHuaweiSyscfgexCombination *item) -{ - /* Just the contents, not the item itself! */ - g_free (item->mode_str); -} - -static gboolean -parse_mode_combination_string (const gchar *mode_str, - MMModemMode *allowed, - MMModemMode *preferred) -{ - guint n; - - if (g_str_equal (mode_str, "00")) { - *allowed = MM_MODEM_MODE_ANY; - *preferred = MM_MODEM_MODE_NONE; - return TRUE; - } - - *allowed = MM_MODEM_MODE_NONE; - *preferred = MM_MODEM_MODE_NONE; - - for (n = 0; n < strlen (mode_str); n+=2) { - MMModemMode mode; - - if (g_ascii_strncasecmp (&mode_str[n], "01", 2) == 0) - /* GSM */ - mode = MM_MODEM_MODE_2G; - else if (g_ascii_strncasecmp (&mode_str[n], "02", 2) == 0) - /* WCDMA */ - mode = MM_MODEM_MODE_3G; - else if (g_ascii_strncasecmp (&mode_str[n], "03", 2) == 0) - /* LTE */ - mode = MM_MODEM_MODE_4G; - else if (g_ascii_strncasecmp (&mode_str[n], "04", 2) == 0) - /* CDMA Note: no EV-DO, just return single value, so assume CDMA1x*/ - mode = MM_MODEM_MODE_2G; - else - mode = MM_MODEM_MODE_NONE; - - if (mode != MM_MODEM_MODE_NONE) { - /* The first one in the list is the preferred combination */ - if (n == 0) - *preferred |= mode; - *allowed |= mode; - } - } - - switch (mm_count_bits_set (*allowed)) { - case 0: - /* No allowed, error */ - return FALSE; - case 1: - /* If only one mode allowed, NONE preferred */ - *preferred = MM_MODEM_MODE_NONE; - /* fall through */ - default: - return TRUE; - } -} - -static GArray * -parse_mode_combination_string_list (const gchar *modes_str, - GError **error) -{ - GArray *supported_mode_combinations; - gchar **mode_combinations; - MMModemMode all = MM_MODEM_MODE_NONE; - gboolean has_all = FALSE; - guint i; - - mode_combinations = g_strsplit (modes_str, ",", -1); - supported_mode_combinations = g_array_sized_new (FALSE, - FALSE, - sizeof (MMHuaweiSyscfgexCombination), - g_strv_length (mode_combinations)); - g_array_set_clear_func (supported_mode_combinations, - (GDestroyNotify)huawei_syscfgex_combination_free); - - for (i = 0; mode_combinations[i]; i++) { - MMHuaweiSyscfgexCombination combination; - - mode_combinations[i] = mm_strip_quotes (mode_combinations[i]); - if (!parse_mode_combination_string (mode_combinations[i], - &combination.allowed, - &combination.preferred)) - continue; - - if (combination.allowed != MM_MODEM_MODE_ANY) { - combination.mode_str = g_strdup (mode_combinations[i]); - g_array_append_val (supported_mode_combinations, combination); - - all |= combination.allowed; - } else { - /* don't add the all_combination here, we may have more - * combinations in the loop afterwards */ - has_all = TRUE; - } - } - - g_strfreev (mode_combinations); - - /* Add here the all_combination */ - if (has_all) { - MMHuaweiSyscfgexCombination combination; - - combination.allowed = all; - combination.preferred = MM_MODEM_MODE_NONE; - combination.mode_str = g_strdup ("00"); - g_array_append_val (supported_mode_combinations, combination); - } - - /* If we didn't build a valid array of combinations, return an error */ - if (supported_mode_combinations->len == 0) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Cannot parse list of allowed mode combinations: '%s'", - modes_str); - g_array_unref (supported_mode_combinations); - return NULL; - } - - return supported_mode_combinations; -} - -GArray * -mm_huawei_parse_syscfgex_test (const gchar *response, - GError **error) -{ - gchar **split; - GError *inner_error = NULL; - GArray *out; - - if (!response || !g_str_has_prefix (response, "^SYSCFGEX:")) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Missing ^SYSCFGEX prefix"); - return NULL; - } - - /* Examples: - * - * ^SYSCFGEX: ("00","03","02","01","99"), - * ((2000004e80380,"GSM850/GSM900/GSM1800/GSM1900/WCDMA850/WCDMA900/WCDMA1900/WCDMA2100"), - * (3fffffff,"All Bands")), - * (0-3), - * (0-4), - * ((800c5,"LTE2100/LTE1800/LTE2600/LTE900/LTE800"), - * (7fffffffffffffff,"All bands")) - */ - split = split_groups (mm_strip_tag (response, "^SYSCFGEX:"), error); - if (!split) - return NULL; - - /* We expect 5 string chunks */ - if (g_strv_length (split) < 5) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Unexpected ^SYSCFGEX format"); - g_strfreev (split); - return NULL; - } - - out = parse_mode_combination_string_list (split[0], &inner_error); - - g_strfreev (split); - - if (inner_error) { - g_propagate_error (error, inner_error); - return NULL; - } - - return out; -} - -/*****************************************************************************/ -/* ^SYSCFGEX response parser */ - -const MMHuaweiSyscfgexCombination * -mm_huawei_parse_syscfgex_response (const gchar *response, - const GArray *supported_mode_combinations, - GError **error) -{ - gchar **split; - guint i; - gsize len; - gchar *str; - - if (!response || !g_str_has_prefix (response, "^SYSCFGEX:")) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Missing ^SYSCFGEX prefix"); - return NULL; - } - - /* Format: - * - * ^SYSCFGEX: "00",3FFFFFFF,1,2,7FFFFFFFFFFFFFFF - * ^SYSCFGEX: <mode>,<band>,<roam>,<srvdomain>,<lte-band> - */ - - response = mm_strip_tag (response, "^SYSCFGEX:"); - split = g_strsplit (response, ",", -1); - - /* We expect 5 string chunks */ - if (g_strv_length (split) < 5) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Unexpected ^SYSCFGEX response format"); - g_strfreev (split); - return NULL; - } - - /* Unquote */ - str = split[0]; - len = strlen (str); - if ((len >= 2) && (str[0] == '"') && (str[len - 1] == '"')) { - str[0] = ' '; - str[len - 1] = ' '; - str = g_strstrip (str); - } - - /* Look for current modes among the supported ones */ - for (i = 0; i < supported_mode_combinations->len; i++) { - const MMHuaweiSyscfgexCombination *combination; - - combination = &g_array_index (supported_mode_combinations, - MMHuaweiSyscfgexCombination, - i); - if (g_str_equal (str, combination->mode_str)) { - g_strfreev (split); - return combination; - } - } - - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "No SYSCFGEX combination found matching the current one (%s)", - str); - g_strfreev (split); - return NULL; -} - -/*****************************************************************************/ -/* ^NWTIME response parser */ - -gboolean -mm_huawei_parse_nwtime_response (const gchar *response, - gchar **iso8601p, - MMNetworkTimezone **tzp, - GError **error) -{ - g_autoptr(GRegex) r = NULL; - g_autoptr(GMatchInfo) match_info = NULL; - GError *match_error = NULL; - guint year = 0; - guint month = 0; - guint day = 0; - guint hour = 0; - guint minute = 0; - guint second = 0; - guint dt = 0; - gint tz = 0; - - g_assert (iso8601p || tzp); /* at least one */ - - r = g_regex_new ("\\^NWTIME:\\s*(\\d+)/(\\d+)/(\\d+),(\\d+):(\\d+):(\\d*)([\\-\\+\\d]+),(\\d+)$", 0, 0, NULL); - g_assert (r != NULL); - - if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) { - if (match_error) { - g_propagate_error (error, match_error); - g_prefix_error (error, "Could not parse ^NWTIME results: "); - } else { - g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "Couldn't match ^NWTIME reply"); - } - return FALSE; - } - - /* Remember that g_match_info_get_match_count() includes match #0 */ - g_assert (g_match_info_get_match_count (match_info) >= 9); - - if (mm_get_uint_from_match_info (match_info, 1, &year) && - mm_get_uint_from_match_info (match_info, 2, &month) && - mm_get_uint_from_match_info (match_info, 3, &day) && - mm_get_uint_from_match_info (match_info, 4, &hour) && - mm_get_uint_from_match_info (match_info, 5, &minute) && - mm_get_uint_from_match_info (match_info, 6, &second) && - mm_get_int_from_match_info (match_info, 7, &tz) && - mm_get_uint_from_match_info (match_info, 8, &dt)) { - - /* adjust year */ - if (year < 100) - year += 2000; - /* - * tz = timezone offset in 15 minute intervals - * dt = daylight adjustment, 0 = none, 1 = 1 hour, 2 = 2 hours - * other values are marked reserved. - */ - if (tzp) { - *tzp = mm_network_timezone_new (); - mm_network_timezone_set_offset (*tzp, tz * 15); - mm_network_timezone_set_dst_offset (*tzp, dt * 60); - } - if (iso8601p) { - /* Return ISO-8601 format date/time string */ - *iso8601p = mm_new_iso8601_time (year, month, day, hour, - minute, second, - TRUE, (tz * 15) + (dt * 60), - error); - return (*iso8601p != NULL); - } - - return TRUE; - } - - g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "Failed to parse ^NWTIME reply"); - return FALSE; -} - -/*****************************************************************************/ -/* ^TIME response parser */ - -gboolean -mm_huawei_parse_time_response (const gchar *response, - gchar **iso8601p, - MMNetworkTimezone **tzp, - GError **error) -{ - g_autoptr(GRegex) r = NULL; - g_autoptr(GMatchInfo) match_info = NULL; - GError *match_error = NULL; - guint year = 0; - guint month = 0; - guint day = 0; - guint hour = 0; - guint minute = 0; - guint second = 0; - - g_assert (iso8601p || tzp); /* at least one */ - - /* TIME response cannot ever provide TZ info */ - if (tzp) { - g_set_error_literal (error, - MM_CORE_ERROR, - MM_CORE_ERROR_UNSUPPORTED, - "^TIME does not provide timezone information"); - return FALSE; - } - - /* Already in ISO-8601 format, but verify just to be sure */ - r = g_regex_new ("\\^TIME:\\s*(\\d+)/(\\d+)/(\\d+)\\s*(\\d+):(\\d+):(\\d*)$", 0, 0, NULL); - g_assert (r != NULL); - - if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) { - if (match_error) { - g_propagate_error (error, match_error); - g_prefix_error (error, "Could not parse ^TIME results: "); - } else { - g_set_error_literal (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't match ^TIME reply"); - } - return FALSE; - } - - /* Remember that g_match_info_get_match_count() includes match #0 */ - g_assert (g_match_info_get_match_count (match_info) >= 7); - - if (mm_get_uint_from_match_info (match_info, 1, &year) && - mm_get_uint_from_match_info (match_info, 2, &month) && - mm_get_uint_from_match_info (match_info, 3, &day) && - mm_get_uint_from_match_info (match_info, 4, &hour) && - mm_get_uint_from_match_info (match_info, 5, &minute) && - mm_get_uint_from_match_info (match_info, 6, &second)) { - /* adjust year */ - if (year < 100) - year += 2000; - - /* Return ISO-8601 format date/time string */ - if (iso8601p) { - *iso8601p = mm_new_iso8601_time (year, month, day, hour, - minute, second, FALSE, 0, - error); - return (*iso8601p != NULL); - } - return TRUE; - } - - g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "Failed to parse ^TIME reply"); - return FALSE; -} - -/*****************************************************************************/ -/* ^HCSQ response parser */ - -gboolean -mm_huawei_parse_hcsq_response (const gchar *response, - MMModemAccessTechnology *out_act, - guint *out_value1, - guint *out_value2, - guint *out_value3, - guint *out_value4, - guint *out_value5, - GError **error) -{ - g_autoptr(GRegex) r = NULL; - g_autoptr(GMatchInfo) match_info = NULL; - GError *match_error = NULL; - - r = g_regex_new ("\\^HCSQ:\\s*\"?([a-zA-Z]*)\"?,(\\d+),?(\\d+)?,?(\\d+)?,?(\\d+)?,?(\\d+)?$", 0, 0, NULL); - g_assert (r != NULL); - - if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) { - if (match_error) { - g_propagate_error (error, match_error); - g_prefix_error (error, "Could not parse ^HCSQ results: "); - } else { - g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "Couldn't match ^HCSQ reply"); - } - return FALSE; - } - - /* Remember that g_match_info_get_match_count() includes match #0 */ - if (g_match_info_get_match_count (match_info) < 3) { - g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "Not enough elements in ^HCSQ reply"); - return FALSE; - } - - if (out_act) { - g_autofree gchar *s = NULL; - - s = g_match_info_fetch (match_info, 1); - *out_act = mm_string_to_access_tech (s); - } - - if (out_value1) - mm_get_uint_from_match_info (match_info, 2, out_value1); - if (out_value2) - mm_get_uint_from_match_info (match_info, 3, out_value2); - if (out_value3) - mm_get_uint_from_match_info (match_info, 4, out_value3); - if (out_value4) - mm_get_uint_from_match_info (match_info, 5, out_value4); - if (out_value5) - mm_get_uint_from_match_info (match_info, 6, out_value5); - - return TRUE; -} - -/*****************************************************************************/ -/* ^CVOICE response parser */ - -gboolean -mm_huawei_parse_cvoice_response (const gchar *response, - guint *out_hz, - guint *out_bits, - GError **error) -{ - g_autoptr(GRegex) r = NULL; - g_autoptr(GMatchInfo) match_info = NULL; - GError *match_error = NULL; - guint supported = 0; - guint hz = 0; - guint bits = 0; - - /* ^CVOICE: <0=supported,1=unsupported>,<hz>,<bits>,<unknown> */ - r = g_regex_new ("\\^CVOICE:\\s*(\\d)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)$", 0, 0, NULL); - g_assert (r != NULL); - - if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) { - if (match_error) { - g_propagate_error (error, match_error); - g_prefix_error (error, "Could not parse ^CVOICE results: "); - } else { - g_set_error_literal (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't match ^CVOICE reply"); - } - return FALSE; - } - - /* Remember that g_match_info_get_match_count() includes match #0 */ - g_assert (g_match_info_get_match_count (match_info) >= 5); - - if (mm_get_uint_from_match_info (match_info, 1, &supported) && - mm_get_uint_from_match_info (match_info, 2, &hz) && - mm_get_uint_from_match_info (match_info, 3, &bits)) { - if (supported == 0) { - if (out_hz) - *out_hz = hz; - if (out_bits) - *out_bits = bits; - return TRUE; - } - - g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, - "^CVOICE not supported by this device"); - return FALSE; - } - - g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "Failed to parse ^CVOICE reply"); - return FALSE; -} - -/*****************************************************************************/ -/* ^GETPORTMODE response parser */ - -#define GETPORTMODE_PREFIX "^GETPORTMODE:" - -GArray * -mm_huawei_parse_getportmode_response (const gchar *response, - gpointer log_object, - GError **error) -{ - g_autoptr(GArray) modes = NULL; - g_auto(GStrv) split = NULL; - guint i; - gint n_items; - - split = g_strsplit (response, ",", -1); - n_items = g_strv_length (split) - 1; - if (n_items < 1) { - g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "Unexpected number of items in response"); - return NULL; - } - - /* validate response prefix */ - if (g_ascii_strncasecmp (split[0], GETPORTMODE_PREFIX, strlen (GETPORTMODE_PREFIX)) != 0) { - g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "Unexpected response prefix"); - return NULL; - } - - mm_obj_dbg (log_object, "processing ^GETPORTMODE response..."); - - modes = g_array_sized_new (FALSE, FALSE, sizeof (MMHuaweiPortMode), n_items); - - /* iterate all port items found */ - for (i = 1; split[i]; i++) { - MMHuaweiPortMode mode = MM_HUAWEI_PORT_MODE_NONE; - gchar *separator; - guint port_number; - - separator = strchr (split[i], ':'); - if (!separator) - continue; - - /* the reported port number may start either by 0 or by 1; the important - * thing is therefore no the number itself, only that it's a number */ - g_strstrip (&separator[1]); - if (!mm_get_uint_from_str (&separator[1], &port_number)) { - mm_obj_warn (log_object, " couldn't parse port number: %s", split[i]); - break; - } - - *separator = '\0'; - g_strstrip (split[i]); - - if (g_ascii_strcasecmp (split[i], "pcui") == 0) - mode = MM_HUAWEI_PORT_MODE_PCUI; - else if ((g_ascii_strcasecmp (split[i], "mdm") == 0) || - (g_ascii_strcasecmp (split[i], "modem") == 0) || - (g_ascii_strcasecmp (split[i], "3g_modem") == 0)) - mode = MM_HUAWEI_PORT_MODE_MODEM; - else if ((g_ascii_strcasecmp (split[i], "diag") == 0) || - (g_ascii_strcasecmp (split[i], "3g_diag") == 0) || - (g_ascii_strcasecmp (split[i], "4g_diag") == 0)) - mode = MM_HUAWEI_PORT_MODE_DIAG; - else if (g_ascii_strcasecmp (split[i], "gps") == 0) - mode = MM_HUAWEI_PORT_MODE_GPS; - else if ((g_ascii_strcasecmp (split[i], "ndis") == 0) || - (g_ascii_strcasecmp (split[i], "rndis") == 0) || - (g_ascii_strcasecmp (split[i], "ncm") == 0) || - (g_ascii_strcasecmp (split[i], "ecm") == 0)) - mode = MM_HUAWEI_PORT_MODE_NET; - else if (g_ascii_strcasecmp (split[i], "cdrom") == 0) - mode = MM_HUAWEI_PORT_MODE_CDROM; - else if ((g_ascii_strcasecmp (split[i], "sd") == 0) || - (g_ascii_strncasecmp (split[i], "mass", 4) == 0)) - mode = MM_HUAWEI_PORT_MODE_SD; - else if (g_ascii_strcasecmp (split[i], "bt") == 0) - mode = MM_HUAWEI_PORT_MODE_BT; - else if ((g_ascii_strcasecmp (split[i], "a_shell") == 0) || - (g_ascii_strcasecmp (split[i], "c_shell") == 0)) - mode = MM_HUAWEI_PORT_MODE_SHELL; - - mm_obj_dbg (log_object, " port mode %s reported at port number %u", - mm_huawei_port_mode_get_string (mode), port_number); - g_array_append_val (modes, mode); - } - - if (!modes->len) { - g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "No port modes loaded"); - return NULL; - } - - return g_steal_pointer (&modes); -} |