diff options
author | Aliaksandr Barouski <alex.borovsky@gmail.com> | 2016-07-07 15:47:20 -0700 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2016-10-26 14:59:43 +0200 |
commit | 24cdbd32e97373626f977b7bbb4bdf1b92b2d513 (patch) | |
tree | 5d3847d156a68fd082a34ff5223b53d49d974cd8 /src/qmicli | |
parent | fa39dc14a6b8bdea736239da9fee59a65e289b1d (diff) | |
download | external_libqmi-24cdbd32e97373626f977b7bbb4bdf1b92b2d513.zip external_libqmi-24cdbd32e97373626f977b7bbb4bdf1b92b2d513.tar.gz external_libqmi-24cdbd32e97373626f977b7bbb4bdf1b92b2d513.tar.bz2 |
pdc: new PDC service implementation
Added calls for configure hp4120 LTE modem
Diffstat (limited to 'src/qmicli')
-rw-r--r-- | src/qmicli/Makefile.am | 1 | ||||
-rw-r--r-- | src/qmicli/qmicli-helpers.c | 105 | ||||
-rw-r--r-- | src/qmicli/qmicli-helpers.h | 8 | ||||
-rw-r--r-- | src/qmicli/qmicli-pdc.c | 1407 | ||||
-rw-r--r-- | src/qmicli/qmicli.c | 11 | ||||
-rw-r--r-- | src/qmicli/qmicli.h | 7 |
6 files changed, 1539 insertions, 0 deletions
diff --git a/src/qmicli/Makefile.am b/src/qmicli/Makefile.am index 9583b41..90d0e53 100644 --- a/src/qmicli/Makefile.am +++ b/src/qmicli/Makefile.am @@ -20,6 +20,7 @@ qmicli_SOURCES = \ qmicli-wds.c \ qmicli-nas.c \ qmicli-pbm.c \ + qmicli-pdc.c \ qmicli-uim.c \ qmicli-wms.c \ qmicli-wda.c \ diff --git a/src/qmicli/qmicli-helpers.c b/src/qmicli/qmicli-helpers.c index 5443cad..801e7fd 100644 --- a/src/qmicli/qmicli-helpers.c +++ b/src/qmicli/qmicli-helpers.c @@ -23,6 +23,7 @@ #include <stdlib.h> #include <string.h> #include <errno.h> +#include <ctype.h> #include "qmicli-helpers.h" @@ -315,6 +316,64 @@ qmicli_read_firmware_id_from_string (const gchar *str, } gboolean +qmicli_read_binary_array_from_string (const gchar *str, + GArray **out) { + char a; + gsize i,len; + if (!str) return FALSE; + + if((len = strlen(str)) & 1) return FALSE; + + *out = g_array_sized_new(FALSE, TRUE, 1, len >> 1); + g_array_set_size(*out, len >> 1); + + for (i = 0; i < len; i++) { + a = toupper(str[i]); + if (!isxdigit(a)) + break; + if (isdigit(a)) { + a -= '0'; + } else { + a = a - 'A' + 10; + } + + if (i & 1) { + g_array_index(*out, gchar, i >> 1) |= a; + } else { + g_array_index(*out, gchar, i >> 1) = a<<4; + } + } + if (i < len) { + g_free(out); + out = NULL; + } + + return TRUE; +} + + +gboolean +qmicli_read_pdc_configuration_type_from_string (const gchar *str, + QmiPdcConfigurationType *out) +{ + GType type; + GEnumClass *enum_class; + GEnumValue *enum_value; + + type = qmi_pdc_configuration_type_get_type (); + enum_class = G_ENUM_CLASS (g_type_class_ref (type)); + enum_value = g_enum_get_value_by_nick (enum_class, str); + + if (enum_value) + *out = (QmiPdcConfigurationType)enum_value->value; + else + g_printerr ("error: invalid configuration type value given: '%s'\n", str); + + g_type_class_unref (enum_class); + return !!enum_value; +} + +gboolean qmicli_read_radio_interface_from_string (const gchar *str, QmiNasRadioInterface *out) { @@ -522,6 +581,52 @@ qmicli_read_uint_from_string (const gchar *str, return FALSE; } +gboolean +qmicli_read_uint16_from_string (const gchar *str, + guint16 *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_MAXUINT16) { + *out = (guint16)num; + return TRUE; + } + return FALSE; +} + +gboolean +qmicli_read_uint8_from_string (const gchar *str, + guint8 *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_MAXUINT8) { + *out = (guint8)num; + return TRUE; + } + return FALSE; +} + gchar * qmicli_get_supported_messages_list (const guint8 *data, gsize len) diff --git a/src/qmicli/qmicli-helpers.h b/src/qmicli/qmicli-helpers.h index 1a2a78c..0bf9e76 100644 --- a/src/qmicli/qmicli-helpers.h +++ b/src/qmicli/qmicli-helpers.h @@ -45,6 +45,10 @@ gboolean qmicli_read_enable_disable_from_string (const gchar *str, gboolean qmicli_read_firmware_id_from_string (const gchar *str, QmiDmsFirmwareImageType *out_type, guint *out_index); +gboolean qmicli_read_binary_array_from_string (const gchar *str, + GArray **out); +gboolean qmicli_read_pdc_configuration_type_from_string (const gchar *str, + QmiPdcConfigurationType *out); gboolean qmicli_read_radio_interface_from_string (const gchar *str, QmiNasRadioInterface *out); gboolean qmicli_read_net_open_flags_from_string (const gchar *str, @@ -65,6 +69,10 @@ gboolean qmicli_read_non_empty_string (const gchar *str, gchar **out); gboolean qmicli_read_uint_from_string (const gchar *str, guint *out); +gboolean qmicli_read_uint16_from_string (const gchar *str, + guint16 *out); +gboolean qmicli_read_uint8_from_string (const gchar *str, + guint8 *out); gboolean qmicli_read_yes_no_from_string (const gchar *str, gboolean *out); diff --git a/src/qmicli/qmicli-pdc.c b/src/qmicli/qmicli-pdc.c new file mode 100644 index 0000000..caa2386 --- /dev/null +++ b/src/qmicli/qmicli-pdc.c @@ -0,0 +1,1407 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * qmicli -- Command line interface to control QMI devices + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright (C) 2013-2015 Aleksander Morgado <aleksander@aleksander.es> + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <locale.h> +#include <string.h> + +#include <glib.h> +#include <glib/gprintf.h> +#include <gio/gio.h> + +#include <libqmi-glib.h> + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#define LOAD_CONFIG_CHUNK_SIZE 0x400 + +/* Info about config */ +typedef struct { + GArray *id; + QmiPdcConfigurationType config_type; + guint32 token; + guint32 version; + gchar *description; + guint32 total_size; +} ConfigInfo; + +/* Info about loading config */ +typedef struct { + GMappedFile *mapped_file; + GArray *checksum; + gsize offset; +} LoadConfigFileData; + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientPdc *client; + GCancellable *cancellable; + + /* local data */ + GArray *config_list; + gint configs_loaded; + GArray *active_config_id; + GArray *pending_config_id; + gboolean ids_loaded; + guint list_configs_indication_id; + guint get_selected_config_indication_id; + + LoadConfigFileData *load_config_file_data; + guint load_config_indication_id; + guint get_config_info_indication_id; + + guint set_selected_config_indication_id; + guint activate_config_indication_id; + guint deactivate_config_indication_id; + + guint token; +} Context; +static Context *ctx; + +/* Options */ +static gchar *list_configs_str; +static gchar *delete_config_str; +static gchar *activate_config_str; +static gchar *deactivate_config_str; +static gchar *load_config_str; +static gboolean noop_flag; + +static GOptionEntry entries[] = { + {"pdc-list-configs", 0, 0, G_OPTION_ARG_STRING, &list_configs_str, + "List all configs", + "[(platform|software)]"}, + {"pdc-delete-config", 0, 0, G_OPTION_ARG_STRING, &delete_config_str, + "Delete config", + "[(platform|software),ConfigId]"}, + {"pdc-activate-config", 0, 0, G_OPTION_ARG_STRING, &activate_config_str, + "Activate config", + "[(platform|software),ConfigId]"}, + {"pdc-deactivate-config", 0, 0, G_OPTION_ARG_STRING, &deactivate_config_str, + "Deactivate config", + "[(platform|software),ConfigId]"}, + {"pdc-load-config", 0, 0, G_OPTION_ARG_STRING, &load_config_str, + "Load config to device", + "[Path to config]"}, + {"pdc-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a PDC client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL}, + {NULL} +}; + +GOptionGroup * +qmicli_pdc_get_option_group ( + void) +{ + GOptionGroup *group; + + group = g_option_group_new ("pdc", + "PDC options", + "Show platform device configurations options", NULL, NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_pdc_options_enabled ( + void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = !!list_configs_str + + !!delete_config_str + + !!activate_config_str + !!deactivate_config_str + !!load_config_str + noop_flag; + + if (n_actions > 1) { + g_printerr ("error: too many PDC actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static Context * +context_new ( + QmiDevice * device, + QmiClientPdc * client, + GCancellable * cancellable) +{ + Context *context; + + context = g_slice_new (Context); + context->device = g_object_ref (device); + context->client = g_object_ref (client); + context->cancellable = g_object_ref (cancellable); + + context->config_list = NULL; + context->configs_loaded = 0; + context->active_config_id = NULL; + context->pending_config_id = NULL; + context->ids_loaded = FALSE; + context->list_configs_indication_id = 0; + context->get_selected_config_indication_id = 0; + + context->load_config_file_data = NULL; + context->load_config_indication_id = 0; + context->get_config_info_indication_id = 0; + + context->set_selected_config_indication_id = 0; + context->activate_config_indication_id = 0; + context->deactivate_config_indication_id = 0; + + context->token = 0; + return context; +} + +static void +context_free ( + Context * context) +{ + int i; + ConfigInfo *current_config; + + if (!context) + return; + + if (context->config_list) { + for (i = 0; i < context->config_list->len; i++) { + current_config = &g_array_index (context->config_list, ConfigInfo, i); + if (current_config->description) + g_free (current_config->description); + if (current_config->id) + g_array_unref (current_config->id); + } + g_array_unref (context->config_list); + g_signal_handler_disconnect (context->client, context->list_configs_indication_id); + g_signal_handler_disconnect (context->client, context->get_config_info_indication_id); + g_signal_handler_disconnect (context->client, context->get_selected_config_indication_id); + } + if (context->load_config_file_data) { + g_array_unref (context->load_config_file_data->checksum); + g_mapped_file_unref (context->load_config_file_data->mapped_file); + g_slice_free (LoadConfigFileData, context->load_config_file_data); + g_signal_handler_disconnect (context->client, context->load_config_indication_id); + } + if (context->client) + g_object_unref (context->client); + if (context->set_selected_config_indication_id) { + g_signal_handler_disconnect (context->client, context->set_selected_config_indication_id); + } + if (context->activate_config_indication_id) { + g_signal_handler_disconnect (context->client, context->activate_config_indication_id); + } + if (context->deactivate_config_indication_id) { + g_signal_handler_disconnect (context->client, context->deactivate_config_indication_id); + } + g_object_unref (context->cancellable); + g_object_unref (context->device); + + g_slice_free (Context, context); +} + +static void +operation_shutdown ( + gboolean operation_status) +{ + /* Cleanup context and finish async operation */ + context_free (ctx); + ctx = NULL; + qmicli_async_operation_done (operation_status); +} + +/**************************************************************************************** + * List configs + ****************************************************************************************/ +static const char * +printable_config_type ( + QmiPdcConfigurationType type) +{ + if (type == QMI_PDC_CONFIGURATION_TYPE_PLATFORM) { + return "Platform"; + } else if (type == QMI_PDC_CONFIGURATION_TYPE_SOFTWARE) { + return "Software"; + } else { + return "Unknown"; + } +} + +static char * +printable_id ( + GArray * id) +{ + if (!id) { + return g_strdup ("Id is NULL?"); + } else if (id->len == 0) { + return g_strdup ("Id is empty"); + } else { + GString *printable; + int i; + + printable = g_string_new (""); + for (i = 0; i < id->len; i++) { + guint8 ch = g_array_index (id, guint8, i); + + g_string_append_printf (printable, "%02hhX", ch); + } + return g_string_free (printable, FALSE); + } +} + +static gboolean +is_array_equals ( + GArray * a, + GArray * b) +{ + int i; + + if (a == NULL && b == NULL) { + return TRUE; + } else if (a == NULL || b == NULL) { + return FALSE; + } else if (a->len != b->len) { + return FALSE; + } + for (i = 0; i < a->len; i++) { + if (g_array_index (a, guint8, i) != g_array_index (b, guint8, i)) { + return FALSE; + } + } + + return TRUE; +} + +static const +char *status_string ( + GArray * id) +{ + if (!id) { + return "UNKNOWN"; + } else if (is_array_equals (id, ctx->active_config_id)) { + return "Active"; + } else if (is_array_equals (id, ctx->pending_config_id)) { + return "Pending"; + } + return "Inactive"; +} + +static void +print_configs ( + GArray * configs) +{ + ConfigInfo *current_config = NULL; + int i; + + g_printf ("CONFIGS: %d\n", ctx->config_list->len); + for (i = 0; i < ctx->config_list->len; i++) { + char *id_str; + + current_config = &g_array_index (ctx->config_list, ConfigInfo, i); + + g_printf ("START_CONFIG\n"); + g_printf ("Config Index : %i\n", i); + g_printf ("Description : %s\n", current_config->description); + g_printf ("Type : %s\n", printable_config_type (current_config->config_type)); + g_printf ("Size : %u\n", current_config->total_size); + g_printf ("Status : %s\n", status_string (current_config->id)); + g_printf ("Version : 0x%x\n", current_config->version); + id_str = printable_id (current_config->id); + g_printf ("ID : %s\n", id_str); + g_free (id_str); + g_printf ("END_CONFIG\n\n"); + + } +} + +static void +get_config_info_ready ( + QmiClientPdc * client, + GAsyncResult * res) +{ + GError *error = NULL; + QmiMessagePdcGetConfigInfoOutput *output; + + output = qmi_client_pdc_get_config_info_finish (client, res, &error); + + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_pdc_get_config_info_output_get_result (output, &error)) { + g_printerr ("error: couldn't get config info: %s\n", error->message); + g_error_free (error); + qmi_message_pdc_get_config_info_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_pdc_get_config_info_output_unref (output); +} + +static void +get_config_info_ready_indication ( + QmiClientPdc * client, + QmiIndicationPdcGetConfigInfoOutput * output) +{ + GError *error = NULL; + ConfigInfo *current_config = NULL; + guint32 token; + const gchar *description; + int i; + guint16 error_code; + + if (!qmi_indication_pdc_get_config_info_output_get_indication_result (output, + &error_code, &error)) { + g_printerr ("error: couldn't get config info: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } else if (error_code != 0) { + g_printerr ("error: couldn't get config info: %s\n", + qmi_protocol_error_get_string ((QmiProtocolError) error_code)); + operation_shutdown (FALSE); + return; + } + + if (!qmi_indication_pdc_get_config_info_output_get_token (output, &token, &error)) { + g_printerr ("error: couldn't get config info: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + for (i = 0; i < ctx->config_list->len; i++) { + current_config = &g_array_index (ctx->config_list, ConfigInfo, i); + if (current_config->token == token) + break; + } + + if (!qmi_indication_pdc_get_config_info_output_get_total_size (output, + ¤t_config->total_size, + &error) || + !qmi_indication_pdc_get_config_info_output_get_version (output, + ¤t_config->version, + &error) || + !qmi_indication_pdc_get_config_info_output_get_description (output, &description, &error)) { + g_printerr ("error: couldn't get config info: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + current_config->description = g_strdup (description); + + ctx->configs_loaded++; + if (ctx->configs_loaded == ctx->config_list->len && ctx->ids_loaded) { + print_configs (ctx->config_list); + operation_shutdown (TRUE); + } +} + +static void +list_configs_ready ( + QmiClientPdc * client, + GAsyncResult * res) +{ + GError *error = NULL; + QmiMessagePdcListConfigsOutput *output; + + output = qmi_client_pdc_list_configs_finish (client, res, &error); + + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_pdc_list_configs_output_get_result (output, &error)) { + g_printerr ("error: couldn't list configs: %s\n", error->message); + g_error_free (error); + qmi_message_pdc_list_configs_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_pdc_list_configs_output_unref (output); +} + +static void +list_configs_ready_indication ( + QmiClientPdc * client, + QmiIndicationPdcListConfigsOutput * output) +{ + GError *error = NULL; + GArray *configs = NULL; + int i; + guint16 error_code; + + if (!qmi_indication_pdc_list_configs_output_get_indication_result (output, &error_code, &error)) { + g_printerr ("error: couldn't list configs: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } else if (error_code != 0) { + g_printerr ("error: couldn't list config: %s\n", + qmi_protocol_error_get_string ((QmiProtocolError) error_code)); + operation_shutdown (FALSE); + return; + } + + if (!qmi_indication_pdc_list_configs_output_get_configs (output, &configs, &error)) { + g_printerr ("error: couldn't list configs: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + ctx->config_list = g_array_sized_new (FALSE, TRUE, sizeof (ConfigInfo), configs->len); + g_array_set_size (ctx->config_list, configs->len); + for (i = 0; i < configs->len; i++) { + ConfigInfo *current_info; + QmiIndicationPdcListConfigsOutputConfigsElement *element; + QmiConfigTypeAndId type_with_id; + QmiMessagePdcGetConfigInfoInput *input; + guint32 token = ctx->token++; + + element = &g_array_index (configs, QmiIndicationPdcListConfigsOutputConfigsElement, i); + + current_info = &g_array_index (ctx->config_list, ConfigInfo, i); + + input = qmi_message_pdc_get_config_info_input_new (); + current_info->token = token; + current_info->id = g_array_ref (element->id); + current_info->config_type = element->config_type; + type_with_id.config_type = element->config_type; + type_with_id.id = current_info->id; + g_printerr ("Fetching config type: %d\n", current_info->config_type); + if (!qmi_message_pdc_get_config_info_input_set_type_with_id (input, &type_with_id, &error)) { + g_printerr ("error: couldn't set type with id: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + if (!qmi_message_pdc_get_config_info_input_set_token (input, token, &error)) { + g_printerr ("error: couldn't set token: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + qmi_client_pdc_get_config_info (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) get_config_info_ready, NULL); + + } + + g_print ("Loaded configs: %d\n", ctx->config_list->len); + if (ctx->configs_loaded == ctx->config_list->len && ctx->ids_loaded) { + print_configs (ctx->config_list); + operation_shutdown (TRUE); + } +} + +static void +get_selected_config_ready ( + QmiClientPdc * client, + GAsyncResult * res) +{ + GError *error = NULL; + QmiMessagePdcGetSelectedConfigOutput *output; + + output = qmi_client_pdc_get_selected_config_finish (client, res, &error); + + if (!qmi_message_pdc_get_selected_config_output_get_result (output, &error)) { + g_printerr ("error: couldn't get selected config: %s\n", error->message); + g_error_free (error); + qmi_message_pdc_get_selected_config_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_pdc_get_selected_config_output_unref (output); +} + +static void +get_selected_config_ready_indication ( + QmiClientPdc * client, + QmiIndicationPdcGetSelectedConfigOutput * output) +{ + GArray *pending_id = NULL; + GArray *active_id = NULL; + GError *error = NULL; + guint16 error_code; + + if (!qmi_indication_pdc_get_selected_config_output_get_indication_result (output, + &error_code, + &error)) { + g_printerr ("error: couldn't get selected config: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } else if (error_code != 0) { + g_printerr ("error: couldn't get selected config: %s\n", + qmi_protocol_error_get_string ((QmiProtocolError) error_code)); + } + + qmi_indication_pdc_get_selected_config_output_get_pending_id (output, &pending_id, NULL); + qmi_indication_pdc_get_selected_config_output_get_active_id (output, &active_id, NULL); + if (active_id) { + ctx->active_config_id = g_array_ref (active_id); + } + if (pending_id) { + ctx->pending_config_id = g_array_ref (pending_id); + } + + ctx->ids_loaded = TRUE; + if (ctx->configs_loaded == ctx->config_list->len && ctx->ids_loaded) { + print_configs (ctx->config_list); + operation_shutdown (TRUE); + } +} + +static void +activate_config_ready_indication ( + QmiClientPdc * client, + QmiIndicationPdcActivateConfigOutput * output) +{ + GError *error = NULL; + guint16 error_code; + + if (!qmi_indication_pdc_activate_config_output_get_indication_result (output, + &error_code, &error)) { + g_printerr ("error: couldn't activate config: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } else if (error_code != 0) { + g_printerr ("error: couldn't activate config: %s\n", + qmi_protocol_error_get_string ((QmiProtocolError) error_code)); + operation_shutdown (FALSE); + } + + operation_shutdown (TRUE); +} + +static void +deactivate_config_ready_indication ( + QmiClientPdc * client, + QmiIndicationPdcDeactivateConfigOutput * output) +{ + GError *error = NULL; + guint16 error_code; + + if (!qmi_indication_pdc_deactivate_config_output_get_indication_result (output, + &error_code, &error)) { + g_printerr ("error: couldn't deactivate config: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } else if (error_code != 0) { + g_printerr ("error: couldn't deactivate config: %s\n", + qmi_protocol_error_get_string ((QmiProtocolError) error_code)); + operation_shutdown (FALSE); + } + + operation_shutdown (TRUE); +} + +static QmiMessagePdcListConfigsInput * +list_configs_input_create ( + const gchar * str) +{ + QmiMessagePdcListConfigsInput *input = NULL; + QmiPdcConfigurationType configType; + + if (qmicli_read_pdc_configuration_type_from_string (str, &configType)) { + GError *error = NULL; + + input = qmi_message_pdc_list_configs_input_new (); + if (!qmi_message_pdc_list_configs_input_set_config_type (input, configType, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", error->message); + g_error_free (error); + qmi_message_pdc_list_configs_input_unref (input); + input = NULL; + } + if (!qmi_message_pdc_list_configs_input_set_token (input, ctx->token++, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", error->message); + g_error_free (error); + qmi_message_pdc_list_configs_input_unref (input); + input = NULL; + } + + } + + return input; +} + +/**************************************************************************************** + * Activate and deactivate configs + ****************************************************************************************/ +static QmiConfigTypeAndId * +parse_type_and_id ( + const gchar * str) +{ + GArray *id = NULL; + int num_parts; + gchar **substrings = g_strsplit (str, ",", -1); + QmiPdcConfigurationType config_type; + QmiConfigTypeAndId *result = NULL; + + for (num_parts = 0; substrings[num_parts]; num_parts++) ; + if (num_parts != 2) { + g_printerr ("Expected 2 parameters, but found: '%d'\n", num_parts); + g_strfreev (substrings); + return NULL; + } + + if (!qmicli_read_pdc_configuration_type_from_string (substrings[0], &config_type)) { + g_printerr ("Incorrect id specified: %s\n", substrings[0]); + g_strfreev (substrings); + return NULL; + } + + if (!qmicli_read_binary_array_from_string (substrings[1], &id)) { + g_printerr ("Incorrect config type specified: %s\n", substrings[1]); + g_strfreev (substrings); + return NULL; + } + + result = g_slice_new0 (QmiConfigTypeAndId); + if (!result) { + g_printerr ("Error allocating QmiConfigTypeAndId\n"); + g_strfreev (substrings); + g_array_unref (id); + return NULL; + } + result->config_type = config_type; + result->id = id; + + return result; +} + +static QmiMessagePdcGetSelectedConfigInput * +get_selected_config_input_create ( + const gchar * str) +{ + QmiMessagePdcGetSelectedConfigInput *input = NULL; + QmiPdcConfigurationType configType; + + if (qmicli_read_pdc_configuration_type_from_string (str, &configType)) { + GError *error = NULL; + + input = qmi_message_pdc_get_selected_config_input_new (); + if (!qmi_message_pdc_get_selected_config_input_set_config_type (input, configType, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", error->message); + g_error_free (error); + qmi_message_pdc_get_selected_config_input_unref (input); + input = NULL; + } + if (!qmi_message_pdc_get_selected_config_input_set_token (input, ctx->token++, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", error->message); + g_error_free (error); + qmi_message_pdc_get_selected_config_input_unref (input); + input = NULL; + } + + } + + return input; +} + +static QmiMessagePdcDeleteConfigInput * +delete_config_input_create ( + const gchar * str) +{ + QmiMessagePdcDeleteConfigInput *input = NULL; + QmiConfigTypeAndId *type_and_id; + + type_and_id = parse_type_and_id (str); + + if (type_and_id) { + GError *error = NULL; + + input = qmi_message_pdc_delete_config_input_new (); + if (!qmi_message_pdc_delete_config_input_set_config_type (input, + type_and_id->config_type, + &error) || + !qmi_message_pdc_delete_config_input_set_token (input, + ctx->token++, + &error) || + !qmi_message_pdc_delete_config_input_set_id (input, type_and_id->id, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", error->message); + g_error_free (error); + g_free (type_and_id); + qmi_message_pdc_delete_config_input_unref (input); + input = NULL; + } + g_slice_free (QmiConfigTypeAndId, type_and_id); + } + + return input; +} + +static void +delete_config_ready ( + QmiClientPdc * client, + GAsyncResult * res) +{ + GError *error = NULL; + QmiMessagePdcDeleteConfigOutput *output; + + output = qmi_client_pdc_delete_config_finish (client, res, &error); + + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_pdc_delete_config_output_get_result (output, &error)) { + g_printerr ("error: couldn't delete config: %s\n", error->message); + g_error_free (error); + qmi_message_pdc_delete_config_output_unref (output); + operation_shutdown (FALSE); + return; + } + + operation_shutdown (TRUE); + qmi_message_pdc_delete_config_output_unref (output); +} + +static QmiMessagePdcActivateConfigInput * +activate_config_input_create ( + const gchar * str) +{ + QmiMessagePdcActivateConfigInput *input = NULL; + QmiConfigTypeAndId *type_and_id; + + type_and_id = parse_type_and_id (str); + + if (type_and_id) { + GError *error = NULL; + + input = qmi_message_pdc_activate_config_input_new (); + if (!qmi_message_pdc_activate_config_input_set_config_type (input, + type_and_id->config_type, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", error->message); + g_error_free (error); + qmi_message_pdc_activate_config_input_unref (input); + g_free (type_and_id); + input = NULL; + } + if (!qmi_message_pdc_activate_config_input_set_token (input, ctx->token++, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", error->message); + g_error_free (error); + qmi_message_pdc_activate_config_input_unref (input); + g_free (type_and_id); + input = NULL; + } + } + + return input; +} + +static void +activate_config_ready ( + QmiClientPdc * client, + GAsyncResult * res) +{ + GError *error = NULL; + QmiMessagePdcActivateConfigOutput *output; + + output = qmi_client_pdc_activate_config_finish (client, res, &error); + + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_pdc_activate_config_output_get_result (output, &error)) { + g_printerr ("error: couldn't activate config: %s\n", error->message); + g_error_free (error); + qmi_message_pdc_activate_config_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_pdc_activate_config_output_unref (output); +} + +static void +deactivate_config_ready ( + QmiClientPdc * client, + GAsyncResult * res) +{ + GError *error = NULL; + QmiMessagePdcDeactivateConfigOutput *output; + + output = qmi_client_pdc_deactivate_config_finish (client, res, &error); + + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_pdc_deactivate_config_output_get_result (output, &error)) { + g_printerr ("error: couldn't activate config: %s\n", error->message); + g_error_free (error); + qmi_message_pdc_deactivate_config_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_pdc_deactivate_config_output_unref (output); +} + +static void +set_selected_config_ready_indication_activation ( + QmiClientPdc * client, + QmiIndicationPdcSetSelectedConfigOutput * output) +{ + GError *error = NULL; + QmiMessagePdcActivateConfigInput *input; + guint16 error_code; + + if (!qmi_indication_pdc_set_selected_config_output_get_indication_result (output, + &error_code, + &error)) { + g_printerr ("error: couldn't set selected config: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } else if (error_code != 0) { + g_printerr ("error: couldn't set selected config: %s\n", + qmi_protocol_error_get_string ((QmiProtocolError) error_code)); + operation_shutdown (FALSE); + return; + } + + input = activate_config_input_create (activate_config_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_pdc_activate_config (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) activate_config_ready, NULL); + qmi_message_pdc_activate_config_input_unref (input); +} + +static void +set_selected_config_ready ( + QmiClientPdc * client, + GAsyncResult * res) +{ + GError *error = NULL; + QmiMessagePdcSetSelectedConfigOutput *output; + + output = qmi_client_pdc_set_selected_config_finish (client, res, &error); + + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + qmi_message_pdc_set_selected_config_output_unref (output); +} + +static QmiMessagePdcDeactivateConfigInput * +deactivate_config_input_create ( + const gchar * str) +{ + QmiMessagePdcDeactivateConfigInput *input = NULL; + QmiConfigTypeAndId *type_and_id; + + type_and_id = parse_type_and_id (str); + + if (type_and_id) { + GError *error = NULL; + + input = qmi_message_pdc_deactivate_config_input_new (); + if (!qmi_message_pdc_deactivate_config_input_set_config_type (input, + type_and_id->config_type, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", error->message); + g_error_free (error); + qmi_message_pdc_deactivate_config_input_unref (input); + g_free (type_and_id); + input = NULL; + } + if (!qmi_message_pdc_deactivate_config_input_set_token (input, ctx->token++, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", error->message); + g_error_free (error); + qmi_message_pdc_deactivate_config_input_unref (input); + g_free (type_and_id); + input = NULL; + } + } + + return input; +} + +static void +set_selected_config_ready_indication_deactivation ( + QmiClientPdc * client, + QmiIndicationPdcSetSelectedConfigOutput * output) +{ + GError *error = NULL; + QmiMessagePdcDeactivateConfigInput *input; + guint16 error_code; + + if (!qmi_indication_pdc_set_selected_config_output_get_indication_result (output, + &error_code, + &error)) { + g_printerr ("error: couldn't set selected config: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } else if (error_code != 0) { + g_printerr ("error: couldn't set selected config: %s\n", + qmi_protocol_error_get_string ((QmiProtocolError) error_code)); + operation_shutdown (FALSE); + return; + } + + input = deactivate_config_input_create (activate_config_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_pdc_deactivate_config (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) deactivate_config_ready, NULL); + qmi_message_pdc_deactivate_config_input_unref (input); +} + +static QmiMessagePdcSetSelectedConfigInput * +set_selected_config_input_create ( + const gchar * str) +{ + QmiMessagePdcSetSelectedConfigInput *input = NULL; + QmiConfigTypeAndId *type_and_id; + + type_and_id = parse_type_and_id (str); + + if (type_and_id) { + GError *error = NULL; + + input = qmi_message_pdc_set_selected_config_input_new (); + if (!qmi_message_pdc_set_selected_config_input_set_type_with_id (input, + type_and_id, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", error->message); + g_error_free (error); + g_free (type_and_id); + qmi_message_pdc_set_selected_config_input_unref (input); + input = NULL; + } + if (!qmi_message_pdc_set_selected_config_input_set_token (input, ctx->token++, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", error->message); + g_error_free (error); + g_free (type_and_id); + qmi_message_pdc_set_selected_config_input_unref (input); + input = NULL; + } + + } + + return input; +} + +/**************************************************************************************** + * Load config + ****************************************************************************************/ + +static LoadConfigFileData * +load_config_file_from_string ( + const gchar * str) +{ + GError *error = NULL; + GMappedFile *mapped_file; + LoadConfigFileData *data; + guchar *file_contents; + GChecksum *checksum; + gsize file_size; + gsize hash_size; + + if (!(mapped_file = g_mapped_file_new (str, FALSE, &error))) { + g_printerr ("error: couldn't map config file: '%s'\n", error->message); + g_error_free (error); + return NULL; + } + + if (!(file_contents = (guchar *) g_mapped_file_get_contents (mapped_file))) { + g_printerr ("error: couldn't get file content\n"); + g_mapped_file_unref (mapped_file); + return NULL; + } + file_size = g_mapped_file_get_length (mapped_file); + hash_size = g_checksum_type_get_length (G_CHECKSUM_SHA1); + g_info ("File opened: %lu, %lu", file_size, hash_size); + checksum = g_checksum_new (G_CHECKSUM_SHA1); + g_checksum_update (checksum, file_contents, file_size); + + data = g_slice_new (LoadConfigFileData); + data->mapped_file = mapped_file; + data->checksum = g_array_sized_new (FALSE, FALSE, sizeof (guint8), hash_size); + g_array_set_size (data->checksum, hash_size); + data->offset = 0; + g_checksum_get_digest (checksum, &g_array_index (data->checksum, guint8, 0), &hash_size); + g_info ("Checksum: %lu, %lu", file_size, hash_size); + + return data; +} + +static QmiMessagePdcLoadConfigInput * +load_config_input_create_chunk ( + LoadConfigFileData * config_file) +{ + QmiMessagePdcLoadConfigInput *input = NULL; + + if (config_file) { + GError *error = NULL; + GArray *chunk; + gsize full_size; + gsize chunk_size; + guchar *file_content; + + input = qmi_message_pdc_load_config_input_new (); + if (!qmi_message_pdc_load_config_input_set_token (input, ctx->token++, &error)) { + g_printerr ("error: couldn't set token: '%s'\n", error->message); + g_error_free (error); + qmi_message_pdc_load_config_input_unref (input); + input = NULL; + } + + chunk = g_array_new (FALSE, FALSE, 1); + full_size = g_mapped_file_get_length (config_file->mapped_file); + chunk_size = config_file->offset + LOAD_CONFIG_CHUNK_SIZE > full_size ? + full_size - config_file->offset : LOAD_CONFIG_CHUNK_SIZE; + file_content = (guchar *) g_mapped_file_get_contents (config_file->mapped_file); + g_array_append_vals (chunk, file_content + config_file->offset, chunk_size); + g_print ("Uploaded %lu of %lu\n", config_file->offset, full_size); + + if (!qmi_message_pdc_load_config_input_set_config_chunk (input, + QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, + config_file->checksum, + g_mapped_file_get_length + (config_file->mapped_file), chunk, + &error)) { + g_printerr ("error: couldn't set chunk: '%s'\n", error->message); + g_error_free (error); + qmi_message_pdc_load_config_input_unref (input); + input = NULL; + } else { + config_file->offset += chunk_size; + } + } + return input; +} + +static QmiMessagePdcLoadConfigInput * +load_config_input_create ( + const gchar * str) +{ + LoadConfigFileData *config_file; + QmiMessagePdcLoadConfigInput *input = NULL; + + if ((config_file = load_config_file_from_string (str))) { + if ((input = load_config_input_create_chunk (config_file))) { + ctx->load_config_file_data = config_file; + } + } + + return input; +} + +static void +load_config_ready ( + QmiClientPdc * client, + GAsyncResult * res) +{ + GError *error = NULL; + QmiMessagePdcLoadConfigOutput *output; + + output = qmi_client_pdc_load_config_finish (client, res, &error); + + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_pdc_load_config_output_get_result (output, &error)) { + g_printerr ("error: couldn't load config: %s\n", error->message); + g_error_free (error); + qmi_message_pdc_load_config_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_pdc_load_config_output_unref (output); +} + +static void +load_config_ready_indication ( + QmiClientPdc * client, + QmiIndicationPdcLoadConfigOutput * output) +{ + GError *error = NULL; + QmiMessagePdcLoadConfigInput *input; + gboolean frame_reset; + guint32 remaining_size; + guint16 error_code; + + if (!qmi_indication_pdc_load_config_output_get_indication_result (output, &error_code, &error)) { + g_printerr ("error: couldn't load config: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } else if (error_code != 0) { + g_printerr ("error: couldn't load config: %s\n", + qmi_protocol_error_get_string ((QmiProtocolError) error_code)); + operation_shutdown (FALSE); + return; + } + + if (qmi_indication_pdc_load_config_output_get_frame_reset (output, &frame_reset, &error)) { + if (frame_reset) { + g_printerr ("error: frame reset requested\n"); + operation_shutdown (FALSE); + return; + } + } else { + g_error_free (error); + error = NULL; + } + + if (!qmi_indication_pdc_load_config_output_get_remaining_size (output, &remaining_size, &error)) { + g_printerr ("error: couldn't load config: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + g_print ("Remaining %d\n", remaining_size); + if (remaining_size > 0) { + g_print ("Loading next\n"); + input = load_config_input_create_chunk (ctx->load_config_file_data); + if (!input) { + g_printerr ("Input is null\n"); + operation_shutdown (FALSE); + return; + } + qmi_client_pdc_load_config (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) load_config_ready, NULL); + qmi_message_pdc_load_config_input_unref (input); + } else { + operation_shutdown (TRUE); + } +} + +/**************************************************************************************** + * Common logic + ****************************************************************************************/ + +static gboolean +noop_cb ( + gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_pdc_run ( + QmiDevice * device, + QmiClientPdc * client, + GCancellable * cancellable) +{ + /* Initialize context */ + ctx = context_new (device, client, cancellable); + + /* Request to get all configs */ + if (list_configs_str) { + QmiMessagePdcListConfigsInput *input; + QmiMessagePdcGetSelectedConfigInput *get_selected_config_input; + + g_debug ("Listing configs asynchroniously..."); + ctx->list_configs_indication_id = g_signal_connect (client, + "list-configs", + G_CALLBACK + (list_configs_ready_indication), NULL); + ctx->get_selected_config_indication_id = + g_signal_connect (client, "get-selected-config", + G_CALLBACK (get_selected_config_ready_indication), NULL); + ctx->get_config_info_indication_id = + g_signal_connect (client, "get-config-info", + G_CALLBACK (get_config_info_ready_indication), NULL); + input = list_configs_input_create (list_configs_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + get_selected_config_input = get_selected_config_input_create (list_configs_str); + if (!get_selected_config_input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_pdc_list_configs (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) list_configs_ready, NULL); + qmi_message_pdc_list_configs_input_unref (input); + + qmi_client_pdc_get_selected_config (ctx->client, + get_selected_config_input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) get_selected_config_ready, NULL); + qmi_message_pdc_get_selected_config_input_unref (get_selected_config_input); + return; + } + + /* Request to delete config */ + if (delete_config_str) { + QmiMessagePdcDeleteConfigInput *input; + + g_debug ("Deleting config asynchroniously..."); + input = delete_config_input_create (delete_config_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_pdc_delete_config (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) delete_config_ready, NULL); + qmi_message_pdc_delete_config_input_unref (input); + return; + } + + /* Request to activate config */ + if (activate_config_str) { + QmiMessagePdcSetSelectedConfigInput *input; + + g_debug ("Activating config asynchroniously..."); + input = set_selected_config_input_create (activate_config_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + ctx->set_selected_config_indication_id = + g_signal_connect (client, + "set-selected-config", + G_CALLBACK (set_selected_config_ready_indication_activation), NULL); + ctx->activate_config_indication_id = g_signal_connect (client, + "activate-config", + G_CALLBACK + (activate_config_ready_indication), + NULL); + qmi_client_pdc_set_selected_config (ctx->client, input, 10, ctx->cancellable, + (GAsyncReadyCallback) set_selected_config_ready, NULL); + qmi_message_pdc_set_selected_config_input_unref (input); + return; + } + + /* Request to deactivate config */ + if (deactivate_config_str) { + QmiMessagePdcSetSelectedConfigInput *input; + + g_debug ("Deactivating config asynchroniously..."); + input = set_selected_config_input_create (activate_config_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + ctx->set_selected_config_indication_id = + g_signal_connect (client, + "set-selected-config", + G_CALLBACK (set_selected_config_ready_indication_deactivation), NULL); + ctx->deactivate_config_indication_id = g_signal_connect (client, + "deactivate-config", + G_CALLBACK + (deactivate_config_ready_indication), + NULL); + qmi_client_pdc_set_selected_config (ctx->client, input, 10, ctx->cancellable, + (GAsyncReadyCallback) set_selected_config_ready, NULL); + qmi_message_pdc_set_selected_config_input_unref (input); + return; + } + + if (load_config_str) { + QmiMessagePdcLoadConfigInput *input; + + g_debug ("Loading config asynchroniously..."); + input = load_config_input_create (load_config_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + ctx->load_config_indication_id = g_signal_connect (client, + "load-config", + G_CALLBACK + (load_config_ready_indication), NULL); + qmi_client_pdc_load_config (ctx->client, input, 10, ctx->cancellable, + (GAsyncReadyCallback) load_config_ready, NULL); + qmi_message_pdc_load_config_input_unref (input); + return; + } + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} diff --git a/src/qmicli/qmicli.c b/src/qmicli/qmicli.c index 705d9bc..2208609 100644 --- a/src/qmicli/qmicli.c +++ b/src/qmicli/qmicli.c @@ -326,6 +326,9 @@ allocate_client_ready (QmiDevice *dev, case QMI_SERVICE_PBM: qmicli_pbm_run (dev, QMI_CLIENT_PBM (client), cancellable); return; + case QMI_SERVICE_PDC: + qmicli_pdc_run (dev, QMI_CLIENT_PDC (client), cancellable); + return; case QMI_SERVICE_UIM: qmicli_uim_run (dev, QMI_CLIENT_UIM (client), cancellable); return; @@ -660,6 +663,12 @@ parse_actions (void) actions_enabled++; } + /* PDC options? */ + if (qmicli_pdc_options_enabled ()) { + service = QMI_SERVICE_PDC; + actions_enabled++; + } + /* UIM options? */ if (qmicli_uim_options_enabled ()) { service = QMI_SERVICE_UIM; @@ -720,6 +729,8 @@ int main (int argc, char **argv) g_option_context_add_group (context, qmicli_pbm_get_option_group ()); g_option_context_add_group (context, + qmicli_pdc_get_option_group ()); + g_option_context_add_group (context, qmicli_uim_get_option_group ()); g_option_context_add_group (context, qmicli_wms_get_option_group ()); diff --git a/src/qmicli/qmicli.h b/src/qmicli/qmicli.h index 6ba7ec9..2cc7d28 100644 --- a/src/qmicli/qmicli.h +++ b/src/qmicli/qmicli.h @@ -54,6 +54,13 @@ void qmicli_pbm_run (QmiDevice *device, QmiClientPbm *client, GCancellable *cancellable); +/* PBM group */ +GOptionGroup *qmicli_pdc_get_option_group (void); +gboolean qmicli_pdc_options_enabled (void); +void qmicli_pdc_run (QmiDevice *device, + QmiClientPdc *client, + GCancellable *cancellable); + /* UIM group */ GOptionGroup *qmicli_uim_get_option_group (void); gboolean qmicli_uim_options_enabled (void); |