biscuit/pkgs/qmi-nmea/qmicli-pdc.c

1476 lines
48 KiB
C
Raw Normal View History

/* -*- 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-2022 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"
#if defined HAVE_QMI_SERVICE_PDC
#define LIST_CONFIGS_TIMEOUT_SECS 2
#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;
gboolean skip_cid_release;
/* local data */
guint timeout_id;
GArray *config_list;
guint 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 device_removed_indication_id;
guint deactivate_config_indication_id;
guint refresh_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 monitor_refresh_flag;
static gboolean noop_flag;
#if defined HAVE_QMI_MESSAGE_PDC_LIST_CONFIGS && \
defined HAVE_QMI_INDICATION_PDC_LIST_CONFIGS && \
defined HAVE_QMI_MESSAGE_PDC_GET_SELECTED_CONFIG && \
defined HAVE_QMI_INDICATION_PDC_GET_SELECTED_CONFIG && \
defined HAVE_QMI_MESSAGE_PDC_GET_CONFIG_INFO && \
defined HAVE_QMI_INDICATION_PDC_GET_CONFIG_INFO
# define HAVE_QMI_ACTION_PDC_LIST_CONFIGS
#endif
#if defined HAVE_QMI_MESSAGE_PDC_ACTIVATE_CONFIG && \
defined HAVE_QMI_INDICATION_PDC_ACTIVATE_CONFIG && \
defined HAVE_QMI_MESSAGE_PDC_SET_SELECTED_CONFIG && \
defined HAVE_QMI_INDICATION_PDC_SET_SELECTED_CONFIG
# define HAVE_QMI_ACTION_PDC_ACTIVATE_CONFIG
#endif
#if defined HAVE_QMI_MESSAGE_PDC_DEACTIVATE_CONFIG && \
defined HAVE_QMI_INDICATION_PDC_DEACTIVATE_CONFIG
# define HAVE_QMI_ACTION_PDC_DEACTIVATE_CONFIG
#endif
#if defined HAVE_QMI_MESSAGE_PDC_LOAD_CONFIG && \
defined HAVE_QMI_INDICATION_PDC_LOAD_CONFIG
# define HAVE_QMI_ACTION_PDC_LOAD_CONFIG
#endif
#if defined HAVE_QMI_INDICATION_PDC_REFRESH && \
defined HAVE_QMI_MESSAGE_PDC_REGISTER
# define HAVE_QMI_ACTION_PDC_REFRESH
#endif
static GOptionEntry entries[] = {
#if defined HAVE_QMI_ACTION_PDC_LIST_CONFIGS
{
"pdc-list-configs", 0, 0, G_OPTION_ARG_STRING, &list_configs_str,
"List all configs",
"[(platform|software)]"
},
#endif
#if defined HAVE_QMI_MESSAGE_PDC_DELETE_CONFIG
{
"pdc-delete-config", 0, 0, G_OPTION_ARG_STRING, &delete_config_str,
"Delete config",
"[(platform|software),ConfigId]"
},
#endif
#if defined HAVE_QMI_ACTION_PDC_ACTIVATE_CONFIG
{
"pdc-activate-config", 0, 0, G_OPTION_ARG_STRING, &activate_config_str,
"Activate config",
"[(platform|software),ConfigId]"
},
#endif
#if defined HAVE_QMI_ACTION_PDC_DEACTIVATE_CONFIG
{
"pdc-deactivate-config", 0, 0, G_OPTION_ARG_STRING, &deactivate_config_str,
"Deactivate config",
"[(platform|software),ConfigId]"
},
#endif
#if defined HAVE_QMI_ACTION_PDC_LOAD_CONFIG
{
"pdc-load-config", 0, 0, G_OPTION_ARG_STRING, &load_config_str,
"Load config to device",
"[Path to config]"
},
#endif
#if defined HAVE_QMI_ACTION_PDC_REFRESH
{
"pdc-monitor-refresh", 0, 0, G_OPTION_ARG_NONE, &monitor_refresh_flag,
"Watch for refresh indications",
NULL
},
#endif
{
"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, 0, 0, 0, NULL, 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 +
monitor_refresh_flag +
noop_flag);
if (n_actions > 1) {
g_printerr ("error: too many PDC actions requested\n");
exit (EXIT_FAILURE);
}
/* Actions that require receiving QMI indication messages must specify that
* indications are expected. */
if (list_configs_str || activate_config_str || deactivate_config_str || load_config_str || monitor_refresh_flag)
qmicli_expect_indications ();
checked = TRUE;
return !!n_actions;
}
static Context *
context_new (QmiDevice *device,
QmiClientPdc *client,
GCancellable *cancellable)
{
Context *context;
context = g_slice_new0 (Context);
context->device = g_object_ref (device);
context->client = g_object_ref (client);
context->cancellable = g_object_ref (cancellable);
return context;
}
static void
context_free (Context *context)
{
guint i;
if (!context)
return;
if (context->config_list) {
for (i = 0; i < context->config_list->len; i++) {
ConfigInfo *current_config;
current_config = &g_array_index (context->config_list, ConfigInfo, i);
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->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->device_removed_indication_id)
g_signal_handler_disconnect (context->device, context->device_removed_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->client);
g_object_unref (context->device);
g_slice_free (Context, context);
}
static void
operation_shutdown (gboolean operation_status)
{
/* Cleanup context and finish async operation */
qmicli_async_operation_done (operation_status, ctx->skip_cid_release);
context_free (ctx);
ctx = NULL;
}
/******************************************************************************/
/* Common */
#if defined HAVE_QMI_ACTION_PDC_ACTIVATE_CONFIG || \
defined HAVE_QMI_ACTION_PDC_DEACTIVATE_CONFIG || \
defined HAVE_QMI_MESSAGE_PDC_DELETE_CONFIG
static gboolean
parse_type_and_id (const gchar *str,
QmiPdcConfigurationType *config_type,
GArray **id)
{
guint num_parts;
g_auto(GStrv) substrings = NULL;
substrings = g_strsplit (str, ",", -1);
num_parts = g_strv_length (substrings);
if (num_parts != 2) {
g_printerr ("Expected 2 parameters, but found %u\n", num_parts);
return FALSE;
}
if (!qmicli_read_pdc_configuration_type_from_string (substrings[0], config_type)) {
g_printerr ("Incorrect id specified: %s\n", substrings[0]);
return FALSE;
}
if (!qmicli_read_binary_array_from_string (substrings[1], id)) {
g_printerr ("Incorrect config type specified: %s\n", substrings[1]);
return FALSE;
}
return TRUE;
}
#endif /* HAVE_QMI_ACTION_PDC_ACTIVATE_CONFIG
* HAVE_QMI_ACTION_PDC_DEACTIVATE_CONFIG
* HAVE_QMI_MESSAGE_PDC_DELETE_CONFIG */
/******************************************************************************/
/* List configs and get selected config */
#if defined HAVE_QMI_ACTION_PDC_LIST_CONFIGS
static const char *
status_string (GArray *id)
{
if (!id)
return "Unknown";
if (ctx->active_config_id &&
id->len == ctx->active_config_id->len &&
memcmp (id->data, ctx->active_config_id->data, id->len) == 0)
return "Active";
if (ctx->pending_config_id &&
id->len == ctx->pending_config_id->len &&
memcmp (id->data, ctx->pending_config_id->data, id->len) == 0)
return "Pending";
return "Inactive";
}
static void
print_configs (GArray *configs)
{
guint i;
g_printf ("Total configurations: %u\n", ctx->config_list->len);
for (i = 0; i < ctx->config_list->len; i++) {
ConfigInfo *current_config;
char *id_str;
current_config = &g_array_index (ctx->config_list, ConfigInfo, i);
g_printf ("Configuration %u:\n", i + 1);
g_printf ("\tDescription: %s\n", current_config->description);
g_printf ("\tType: %s\n", qmi_pdc_configuration_type_get_string (current_config->config_type));
g_printf ("\tSize: %u\n", current_config->total_size);
g_printf ("\tStatus: %s\n", status_string (current_config->id));
g_printf ("\tVersion: 0x%X\n", current_config->version);
id_str = qmicli_get_raw_data_printable (current_config->id, 80, "");
g_printf ("\tID: %s\n", id_str ? id_str : "none");
g_free (id_str);
}
}
static void
check_list_config_completed (void)
{
if (ctx->configs_loaded == ctx->config_list->len &&
ctx->ids_loaded) {
print_configs (ctx->config_list);
operation_shutdown (TRUE);
}
}
static void
get_config_info_ready (QmiClientPdc *client,
GAsyncResult *res)
{
g_autoptr(GError) error = NULL;
g_autoptr(QmiMessagePdcGetConfigInfoOutput) output = NULL;
output = qmi_client_pdc_get_config_info_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
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);
operation_shutdown (FALSE);
return;
}
}
static void
get_config_info_ready_indication (QmiClientPdc *client,
QmiIndicationPdcGetConfigInfoOutput *output)
{
g_autoptr(GError) error = NULL;
ConfigInfo *current_config = NULL;
guint32 token;
const gchar *description;
guint i;
guint16 error_code = 0;
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);
operation_shutdown (FALSE);
return;
}
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 token: %s\n", error->message);
operation_shutdown (FALSE);
return;
}
/* Look for the current config in the list */
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;
}
/* Store total size, version and description of the current config */
if (!qmi_indication_pdc_get_config_info_output_get_total_size (output,
&current_config->total_size,
&error) ||
!qmi_indication_pdc_get_config_info_output_get_version (output,
&current_config->version,
&error) ||
!qmi_indication_pdc_get_config_info_output_get_description (output, &description, &error)) {
g_printerr ("error: couldn't get config info details: %s\n", error->message);
operation_shutdown (FALSE);
return;
}
current_config->description = g_strdup (description);
ctx->configs_loaded++;
check_list_config_completed ();
}
static gboolean
list_configs_timeout (void)
{
/* No indication yet, cancelling */
if (!ctx->config_list) {
g_printf ("Total configurations: 0\n");
operation_shutdown (TRUE);
}
return FALSE;
}
static void
list_configs_ready_indication (QmiClientPdc *client,
QmiIndicationPdcListConfigsOutput *output)
{
g_autoptr(GError) error = NULL;
GArray *configs = NULL;
guint i;
guint16 error_code = 0;
/* Remove timeout as soon as we get the indication */
if (ctx->timeout_id) {
g_source_remove (ctx->timeout_id);
ctx->timeout_id = 0;
}
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);
operation_shutdown (FALSE);
return;
}
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);
operation_shutdown (FALSE);
return;
}
/* Preallocate config list and request details for each */
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;
guint32 token;
g_autoptr(QmiMessagePdcGetConfigInfoInput) input = NULL;
token = ctx->token++;
element = &g_array_index (configs, QmiIndicationPdcListConfigsOutputConfigsElement, i);
current_info = &g_array_index (ctx->config_list, ConfigInfo, i);
current_info->token = token;
current_info->id = g_array_ref (element->id);
current_info->config_type = element->config_type;
input = qmi_message_pdc_get_config_info_input_new ();
/* Add type with id */
if (!qmi_message_pdc_get_config_info_input_set_type_with_id_v2 (input, element->config_type, current_info->id, &error)) {
g_printerr ("error: couldn't set type with id: %s\n", error->message);
operation_shutdown (FALSE);
return;
}
/* Add token */
if (!qmi_message_pdc_get_config_info_input_set_token (input, token, &error)) {
g_printerr ("error: couldn't set token: %s\n", error->message);
operation_shutdown (FALSE);
return;
}
qmi_client_pdc_get_config_info (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback) get_config_info_ready, NULL);
}
check_list_config_completed ();
}
static void
list_configs_ready (QmiClientPdc *client,
GAsyncResult *res)
{
g_autoptr(GError) error = NULL;
g_autoptr(QmiMessagePdcListConfigsOutput) output = NULL;
output = qmi_client_pdc_list_configs_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
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);
operation_shutdown (FALSE);
return;
}
}
static QmiMessagePdcListConfigsInput *
list_configs_input_create (const gchar *str)
{
g_autoptr(GError) error = NULL;
g_autoptr(QmiMessagePdcListConfigsInput) input = NULL;
QmiPdcConfigurationType config_type;
if (!qmicli_read_pdc_configuration_type_from_string (str, &config_type))
return NULL;
input = qmi_message_pdc_list_configs_input_new ();
if (!qmi_message_pdc_list_configs_input_set_config_type (input, config_type, &error) ||
!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);
return NULL;
}
return g_steal_pointer (&input);
}
static void
get_selected_config_ready_indication (QmiClientPdc *client,
QmiIndicationPdcGetSelectedConfigOutput *output)
{
g_autoptr(GError) error = NULL;
GArray *pending_id = NULL;
GArray *active_id = NULL;
guint16 error_code = 0;
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);
operation_shutdown (FALSE);
return;
}
if (error_code != 0 && error_code != QMI_PROTOCOL_ERROR_NOT_PROVISIONED) { /* No configs active */
g_printerr ("error: couldn't get selected config: %s\n",
qmi_protocol_error_get_string ((QmiProtocolError) error_code));
operation_shutdown (FALSE);
return;
}
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;
check_list_config_completed ();
}
static void
get_selected_config_ready (QmiClientPdc *client,
GAsyncResult *res)
{
g_autoptr(GError) error = NULL;
g_autoptr(QmiMessagePdcGetSelectedConfigOutput) output = NULL;
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);
operation_shutdown (FALSE);
return;
}
}
static QmiMessagePdcGetSelectedConfigInput *
get_selected_config_input_create (const gchar *str)
{
g_autoptr(GError) error = NULL;
g_autoptr(QmiMessagePdcGetSelectedConfigInput) input = NULL;
QmiPdcConfigurationType config_type;
if (!qmicli_read_pdc_configuration_type_from_string (str, &config_type))
return NULL;
input = qmi_message_pdc_get_selected_config_input_new ();
if (!qmi_message_pdc_get_selected_config_input_set_config_type (input, config_type, &error) ||
!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);
return NULL;
}
return g_steal_pointer (&input);
}
static void
run_list_configs (void)
{
g_autoptr(QmiMessagePdcListConfigsInput) input = NULL;
g_autoptr(QmiMessagePdcGetSelectedConfigInput) get_selected_config_input = NULL;
g_debug ("Listing configs asynchronously...");
/* Results are reported via indications */
ctx->list_configs_indication_id =
g_signal_connect (ctx->client,
"list-configs",
G_CALLBACK
(list_configs_ready_indication), NULL);
ctx->get_selected_config_indication_id =
g_signal_connect (ctx->client, "get-selected-config",
G_CALLBACK (get_selected_config_ready_indication), NULL);
ctx->get_config_info_indication_id =
g_signal_connect (ctx->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;
}
/* We need a timeout, because there will be no indications if no configs
* are loaded */
ctx->timeout_id = g_timeout_add_seconds (LIST_CONFIGS_TIMEOUT_SECS,
(GSourceFunc) list_configs_timeout,
NULL);
qmi_client_pdc_list_configs (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback) list_configs_ready,
NULL);
qmi_client_pdc_get_selected_config (ctx->client,
get_selected_config_input,
10,
ctx->cancellable,
(GAsyncReadyCallback) get_selected_config_ready,
NULL);
}
#endif /* HAVE_QMI_ACTION_PDC_LIST_CONFIGS */
/******************************************************************************/
/* Activate config */
#if defined HAVE_QMI_ACTION_PDC_ACTIVATE_CONFIG
static void
device_removed_indication (QmiDevice *device)
{
g_print ("[%s] Successfully requested config activation\n",
qmi_device_get_path_display (ctx->device));
/* Device gone, don't attempt to release CIDs */
ctx->skip_cid_release = TRUE;
/* If device gets removed during an activate config operation,
* it means the operation is successful */
operation_shutdown (TRUE);
}
static void
activate_config_ready_indication (QmiClientPdc *client,
QmiIndicationPdcActivateConfigOutput *output)
{
g_autoptr(GError) error = NULL;
guint16 error_code = 0;
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);
operation_shutdown (FALSE);
return;
}
if (error_code != 0) {
g_printerr ("error: couldn't activate config: %s\n", qmi_protocol_error_get_string ((QmiProtocolError) error_code));
operation_shutdown (FALSE);
return;
}
/* NOTE: config activation is expected to reboot the device, so we may detect the
* actual reboot before receiving this indication */
g_print ("[%s] Successfully requested config activation\n",
qmi_device_get_path_display (ctx->device));
operation_shutdown (TRUE);
}
static void
activate_config_ready (QmiClientPdc *client,
GAsyncResult *res)
{
g_autoptr(GError) error = NULL;
g_autoptr(QmiMessagePdcActivateConfigOutput) output = NULL;
output = qmi_client_pdc_activate_config_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
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);
operation_shutdown (FALSE);
return;
}
}
static QmiMessagePdcActivateConfigInput *
activate_config_input_create (const gchar *str)
{
g_autoptr(QmiMessagePdcActivateConfigInput) input = NULL;
g_autoptr(GError) error = NULL;
QmiPdcConfigurationType config_type;
g_autoptr(GArray) id = NULL;
/* Note: id not needed here really */
if (!parse_type_and_id (str, &config_type, &id))
return NULL;
input = qmi_message_pdc_activate_config_input_new ();
if (!qmi_message_pdc_activate_config_input_set_config_type (input, config_type, &error) ||
!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);
return NULL;
}
return g_steal_pointer (&input);
}
static void
set_selected_config_ready_indication (QmiClientPdc *client,
QmiIndicationPdcSetSelectedConfigOutput *output)
{
g_autoptr(GError) error = NULL;
g_autoptr(QmiMessagePdcActivateConfigInput) input = NULL;
guint16 error_code = 0;
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);
operation_shutdown (FALSE);
return;
}
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;
}
ctx->activate_config_indication_id =
g_signal_connect (ctx->client,
"activate-config",
G_CALLBACK (activate_config_ready_indication),
NULL);
ctx->device_removed_indication_id =
g_signal_connect (ctx->device,
QMI_DEVICE_SIGNAL_REMOVED,
G_CALLBACK (device_removed_indication),
NULL);
qmi_client_pdc_activate_config (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback) activate_config_ready, NULL);
}
static void
set_selected_config_ready (QmiClientPdc *client,
GAsyncResult *res)
{
g_autoptr(GError) error = NULL;
g_autoptr(QmiMessagePdcSetSelectedConfigOutput) output = NULL;
output = qmi_client_pdc_set_selected_config_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
operation_shutdown (FALSE);
return;
}
}
static QmiMessagePdcSetSelectedConfigInput *
set_selected_config_input_create (const gchar *str)
{
g_autoptr(QmiMessagePdcSetSelectedConfigInput) input = NULL;
g_autoptr(GError) error = NULL;
QmiPdcConfigurationType config_type;
g_autoptr(GArray) id = NULL;
if (!parse_type_and_id (str, &config_type, &id))
return NULL;
input = qmi_message_pdc_set_selected_config_input_new ();
if (!qmi_message_pdc_set_selected_config_input_set_type_with_id_v2 (input, config_type, id, &error) ||
!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);
return NULL;
}
return g_steal_pointer (&input);
}
static void
run_activate_config (void)
{
g_autoptr(QmiMessagePdcSetSelectedConfigInput) input = NULL;
g_debug ("Activating config asynchronously...");
input = set_selected_config_input_create (activate_config_str);
if (!input) {
operation_shutdown (FALSE);
return;
}
/* Results are reported via indications */
ctx->set_selected_config_indication_id =
g_signal_connect (ctx->client,
"set-selected-config",
G_CALLBACK (set_selected_config_ready_indication), NULL);
qmi_client_pdc_set_selected_config (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback) set_selected_config_ready,
NULL);
}
#endif /* HAVE_QMI_ACTION_PDC_ACTIVATE_CONFIG */
/******************************************************************************/
/* Deactivate config */
#if defined HAVE_QMI_ACTION_PDC_DEACTIVATE_CONFIG
static void
deactivate_config_ready_indication (QmiClientPdc *client,
QmiIndicationPdcDeactivateConfigOutput *output)
{
g_autoptr(GError) error = NULL;
guint16 error_code = 0;
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);
operation_shutdown (FALSE);
return;
}
if (error_code != 0) {
g_printerr ("error: couldn't deactivate config: %s\n", qmi_protocol_error_get_string ((QmiProtocolError) error_code));
operation_shutdown (FALSE);
return;
}
g_print ("[%s] Successfully requested config deactivation\n",
qmi_device_get_path_display (ctx->device));
operation_shutdown (TRUE);
}
static void
deactivate_config_ready (QmiClientPdc *client,
GAsyncResult *res)
{
g_autoptr(GError) error = NULL;
g_autoptr(QmiMessagePdcDeactivateConfigOutput) output = NULL;
output = qmi_client_pdc_deactivate_config_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_pdc_deactivate_config_output_get_result (output, &error)) {
g_printerr ("error: couldn't deactivate config: %s\n", error->message);
operation_shutdown (FALSE);
return;
}
}
static QmiMessagePdcDeactivateConfigInput *
deactivate_config_input_create (const gchar *str)
{
g_autoptr(QmiMessagePdcDeactivateConfigInput) input = NULL;
g_autoptr(GError) error = NULL;
QmiPdcConfigurationType config_type;
g_autoptr(GArray) id = NULL;
/* Note: id not needed here really */
if (!parse_type_and_id (str, &config_type, &id))
return NULL;
input = qmi_message_pdc_deactivate_config_input_new ();
if (!qmi_message_pdc_deactivate_config_input_set_config_type (input, config_type, &error) ||
!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);
return NULL;
}
return g_steal_pointer (&input);
}
static void
run_deactivate_config (void)
{
g_autoptr(QmiMessagePdcDeactivateConfigInput) input = NULL;
g_debug ("Deactivating config asynchronously...");
input = deactivate_config_input_create (deactivate_config_str);
if (!input) {
operation_shutdown (FALSE);
return;
}
/* Results are reported via indications */
ctx->deactivate_config_indication_id =
g_signal_connect (ctx->client,
"deactivate-config",
G_CALLBACK
(deactivate_config_ready_indication),
NULL);
qmi_client_pdc_deactivate_config (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback) deactivate_config_ready, NULL);
}
#endif /* HAVE_QMI_ACTION_PDC_DEACTIVATE_CONFIG */
/******************************************************************************/
/* Delete config */
#if defined HAVE_QMI_MESSAGE_PDC_DELETE_CONFIG
static void
delete_config_ready (QmiClientPdc *client,
GAsyncResult *res)
{
g_autoptr(GError) error = NULL;
g_autoptr(QmiMessagePdcDeleteConfigOutput) output = NULL;
output = qmi_client_pdc_delete_config_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
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);
operation_shutdown (FALSE);
return;
}
g_print ("[%s] Successfully deleted config\n",
qmi_device_get_path_display (ctx->device));
operation_shutdown (TRUE);
}
static QmiMessagePdcDeleteConfigInput *
delete_config_input_create (const gchar *str)
{
g_autoptr(QmiMessagePdcDeleteConfigInput) input = NULL;
g_autoptr(GError) error = NULL;
QmiPdcConfigurationType config_type;
g_autoptr(GArray) id = NULL;
if (!parse_type_and_id (str, &config_type, &id))
return NULL;
input = qmi_message_pdc_delete_config_input_new ();
if (!qmi_message_pdc_delete_config_input_set_config_type (input, config_type, &error) ||
!qmi_message_pdc_delete_config_input_set_token (input, ctx->token++, &error) ||
!qmi_message_pdc_delete_config_input_set_id (input, id, &error)) {
g_printerr ("error: couldn't create input data bundle: '%s'\n", error->message);
return NULL;
}
return g_steal_pointer (&input);
}
static void
run_delete_config (void)
{
g_autoptr(QmiMessagePdcDeleteConfigInput) input = NULL;
g_debug ("Deleting config asynchronously...");
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);
}
#endif /* HAVE_QMI_MESSAGE_PDC_DELETE_CONFIG */
/******************************************************************************/
/* Load config */
#if defined HAVE_QMI_ACTION_PDC_LOAD_CONFIG
static LoadConfigFileData *
load_config_file_from_string (const gchar *str)
{
g_autoptr(GError) error = NULL;
g_autoptr(GMappedFile) mapped_file = NULL;
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);
return NULL;
}
if (!(file_contents = (guchar *) g_mapped_file_get_contents (mapped_file))) {
g_printerr ("error: couldn't get file content\n");
return NULL;
}
/* Get checksum */
file_size = g_mapped_file_get_length (mapped_file);
hash_size = g_checksum_type_get_length (G_CHECKSUM_SHA1);
checksum = g_checksum_new (G_CHECKSUM_SHA1);
g_checksum_update (checksum, file_contents, file_size);
g_free (file_contents);
data = g_slice_new (LoadConfigFileData);
data->mapped_file = g_mapped_file_ref (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_checksum_free (checksum);
return data;
}
static QmiMessagePdcLoadConfigInput *
load_config_input_create_chunk (LoadConfigFileData *config_file)
{
g_autoptr(QmiMessagePdcLoadConfigInput) input = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GArray) chunk = NULL;
gsize full_size;
gsize chunk_size;
guint8 *file_content;
if (!config_file)
return NULL;
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);
return NULL;
}
chunk = g_array_new (FALSE, FALSE, sizeof (guint8));
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 = (guint8 *) g_mapped_file_get_contents (config_file->mapped_file);
g_array_append_vals (chunk, &file_content[config_file->offset], chunk_size);
g_print ("Uploaded %" G_GSIZE_FORMAT " of %" G_GSIZE_FORMAT "\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,
full_size,
chunk,
&error)) {
g_printerr ("error: couldn't set chunk: '%s'\n", error->message);
return NULL;
}
config_file->offset += chunk_size;
return g_steal_pointer (&input);
}
static void load_config_ready (QmiClientPdc *client,
GAsyncResult *res);
static void
load_config_ready_indication (QmiClientPdc *client,
QmiIndicationPdcLoadConfigOutput *output)
{
g_autoptr(GError) error = NULL;
g_autoptr(QmiMessagePdcLoadConfigInput) input = NULL;
gboolean frame_reset;
guint32 remaining_size;
guint16 error_code = 0;
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);
operation_shutdown (FALSE);
return;
}
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, NULL) && frame_reset) {
g_printerr ("error: frame reset requested\n");
operation_shutdown (FALSE);
return;
}
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);
operation_shutdown (FALSE);
return;
}
if (remaining_size == 0) {
g_print ("Finished loading\n");
operation_shutdown (TRUE);
return;
}
g_print ("Loading next chunk (%u bytes remaining)\n", remaining_size);
input = load_config_input_create_chunk (ctx->load_config_file_data);
if (!input) {
g_printerr ("error: couldn't create next chunk\n");
operation_shutdown (FALSE);
return;
}
qmi_client_pdc_load_config (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback) load_config_ready, NULL);
}
static void
load_config_ready (QmiClientPdc *client,
GAsyncResult *res)
{
g_autoptr(GError) error = NULL;
g_autoptr(QmiMessagePdcLoadConfigOutput) output = NULL;
output = qmi_client_pdc_load_config_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
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);
operation_shutdown (FALSE);
return;
}
}
static QmiMessagePdcLoadConfigInput *
load_config_input_create (const gchar *str)
{
LoadConfigFileData *config_file;
QmiMessagePdcLoadConfigInput *input;
config_file = load_config_file_from_string (str);
if (!config_file)
return NULL;
input = load_config_input_create_chunk (config_file);
if (!input)
return NULL;
ctx->load_config_file_data = config_file;
return input;
}
static void
run_load_config (void)
{
g_autoptr(QmiMessagePdcLoadConfigInput) input = NULL;
g_debug ("Loading config asynchronously...");
input = load_config_input_create (load_config_str);
if (!input) {
operation_shutdown (FALSE);
return;
}
/* Results are reported via indications */
ctx->load_config_indication_id =
g_signal_connect (ctx->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);
}
#endif /* HAVE_QMI_ACTION_PDC_LOAD_CONFIG */
/******************************************************************************/
/* Refresh */
#if defined HAVE_QMI_ACTION_PDC_REFRESH
#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
static void
monitoring_cancelled (GCancellable *cancellable)
{
operation_shutdown (TRUE);
}
static void
refresh_received (QmiClientPdc *client,
QmiIndicationPdcRefreshOutput *output)
{
g_autoptr(GError) error = NULL;
QmiPdcRefreshEventType type;
guint subscription_id;
guint slot_id;
g_print ("[%s] Received refresh indication:\n",
qmi_device_get_path_display (ctx->device));
if (!qmi_indication_pdc_refresh_output_get_refresh_event (output, &type, &error)) {
g_printerr ("error: refresh event has no type: %s\n", error->message);
operation_shutdown (FALSE);
return;
}
g_print (" Received event type %s",
VALIDATE_UNKNOWN (qmi_pdc_refresh_event_type_get_string (type)));
if (qmi_indication_pdc_refresh_output_get_subscription_id (output, &subscription_id, NULL))
g_print (", subscription ID: %u", subscription_id);
if (qmi_indication_pdc_refresh_output_get_slot_id (output, &slot_id, NULL))
g_print (", slot ID: %u", slot_id);
g_print ("\n");
}
static void
register_refresh_ready (QmiClientPdc *client,
GAsyncResult *res)
{
g_autoptr(QmiMessagePdcRegisterOutput) output = NULL;
g_autoptr(GError) error = NULL;
output = qmi_client_pdc_register_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_pdc_register_output_get_result (output, &error)) {
g_printerr ("error: could not register for refresh events: %s\n", error->message);
operation_shutdown (FALSE);
return;
}
g_debug ("Registered for refresh events...");
ctx->refresh_indication_id =
g_signal_connect (ctx->client,
"refresh",
G_CALLBACK (refresh_received),
NULL);
/* User can use Ctrl+C to cancel the monitoring at any time */
g_cancellable_connect (ctx->cancellable,
G_CALLBACK (monitoring_cancelled),
NULL,
NULL);
}
static void
register_refresh_events (void)
{
g_autoptr(QmiMessagePdcRegisterInput) re_input = NULL;
re_input = qmi_message_pdc_register_input_new ();
qmi_message_pdc_register_input_set_enable_reporting (re_input, TRUE, NULL);
qmi_message_pdc_register_input_set_enable_refresh (re_input, TRUE, NULL);
qmi_client_pdc_register (
ctx->client,
re_input,
10,
ctx->cancellable,
(GAsyncReadyCallback) register_refresh_ready,
NULL);
}
#endif /* HAVE_QMI_ACTION_PDC_REFRESH */
/******************************************************************************/
/* Common */
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);
#if defined HAVE_QMI_ACTION_PDC_LIST_CONFIGS
if (list_configs_str) {
run_list_configs ();
return;
}
#endif
#if defined HAVE_QMI_ACTION_PDC_ACTIVATE_CONFIG
if (activate_config_str) {
run_activate_config ();
return;
}
#endif
#if defined HAVE_QMI_ACTION_PDC_DEACTIVATE_CONFIG
if (deactivate_config_str) {
run_deactivate_config ();
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_PDC_DELETE_CONFIG
if (delete_config_str) {
run_delete_config ();
return;
}
#endif
#if defined HAVE_QMI_ACTION_PDC_LOAD_CONFIG
if (load_config_str) {
run_load_config ();
return;
}
#endif
#if defined HAVE_QMI_ACTION_PDC_REFRESH
if (monitor_refresh_flag) {
register_refresh_events ();
return;
}
#endif
/* Just client allocate/release? */
if (noop_flag) {
g_idle_add (noop_cb, NULL);
return;
}
g_warn_if_reached ();
}
#endif /* HAVE_QMI_SERVICE_PDC */