
We will expose a new 'Ports' property listing all ports currently known by a given modem. Ports which are not used but are detected as being part of the modem will be listed with an 'unknown' port type. This change uses the new 'MMModemPortType' enum and the new 'MMModemPortInfo' helper struct to handle these values in libmm-glib. The already available 'MMPortType' enum hasn't been re-used for the interface because it contains values that we don't need (e.g. IGNORED). The port list is now also included in the modem information command of mmcli: $ sudo mmcli -m 0 /org/freedesktop/ModemManager1/Modem/0 (device id '97b7b99e3e2bea103880545b619fb05a3cc81b26') ------------------------- System | device: '/sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4' | drivers: 'qcserial, qmi_wwan' | plugin: 'Gobi' | primary port: 'cdc-wdm0' | ports: 'ttyUSB0 (qcdm), ttyUSB1 (at), cdc-wdm0 (qmi), wwp0s29u1u4 (net)' https://bugzilla.gnome.org/show_bug.cgi?id=702678
1376 lines
36 KiB
C
1376 lines
36 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) 2010 - 2012 Red Hat, Inc.
|
|
* Copyright (C) 2011 - 2012 Google, Inc.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <gio/gio.h>
|
|
|
|
#include <ModemManager.h>
|
|
|
|
#include "mm-enums-types.h"
|
|
#include "mm-errors-types.h"
|
|
#include "mm-common-helpers.h"
|
|
|
|
gchar *
|
|
mm_common_build_capabilities_string (const MMModemCapability *capabilities,
|
|
guint n_capabilities)
|
|
{
|
|
gboolean first = TRUE;
|
|
GString *str;
|
|
guint i;
|
|
|
|
if (!capabilities || !n_capabilities)
|
|
return g_strdup ("none");
|
|
|
|
str = g_string_new ("");
|
|
for (i = 0; i < n_capabilities; i++) {
|
|
gchar *tmp;
|
|
|
|
tmp = mm_modem_capability_build_string_from_mask (capabilities[i]);
|
|
g_string_append_printf (str, "%s%s",
|
|
first ? "" : "\n",
|
|
tmp);
|
|
g_free (tmp);
|
|
|
|
if (first)
|
|
first = FALSE;
|
|
}
|
|
|
|
return g_string_free (str, FALSE);
|
|
}
|
|
|
|
gchar *
|
|
mm_common_build_bands_string (const MMModemBand *bands,
|
|
guint n_bands)
|
|
{
|
|
gboolean first = TRUE;
|
|
GString *str;
|
|
guint i;
|
|
|
|
if (!bands || !n_bands)
|
|
return g_strdup ("none");
|
|
|
|
str = g_string_new ("");
|
|
for (i = 0; i < n_bands; i++) {
|
|
g_string_append_printf (str, "%s%s",
|
|
first ? "" : ", ",
|
|
mm_modem_band_get_string (bands[i]));
|
|
|
|
if (first)
|
|
first = FALSE;
|
|
}
|
|
|
|
return g_string_free (str, FALSE);
|
|
}
|
|
|
|
gchar *
|
|
mm_common_build_ports_string (const MMModemPortInfo *ports,
|
|
guint n_ports)
|
|
{
|
|
gboolean first = TRUE;
|
|
GString *str;
|
|
guint i;
|
|
|
|
if (!ports || !n_ports)
|
|
return g_strdup ("none");
|
|
|
|
str = g_string_new ("");
|
|
for (i = 0; i < n_ports; i++) {
|
|
g_string_append_printf (str, "%s%s (%s)",
|
|
first ? "" : ", ",
|
|
ports[i].name,
|
|
mm_modem_port_type_get_string (ports[i].type));
|
|
|
|
if (first)
|
|
first = FALSE;
|
|
}
|
|
|
|
return g_string_free (str, FALSE);
|
|
}
|
|
|
|
gchar *
|
|
mm_common_build_sms_storages_string (const MMSmsStorage *storages,
|
|
guint n_storages)
|
|
{
|
|
gboolean first = TRUE;
|
|
GString *str;
|
|
guint i;
|
|
|
|
if (!storages || !n_storages)
|
|
return g_strdup ("none");
|
|
|
|
str = g_string_new ("");
|
|
for (i = 0; i < n_storages; i++) {
|
|
g_string_append_printf (str, "%s%s",
|
|
first ? "" : ", ",
|
|
mm_sms_storage_get_string (storages[i]));
|
|
|
|
if (first)
|
|
first = FALSE;
|
|
}
|
|
|
|
return g_string_free (str, FALSE);
|
|
}
|
|
|
|
gchar *
|
|
mm_common_build_mode_combinations_string (const MMModemModeCombination *modes,
|
|
guint n_modes)
|
|
{
|
|
gboolean first = TRUE;
|
|
GString *str;
|
|
guint i;
|
|
|
|
if (!modes || !n_modes)
|
|
return g_strdup ("none");
|
|
|
|
str = g_string_new ("");
|
|
for (i = 0; i < n_modes; i++) {
|
|
gchar *allowed;
|
|
gchar *preferred;
|
|
|
|
allowed = mm_modem_mode_build_string_from_mask (modes[i].allowed);
|
|
preferred = mm_modem_mode_build_string_from_mask (modes[i].preferred);
|
|
g_string_append_printf (str, "%sallowed: %s; preferred: %s",
|
|
first ? "" : "\n",
|
|
allowed,
|
|
preferred);
|
|
g_free (allowed);
|
|
g_free (preferred);
|
|
|
|
if (first)
|
|
first = FALSE;
|
|
}
|
|
|
|
return g_string_free (str, FALSE);
|
|
}
|
|
|
|
GArray *
|
|
mm_common_sms_storages_variant_to_garray (GVariant *variant)
|
|
{
|
|
GArray *array = NULL;
|
|
|
|
if (variant) {
|
|
GVariantIter iter;
|
|
guint n;
|
|
|
|
g_variant_iter_init (&iter, variant);
|
|
n = g_variant_iter_n_children (&iter);
|
|
|
|
if (n > 0) {
|
|
guint32 storage;
|
|
|
|
array = g_array_sized_new (FALSE, FALSE, sizeof (MMSmsStorage), n);
|
|
while (g_variant_iter_loop (&iter, "u", &storage))
|
|
g_array_append_val (array, storage);
|
|
}
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
MMSmsStorage *
|
|
mm_common_sms_storages_variant_to_array (GVariant *variant,
|
|
guint *n_storages)
|
|
{
|
|
GArray *array;
|
|
|
|
array = mm_common_sms_storages_variant_to_garray (variant);
|
|
if (n_storages)
|
|
*n_storages = array->len;
|
|
return (MMSmsStorage *) g_array_free (array, FALSE);
|
|
}
|
|
|
|
GVariant *
|
|
mm_common_sms_storages_array_to_variant (const MMSmsStorage *storages,
|
|
guint n_storages)
|
|
{
|
|
GVariantBuilder builder;
|
|
guint i;
|
|
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
|
|
|
|
for (i = 0; i < n_storages; i++)
|
|
g_variant_builder_add_value (&builder,
|
|
g_variant_new_uint32 ((guint32)storages[i]));
|
|
return g_variant_builder_end (&builder);
|
|
}
|
|
|
|
GVariant *
|
|
mm_common_sms_storages_garray_to_variant (GArray *array)
|
|
{
|
|
if (array)
|
|
return mm_common_sms_storages_array_to_variant ((const MMSmsStorage *)array->data,
|
|
array->len);
|
|
|
|
return mm_common_sms_storages_array_to_variant (NULL, 0);
|
|
}
|
|
|
|
GArray *
|
|
mm_common_ports_variant_to_garray (GVariant *variant)
|
|
{
|
|
GArray *array = NULL;
|
|
|
|
if (variant) {
|
|
guint i;
|
|
guint n;
|
|
|
|
n = g_variant_n_children (variant);
|
|
|
|
if (n > 0) {
|
|
array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemPortInfo), n);
|
|
for (i = 0; i < n; i++) {
|
|
MMModemPortInfo info;
|
|
|
|
g_variant_get_child (variant, i, "(su)", &info.name, &info.type);
|
|
g_array_append_val (array, info);
|
|
}
|
|
}
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
MMModemPortInfo *
|
|
mm_common_ports_variant_to_array (GVariant *variant,
|
|
guint *n_ports)
|
|
{
|
|
GArray *array;
|
|
|
|
array = mm_common_ports_variant_to_garray (variant);
|
|
if (n_ports)
|
|
*n_ports = array->len;
|
|
return (MMModemPortInfo *) g_array_free (array, FALSE);
|
|
}
|
|
|
|
GVariant *
|
|
mm_common_ports_array_to_variant (const MMModemPortInfo *ports,
|
|
guint n_ports)
|
|
{
|
|
GVariantBuilder builder;
|
|
guint i;
|
|
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(su)"));
|
|
|
|
for (i = 0; i < n_ports; i++) {
|
|
GVariant *tuple[2];
|
|
|
|
tuple[0] = g_variant_new_string (ports[i].name);
|
|
tuple[1] = g_variant_new_uint32 ((guint32)ports[i].type);
|
|
g_variant_builder_add_value (&builder, g_variant_new_tuple (tuple, 2));
|
|
}
|
|
return g_variant_builder_end (&builder);
|
|
}
|
|
|
|
GVariant *
|
|
mm_common_ports_garray_to_variant (GArray *array)
|
|
{
|
|
if (array)
|
|
return mm_common_ports_array_to_variant ((const MMModemPortInfo *)array->data,
|
|
array->len);
|
|
|
|
return mm_common_ports_array_to_variant (NULL, 0);
|
|
}
|
|
|
|
MMModemCapability
|
|
mm_common_get_capabilities_from_string (const gchar *str,
|
|
GError **error)
|
|
{
|
|
GError *inner_error = NULL;
|
|
MMModemCapability capabilities;
|
|
gchar **capability_strings;
|
|
GFlagsClass *flags_class;
|
|
|
|
capabilities = MM_MODEM_CAPABILITY_NONE;
|
|
|
|
flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_MODEM_CAPABILITY));
|
|
capability_strings = g_strsplit (str, "|", -1);
|
|
|
|
if (capability_strings) {
|
|
guint i;
|
|
|
|
for (i = 0; capability_strings[i]; i++) {
|
|
guint j;
|
|
gboolean found = FALSE;
|
|
|
|
for (j = 0; flags_class->values[j].value_nick; j++) {
|
|
if (!g_ascii_strcasecmp (capability_strings[i], flags_class->values[j].value_nick)) {
|
|
capabilities |= flags_class->values[j].value;
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
inner_error = g_error_new (
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_INVALID_ARGS,
|
|
"Couldn't match '%s' with a valid MMModemCapability value",
|
|
capability_strings[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (inner_error) {
|
|
g_propagate_error (error, inner_error);
|
|
capabilities = MM_MODEM_CAPABILITY_NONE;
|
|
}
|
|
|
|
g_type_class_unref (flags_class);
|
|
g_strfreev (capability_strings);
|
|
return capabilities;
|
|
}
|
|
|
|
MMModemMode
|
|
mm_common_get_modes_from_string (const gchar *str,
|
|
GError **error)
|
|
{
|
|
GError *inner_error = NULL;
|
|
MMModemMode modes;
|
|
gchar **mode_strings;
|
|
GFlagsClass *flags_class;
|
|
|
|
modes = MM_MODEM_MODE_NONE;
|
|
|
|
flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_MODEM_MODE));
|
|
mode_strings = g_strsplit (str, "|", -1);
|
|
|
|
if (mode_strings) {
|
|
guint i;
|
|
|
|
for (i = 0; mode_strings[i]; i++) {
|
|
guint j;
|
|
gboolean found = FALSE;
|
|
|
|
for (j = 0; flags_class->values[j].value_nick; j++) {
|
|
if (!g_ascii_strcasecmp (mode_strings[i], flags_class->values[j].value_nick)) {
|
|
modes |= flags_class->values[j].value;
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
inner_error = g_error_new (
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_INVALID_ARGS,
|
|
"Couldn't match '%s' with a valid MMModemMode value",
|
|
mode_strings[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (inner_error) {
|
|
g_propagate_error (error, inner_error);
|
|
modes = MM_MODEM_MODE_NONE;
|
|
}
|
|
|
|
g_type_class_unref (flags_class);
|
|
g_strfreev (mode_strings);
|
|
return modes;
|
|
}
|
|
|
|
GArray *
|
|
mm_common_capability_combinations_variant_to_garray (GVariant *variant)
|
|
{
|
|
GArray *array = NULL;
|
|
|
|
if (variant) {
|
|
GVariantIter iter;
|
|
guint n;
|
|
|
|
g_variant_iter_init (&iter, variant);
|
|
n = g_variant_iter_n_children (&iter);
|
|
|
|
if (n > 0) {
|
|
guint32 capability;
|
|
|
|
array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), n);
|
|
while (g_variant_iter_loop (&iter, "u", &capability))
|
|
g_array_append_val (array, capability);
|
|
}
|
|
}
|
|
|
|
/* If nothing set, fallback to default */
|
|
if (!array) {
|
|
guint32 capability = MM_MODEM_CAPABILITY_NONE;
|
|
|
|
array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 1);
|
|
g_array_append_val (array, capability);
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
MMModemCapability *
|
|
mm_common_capability_combinations_variant_to_array (GVariant *variant,
|
|
guint *n_capabilities)
|
|
{
|
|
GArray *array;
|
|
|
|
array = mm_common_capability_combinations_variant_to_garray (variant);
|
|
if (n_capabilities)
|
|
*n_capabilities = array->len;
|
|
return (MMModemCapability *) g_array_free (array, FALSE);
|
|
}
|
|
|
|
GVariant *
|
|
mm_common_capability_combinations_array_to_variant (const MMModemCapability *capabilities,
|
|
guint n_capabilities)
|
|
{
|
|
GVariantBuilder builder;
|
|
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
|
|
|
|
if (n_capabilities > 0) {
|
|
guint i;
|
|
|
|
for (i = 0; i < n_capabilities; i++)
|
|
g_variant_builder_add_value (&builder,
|
|
g_variant_new_uint32 ((guint32)capabilities[i]));
|
|
} else
|
|
g_variant_builder_add_value (&builder,
|
|
g_variant_new_uint32 (MM_MODEM_CAPABILITY_NONE));
|
|
|
|
return g_variant_builder_end (&builder);
|
|
}
|
|
|
|
GVariant *
|
|
mm_common_capability_combinations_garray_to_variant (GArray *array)
|
|
{
|
|
if (array)
|
|
return mm_common_capability_combinations_array_to_variant ((const MMModemCapability *)array->data,
|
|
array->len);
|
|
|
|
return mm_common_capability_combinations_array_to_variant (NULL, 0);
|
|
}
|
|
|
|
GVariant *
|
|
mm_common_build_capability_combinations_none (void)
|
|
{
|
|
GVariantBuilder builder;
|
|
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
|
|
g_variant_builder_add_value (&builder,
|
|
g_variant_new_uint32 (MM_MODEM_CAPABILITY_NONE));
|
|
return g_variant_builder_end (&builder);
|
|
}
|
|
|
|
GVariant *
|
|
mm_common_build_capability_combinations_any (void)
|
|
{
|
|
GVariantBuilder builder;
|
|
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
|
|
g_variant_builder_add_value (&builder,
|
|
g_variant_new_uint32 (MM_MODEM_CAPABILITY_ANY));
|
|
return g_variant_builder_end (&builder);
|
|
}
|
|
|
|
void
|
|
mm_common_get_bands_from_string (const gchar *str,
|
|
MMModemBand **bands,
|
|
guint *n_bands,
|
|
GError **error)
|
|
{
|
|
GError *inner_error = NULL;
|
|
GArray *array;
|
|
gchar **band_strings;
|
|
GEnumClass *enum_class;
|
|
|
|
array = g_array_new (FALSE, FALSE, sizeof (MMModemBand));
|
|
|
|
enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_MODEM_BAND));
|
|
band_strings = g_strsplit (str, "|", -1);
|
|
|
|
if (band_strings) {
|
|
guint i;
|
|
|
|
for (i = 0; band_strings[i]; i++) {
|
|
guint j;
|
|
gboolean found = FALSE;
|
|
|
|
for (j = 0; enum_class->values[j].value_nick; j++) {
|
|
if (!g_ascii_strcasecmp (band_strings[i], enum_class->values[j].value_nick)) {
|
|
g_array_append_val (array, enum_class->values[j].value);
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
inner_error = g_error_new (MM_CORE_ERROR,
|
|
MM_CORE_ERROR_INVALID_ARGS,
|
|
"Couldn't match '%s' with a valid MMModemBand value",
|
|
band_strings[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (inner_error) {
|
|
g_propagate_error (error, inner_error);
|
|
g_array_free (array, TRUE);
|
|
*n_bands = 0;
|
|
*bands = NULL;
|
|
} else {
|
|
if (!array->len) {
|
|
GEnumValue *value;
|
|
|
|
value = g_enum_get_value (enum_class, MM_MODEM_BAND_UNKNOWN);
|
|
g_array_append_val (array, value->value);
|
|
}
|
|
|
|
*n_bands = array->len;
|
|
*bands = (MMModemBand *)g_array_free (array, FALSE);
|
|
}
|
|
|
|
g_type_class_unref (enum_class);
|
|
g_strfreev (band_strings);
|
|
}
|
|
|
|
GArray *
|
|
mm_common_bands_variant_to_garray (GVariant *variant)
|
|
{
|
|
GArray *array = NULL;
|
|
|
|
if (variant) {
|
|
GVariantIter iter;
|
|
guint n;
|
|
|
|
g_variant_iter_init (&iter, variant);
|
|
n = g_variant_iter_n_children (&iter);
|
|
|
|
if (n > 0) {
|
|
guint32 band;
|
|
|
|
array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), n);
|
|
while (g_variant_iter_loop (&iter, "u", &band))
|
|
g_array_append_val (array, band);
|
|
}
|
|
}
|
|
|
|
/* If nothing set, fallback to default */
|
|
if (!array) {
|
|
guint32 band = MM_MODEM_BAND_UNKNOWN;
|
|
|
|
array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
|
|
g_array_append_val (array, band);
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
MMModemBand *
|
|
mm_common_bands_variant_to_array (GVariant *variant,
|
|
guint *n_bands)
|
|
{
|
|
GArray *array;
|
|
|
|
array = mm_common_bands_variant_to_garray (variant);
|
|
if (n_bands)
|
|
*n_bands = array->len;
|
|
return (MMModemBand *) g_array_free (array, FALSE);
|
|
}
|
|
|
|
GVariant *
|
|
mm_common_bands_array_to_variant (const MMModemBand *bands,
|
|
guint n_bands)
|
|
{
|
|
if (n_bands > 0) {
|
|
GVariantBuilder builder;
|
|
guint i;
|
|
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
|
|
|
|
for (i = 0; i < n_bands; i++)
|
|
g_variant_builder_add_value (&builder,
|
|
g_variant_new_uint32 ((guint32)bands[i]));
|
|
return g_variant_builder_end (&builder);
|
|
}
|
|
|
|
return mm_common_build_bands_unknown ();
|
|
}
|
|
|
|
GVariant *
|
|
mm_common_bands_garray_to_variant (GArray *array)
|
|
{
|
|
if (array)
|
|
return mm_common_bands_array_to_variant ((const MMModemBand *)array->data,
|
|
array->len);
|
|
|
|
return mm_common_bands_array_to_variant (NULL, 0);
|
|
}
|
|
|
|
static guint
|
|
cmp_band (MMModemBand *a, MMModemBand *b)
|
|
{
|
|
return (*a - *b);
|
|
}
|
|
|
|
gboolean
|
|
mm_common_bands_garray_cmp (GArray *a, GArray *b)
|
|
{
|
|
GArray *dup_a;
|
|
GArray *dup_b;
|
|
guint i;
|
|
gboolean different;
|
|
|
|
if (a->len != b->len)
|
|
return FALSE;
|
|
|
|
dup_a = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), a->len);
|
|
g_array_append_vals (dup_a, a->data, a->len);
|
|
|
|
dup_b = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), b->len);
|
|
g_array_append_vals (dup_b, b->data, b->len);
|
|
|
|
g_array_sort (dup_a, (GCompareFunc)cmp_band);
|
|
g_array_sort (dup_b, (GCompareFunc)cmp_band);
|
|
|
|
different = FALSE;
|
|
for (i = 0; !different && i < a->len; i++) {
|
|
if (g_array_index (dup_a, MMModemBand, i) != g_array_index (dup_b, MMModemBand, i))
|
|
different = TRUE;
|
|
}
|
|
|
|
g_array_unref (dup_a);
|
|
g_array_unref (dup_b);
|
|
|
|
return !different;
|
|
}
|
|
|
|
GArray *
|
|
mm_common_mode_combinations_variant_to_garray (GVariant *variant)
|
|
{
|
|
GArray *array = NULL;
|
|
|
|
if (variant) {
|
|
GVariantIter iter;
|
|
guint n;
|
|
|
|
g_variant_iter_init (&iter, variant);
|
|
n = g_variant_iter_n_children (&iter);
|
|
|
|
if (n > 0) {
|
|
MMModemModeCombination mode;
|
|
|
|
array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), n);
|
|
while (g_variant_iter_loop (&iter, "(uu)", &mode.allowed, &mode.preferred))
|
|
g_array_append_val (array, mode);
|
|
}
|
|
}
|
|
|
|
/* If nothing set, fallback to default */
|
|
if (!array) {
|
|
MMModemModeCombination default_mode;
|
|
|
|
default_mode.allowed = MM_MODEM_MODE_ANY;
|
|
default_mode.preferred = MM_MODEM_MODE_NONE;
|
|
array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
|
|
g_array_append_val (array, default_mode);
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
MMModemModeCombination *
|
|
mm_common_mode_combinations_variant_to_array (GVariant *variant,
|
|
guint *n_modes)
|
|
{
|
|
GArray *array;
|
|
|
|
array = mm_common_mode_combinations_variant_to_garray (variant);
|
|
if (n_modes)
|
|
*n_modes = array->len;
|
|
return (MMModemModeCombination *) g_array_free (array, FALSE);
|
|
}
|
|
|
|
GVariant *
|
|
mm_common_mode_combinations_array_to_variant (const MMModemModeCombination *modes,
|
|
guint n_modes)
|
|
{
|
|
if (n_modes > 0) {
|
|
GVariantBuilder builder;
|
|
guint i;
|
|
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uu)"));
|
|
|
|
for (i = 0; i < n_modes; i++)
|
|
g_variant_builder_add_value (&builder,
|
|
g_variant_new ("(uu)",
|
|
((guint32)modes[i].allowed),
|
|
((guint32)modes[i].preferred)));
|
|
return g_variant_builder_end (&builder);
|
|
}
|
|
|
|
return mm_common_build_mode_combinations_default ();
|
|
}
|
|
|
|
GVariant *
|
|
mm_common_mode_combinations_garray_to_variant (GArray *array)
|
|
{
|
|
if (array)
|
|
return mm_common_mode_combinations_array_to_variant ((const MMModemModeCombination *)array->data,
|
|
array->len);
|
|
|
|
return mm_common_mode_combinations_array_to_variant (NULL, 0);
|
|
}
|
|
|
|
GVariant *
|
|
mm_common_build_mode_combinations_default (void)
|
|
{
|
|
GVariantBuilder builder;
|
|
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uu)"));
|
|
g_variant_builder_add_value (&builder,
|
|
g_variant_new ("(uu)",
|
|
MM_MODEM_MODE_ANY,
|
|
MM_MODEM_MODE_NONE));
|
|
return g_variant_builder_end (&builder);
|
|
}
|
|
|
|
gboolean
|
|
mm_common_get_boolean_from_string (const gchar *value,
|
|
GError **error)
|
|
{
|
|
if (!g_ascii_strcasecmp (value, "true") || g_str_equal (value, "1"))
|
|
return TRUE;
|
|
|
|
if (g_ascii_strcasecmp (value, "false") && g_str_equal (value, "0"))
|
|
g_set_error (error,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_INVALID_ARGS,
|
|
"Cannot get boolean from string '%s'", value);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
MMModemCdmaRmProtocol
|
|
mm_common_get_rm_protocol_from_string (const gchar *str,
|
|
GError **error)
|
|
{
|
|
GEnumClass *enum_class;
|
|
guint i;
|
|
|
|
enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_MODEM_CDMA_RM_PROTOCOL));
|
|
|
|
for (i = 0; enum_class->values[i].value_nick; i++) {
|
|
if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
|
|
return enum_class->values[i].value;
|
|
}
|
|
|
|
g_set_error (error,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_INVALID_ARGS,
|
|
"Couldn't match '%s' with a valid MMModemCdmaRmProtocol value",
|
|
str);
|
|
return MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN;
|
|
}
|
|
|
|
MMBearerIpFamily
|
|
mm_common_get_ip_type_from_string (const gchar *str,
|
|
GError **error)
|
|
{
|
|
GFlagsClass *flags_class;
|
|
guint i;
|
|
|
|
flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_BEARER_IP_FAMILY));
|
|
|
|
for (i = 0; flags_class->values[i].value_nick; i++) {
|
|
if (!g_ascii_strcasecmp (str, flags_class->values[i].value_nick))
|
|
return flags_class->values[i].value;
|
|
}
|
|
|
|
g_set_error (error,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_INVALID_ARGS,
|
|
"Couldn't match '%s' with a valid MMBearerIpFamily value",
|
|
str);
|
|
return MM_BEARER_IP_FAMILY_NONE;
|
|
}
|
|
|
|
MMBearerAllowedAuth
|
|
mm_common_get_allowed_auth_from_string (const gchar *str,
|
|
GError **error)
|
|
{
|
|
GError *inner_error = NULL;
|
|
MMBearerAllowedAuth allowed_auth;
|
|
gchar **strings;
|
|
GFlagsClass *flags_class;
|
|
|
|
allowed_auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;
|
|
|
|
flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_BEARER_ALLOWED_AUTH));
|
|
strings = g_strsplit (str, "|", -1);
|
|
|
|
if (strings) {
|
|
guint i;
|
|
|
|
for (i = 0; strings[i]; i++) {
|
|
guint j;
|
|
gboolean found = FALSE;
|
|
|
|
for (j = 0; flags_class->values[j].value_nick; j++) {
|
|
if (!g_ascii_strcasecmp (strings[i], flags_class->values[j].value_nick)) {
|
|
allowed_auth |= flags_class->values[j].value;
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
inner_error = g_error_new (
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_INVALID_ARGS,
|
|
"Couldn't match '%s' with a valid MMBearerAllowedAuth value",
|
|
strings[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (inner_error) {
|
|
g_propagate_error (error, inner_error);
|
|
allowed_auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;
|
|
}
|
|
|
|
g_type_class_unref (flags_class);
|
|
g_strfreev (strings);
|
|
return allowed_auth;
|
|
}
|
|
|
|
MMSmsStorage
|
|
mm_common_get_sms_storage_from_string (const gchar *str,
|
|
GError **error)
|
|
{
|
|
GEnumClass *enum_class;
|
|
guint i;
|
|
|
|
enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_SMS_STORAGE));
|
|
|
|
for (i = 0; enum_class->values[i].value_nick; i++) {
|
|
if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
|
|
return enum_class->values[i].value;
|
|
}
|
|
|
|
g_set_error (error,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_INVALID_ARGS,
|
|
"Couldn't match '%s' with a valid MMSmsStorage value",
|
|
str);
|
|
return MM_SMS_STORAGE_UNKNOWN;
|
|
}
|
|
|
|
GVariant *
|
|
mm_common_build_bands_unknown (void)
|
|
{
|
|
GVariantBuilder builder;
|
|
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
|
|
g_variant_builder_add_value (&builder,
|
|
g_variant_new_uint32 (MM_MODEM_BAND_UNKNOWN));
|
|
return g_variant_builder_end (&builder);
|
|
}
|
|
|
|
GVariant *
|
|
mm_common_build_bands_any (void)
|
|
{
|
|
GVariantBuilder builder;
|
|
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
|
|
g_variant_builder_add_value (&builder,
|
|
g_variant_new_uint32 (MM_MODEM_BAND_ANY));
|
|
return g_variant_builder_end (&builder);
|
|
}
|
|
|
|
/* Expecting input as:
|
|
* key1=string,key2=true,key3=false...
|
|
* Strings may also be passed enclosed between double or single quotes, like:
|
|
* key1="this is a string", key2='and so is this'
|
|
*/
|
|
gboolean
|
|
mm_common_parse_key_value_string (const gchar *str,
|
|
GError **error,
|
|
MMParseKeyValueForeachFn callback,
|
|
gpointer user_data)
|
|
{
|
|
GError *inner_error = NULL;
|
|
gchar *dup, *p, *key, *key_end, *value, *value_end, quote;
|
|
|
|
g_return_val_if_fail (callback != NULL, FALSE);
|
|
g_return_val_if_fail (str != NULL, FALSE);
|
|
|
|
/* Allow empty strings, we'll just return with success */
|
|
while (g_ascii_isspace (*str))
|
|
str++;
|
|
if (!str[0])
|
|
return TRUE;
|
|
|
|
dup = g_strdup (str);
|
|
p = dup;
|
|
|
|
while (TRUE) {
|
|
gboolean keep_iteration = FALSE;
|
|
|
|
/* Skip leading spaces */
|
|
while (g_ascii_isspace (*p))
|
|
p++;
|
|
|
|
/* Key start */
|
|
key = p;
|
|
if (!g_ascii_isalnum (*key)) {
|
|
inner_error = g_error_new (MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"Key must start with alpha/num, starts with '%c'",
|
|
*key);
|
|
break;
|
|
}
|
|
|
|
/* Key end */
|
|
while (g_ascii_isalnum (*p) || (*p == '-') || (*p == '_'))
|
|
p++;
|
|
key_end = p;
|
|
if (key_end == key) {
|
|
inner_error = g_error_new (MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"Couldn't find a proper key");
|
|
break;
|
|
}
|
|
|
|
/* Skip whitespaces, if any */
|
|
while (g_ascii_isspace (*p))
|
|
p++;
|
|
|
|
/* Equal sign must be here */
|
|
if (*p != '=') {
|
|
inner_error = g_error_new (MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"Couldn't find equal sign separator");
|
|
break;
|
|
}
|
|
/* Skip the equal */
|
|
p++;
|
|
|
|
/* Skip whitespaces, if any */
|
|
while (g_ascii_isspace (*p))
|
|
p++;
|
|
|
|
/* Do we have a quote-enclosed string? */
|
|
if (*p == '\"' || *p == '\'') {
|
|
quote = *p;
|
|
/* Skip the quote */
|
|
p++;
|
|
/* Value start */
|
|
value = p;
|
|
/* Find the closing quote */
|
|
p = strchr (p, quote);
|
|
if (!p) {
|
|
inner_error = g_error_new (MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"Unmatched quotes in string value");
|
|
break;
|
|
}
|
|
|
|
/* Value end */
|
|
value_end = p;
|
|
/* Skip the quote */
|
|
p++;
|
|
} else {
|
|
/* Value start */
|
|
value = p;
|
|
|
|
/* Value end */
|
|
while ((*p != ',') && (*p != '\0') && !g_ascii_isspace (*p))
|
|
p++;
|
|
value_end = p;
|
|
}
|
|
|
|
/* Note that we allow value == value_end here */
|
|
|
|
/* Skip whitespaces, if any */
|
|
while (g_ascii_isspace (*p))
|
|
p++;
|
|
|
|
/* If a comma is found, we should keep the iteration */
|
|
if (*p == ',') {
|
|
/* skip the comma */
|
|
p++;
|
|
keep_iteration = TRUE;
|
|
}
|
|
|
|
/* Got key and value, prepare them and run the callback */
|
|
*value_end = '\0';
|
|
*key_end = '\0';
|
|
if (!callback (key, value, user_data)) {
|
|
/* We were told to abort */
|
|
break;
|
|
}
|
|
|
|
if (keep_iteration)
|
|
continue;
|
|
|
|
/* Check if no more key/value pairs expected */
|
|
if (*p == '\0')
|
|
break;
|
|
|
|
inner_error = g_error_new (MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"Unexpected content (%s) after value",
|
|
p);
|
|
break;
|
|
}
|
|
|
|
g_free (dup);
|
|
|
|
if (inner_error) {
|
|
g_propagate_error (error, inner_error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean
|
|
mm_get_int_from_str (const gchar *str,
|
|
gint *out)
|
|
{
|
|
glong num;
|
|
|
|
if (!str || !str[0])
|
|
return FALSE;
|
|
|
|
for (num = 0; str[num]; num++) {
|
|
if (str[num] != '-' && !g_ascii_isdigit (str[num]))
|
|
return FALSE;
|
|
}
|
|
|
|
errno = 0;
|
|
num = strtol (str, NULL, 10);
|
|
if (!errno && num >= G_MININT && num <= G_MAXINT) {
|
|
*out = (gint)num;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
mm_get_int_from_match_info (GMatchInfo *match_info,
|
|
guint32 match_index,
|
|
gint *out)
|
|
{
|
|
gchar *s;
|
|
gboolean ret;
|
|
|
|
s = g_match_info_fetch (match_info, match_index);
|
|
g_return_val_if_fail (s != NULL, FALSE);
|
|
|
|
ret = mm_get_int_from_str (s, out);
|
|
g_free (s);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* mm_get_uint_from_str:
|
|
* @str: the string to convert to an unsigned int
|
|
* @out: on success, the number
|
|
*
|
|
* Converts a string to an unsigned number. All characters in the string
|
|
* MUST be valid digits (0 - 9), otherwise FALSE is returned.
|
|
*
|
|
* Returns: %TRUE if the string was converted, %FALSE if it was not or if it
|
|
* did not contain only digits.
|
|
*/
|
|
gboolean
|
|
mm_get_uint_from_str (const gchar *str,
|
|
guint *out)
|
|
{
|
|
gulong num;
|
|
|
|
if (!str || !str[0])
|
|
return FALSE;
|
|
|
|
for (num = 0; str[num]; num++) {
|
|
if (!g_ascii_isdigit (str[num]))
|
|
return FALSE;
|
|
}
|
|
|
|
errno = 0;
|
|
num = strtoul (str, NULL, 10);
|
|
if (!errno && num <= G_MAXUINT) {
|
|
*out = (guint)num;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
mm_get_uint_from_match_info (GMatchInfo *match_info,
|
|
guint32 match_index,
|
|
guint *out)
|
|
{
|
|
gchar *s;
|
|
gboolean ret;
|
|
|
|
s = g_match_info_fetch (match_info, match_index);
|
|
g_return_val_if_fail (s != NULL, FALSE);
|
|
|
|
ret = mm_get_uint_from_str (s, out);
|
|
g_free (s);
|
|
|
|
return ret;
|
|
}
|
|
|
|
gboolean
|
|
mm_get_double_from_str (const gchar *str,
|
|
gdouble *out)
|
|
{
|
|
gdouble num;
|
|
guint i;
|
|
|
|
if (!str || !str[0])
|
|
return FALSE;
|
|
|
|
for (i = 0; str[i]; i++) {
|
|
/* we don't really expect numbers in scientific notation, so
|
|
* don't bother looking for exponents and such */
|
|
if (str[i] != '-' &&
|
|
str[i] != '.' &&
|
|
!g_ascii_isdigit (str[i]))
|
|
return FALSE;
|
|
}
|
|
|
|
errno = 0;
|
|
num = strtod (str, NULL);
|
|
if (!errno) {
|
|
*out = num;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
mm_get_double_from_match_info (GMatchInfo *match_info,
|
|
guint32 match_index,
|
|
gdouble *out)
|
|
{
|
|
gchar *s;
|
|
gboolean ret;
|
|
|
|
s = g_match_info_fetch (match_info, match_index);
|
|
g_return_val_if_fail (s != NULL, FALSE);
|
|
|
|
ret = mm_get_double_from_str (s, out);
|
|
g_free (s);
|
|
|
|
return ret;
|
|
}
|
|
|
|
gchar *
|
|
mm_get_string_unquoted_from_match_info (GMatchInfo *match_info,
|
|
guint32 match_index)
|
|
{
|
|
gchar *str;
|
|
gsize len;
|
|
|
|
str = g_match_info_fetch (match_info, match_index);
|
|
if (!str)
|
|
return NULL;
|
|
|
|
len = strlen (str);
|
|
|
|
/* Unquote the item if needed */
|
|
if ((len >= 2) && (str[0] == '"') && (str[len - 1] == '"')) {
|
|
str[0] = ' ';
|
|
str[len - 1] = ' ';
|
|
str = g_strstrip (str);
|
|
}
|
|
|
|
if (!str[0]) {
|
|
g_free (str);
|
|
return NULL;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const gchar *
|
|
mm_sms_delivery_state_get_string_extended (guint delivery_state)
|
|
{
|
|
if (delivery_state > 0x02 && delivery_state < 0x20) {
|
|
if (delivery_state < 0x10)
|
|
return "completed-reason-reserved";
|
|
else
|
|
return "completed-sc-specific-reason";
|
|
}
|
|
|
|
if (delivery_state > 0x25 && delivery_state < 0x40) {
|
|
if (delivery_state < 0x30)
|
|
return "temporary-error-reason-reserved";
|
|
else
|
|
return "temporary-error-sc-specific-reason";
|
|
}
|
|
|
|
if (delivery_state > 0x49 && delivery_state < 0x60) {
|
|
if (delivery_state < 0x50)
|
|
return "error-reason-reserved";
|
|
else
|
|
return "error-sc-specific-reason";
|
|
}
|
|
|
|
if (delivery_state > 0x65 && delivery_state < 0x80) {
|
|
if (delivery_state < 0x70)
|
|
return "temporary-fatal-error-reason-reserved";
|
|
else
|
|
return "temporary-fatal-error-sc-specific-reason";
|
|
}
|
|
|
|
if (delivery_state >= 0x80 && delivery_state < 0x100)
|
|
return "unknown-reason-reserved";
|
|
|
|
if (delivery_state >= 0x100)
|
|
return "unknown";
|
|
|
|
/* Otherwise, use the MMSmsDeliveryState enum as we can match the known
|
|
* value */
|
|
return mm_sms_delivery_state_get_string ((MMSmsDeliveryState)delivery_state);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* From hostap, Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> */
|
|
|
|
static gint
|
|
hex2num (gchar c)
|
|
{
|
|
if (c >= '0' && c <= '9')
|
|
return c - '0';
|
|
if (c >= 'a' && c <= 'f')
|
|
return c - 'a' + 10;
|
|
if (c >= 'A' && c <= 'F')
|
|
return c - 'A' + 10;
|
|
return -1;
|
|
}
|
|
|
|
gint
|
|
mm_utils_hex2byte (const gchar *hex)
|
|
{
|
|
gint a, b;
|
|
|
|
a = hex2num (*hex++);
|
|
if (a < 0)
|
|
return -1;
|
|
b = hex2num (*hex++);
|
|
if (b < 0)
|
|
return -1;
|
|
return (a << 4) | b;
|
|
}
|
|
|
|
gchar *
|
|
mm_utils_hexstr2bin (const gchar *hex, gsize *out_len)
|
|
{
|
|
const gchar *ipos = hex;
|
|
gchar *buf = NULL;
|
|
gsize i;
|
|
gint a;
|
|
gchar *opos;
|
|
gsize len;
|
|
|
|
len = strlen (hex);
|
|
|
|
/* Length must be a multiple of 2 */
|
|
g_return_val_if_fail ((len % 2) == 0, NULL);
|
|
|
|
opos = buf = g_malloc0 ((len / 2) + 1);
|
|
for (i = 0; i < len; i += 2) {
|
|
a = mm_utils_hex2byte (ipos);
|
|
if (a < 0) {
|
|
g_free (buf);
|
|
return NULL;
|
|
}
|
|
*opos++ = a;
|
|
ipos += 2;
|
|
}
|
|
*out_len = len / 2;
|
|
return buf;
|
|
}
|
|
|
|
/* End from hostap */
|
|
|
|
gboolean
|
|
mm_utils_ishexstr (const gchar *hex)
|
|
{
|
|
gsize len;
|
|
gsize i;
|
|
|
|
/* Length not multiple of 2? */
|
|
len = strlen (hex);
|
|
if (len % 2 != 0)
|
|
return FALSE;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
/* Non-hex char? */
|
|
if (hex[i] >= '0' && hex[i] <= '9')
|
|
continue;
|
|
if (hex[i] >= 'a' && hex[i] <= 'f')
|
|
continue;
|
|
if (hex[i] >= 'A' && hex[i] <= 'F')
|
|
continue;
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gchar *
|
|
mm_utils_bin2hexstr (const guint8 *bin, gsize len)
|
|
{
|
|
GString *ret;
|
|
gsize i;
|
|
|
|
g_return_val_if_fail (bin != NULL, NULL);
|
|
|
|
ret = g_string_sized_new (len * 2 + 1);
|
|
for (i = 0; i < len; i++)
|
|
g_string_append_printf (ret, "%.2X", bin[i]);
|
|
return g_string_free (ret, FALSE);
|
|
}
|
|
|
|
gboolean
|
|
mm_utils_check_for_single_value (guint32 value)
|
|
{
|
|
gboolean found = FALSE;
|
|
guint32 i;
|
|
|
|
for (i = 1; i <= 32; i++) {
|
|
if (value & 0x1) {
|
|
if (found)
|
|
return FALSE; /* More than one bit set */
|
|
found = TRUE;
|
|
}
|
|
value >>= 1;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|