/* -*- 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 . * * Copyright (C) 2013-2015 Aleksander Morgado */ #include "config.h" #include #include #include #include #include #include #include #include #include "qmicli.h" #include "qmicli-helpers.h" #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; /* 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 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_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) { int 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->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 */ context_free (ctx); ctx = NULL; qmicli_async_operation_done (operation_status); } /******************************************************************************/ /* 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); 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) { 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 = 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); g_error_free (error); 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); g_error_free (error); 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, ¤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 details: %s\n", error->message); g_error_free (error); 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 (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 = 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); g_error_free (error); 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); g_error_free (error); 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; 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); 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 (); g_print ("Fetching config type: %s\n", qmi_pdc_configuration_type_get_string (current_info->config_type)); /* Add type with id */ type_with_id.config_type = element->config_type; type_with_id.id = current_info->id; 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; } /* 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); 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); qmi_message_pdc_get_config_info_input_unref (input); } g_print ("Loaded configs: %u\n", ctx->config_list->len); check_list_config_completed (); } 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 = 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); g_error_free (error); 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 activate_config_ready_indication (QmiClientPdc *client, QmiIndicationPdcActivateConfigOutput *output) { 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); g_error_free (error); 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; } operation_shutdown (TRUE); } static void deactivate_config_ready_indication (QmiClientPdc *client, QmiIndicationPdcDeactivateConfigOutput *output) { 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); g_error_free (error); 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; } operation_shutdown (TRUE); } static QmiMessagePdcListConfigsInput * list_configs_input_create (const gchar *str) { QmiMessagePdcListConfigsInput *input = NULL; QmiPdcConfigurationType config_type; GError *error = NULL; 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); g_error_free (error); qmi_message_pdc_list_configs_input_unref (input); return NULL; } return input; } /******************************************************************************/ /* Activate and deactivate configs */ static QmiConfigTypeAndId * parse_type_and_id (const gchar *str) { GArray *id = NULL; guint num_parts; gchar **substrings; QmiPdcConfigurationType config_type; QmiConfigTypeAndId *result = 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); 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); result->config_type = config_type; result->id = id; return result; } static QmiMessagePdcGetSelectedConfigInput * get_selected_config_input_create (const gchar *str) { QmiMessagePdcGetSelectedConfigInput *input = NULL; QmiPdcConfigurationType config_type; GError *error = NULL; 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); g_error_free (error); qmi_message_pdc_get_selected_config_input_unref (input); return NULL; } return input; } static QmiMessagePdcDeleteConfigInput * delete_config_input_create (const gchar *str) { QmiMessagePdcDeleteConfigInput *input = NULL; QmiConfigTypeAndId *type_and_id; GError *error = NULL; type_and_id = parse_type_and_id (str); if (!type_and_id) return 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); qmi_message_pdc_delete_config_input_unref (input); input = NULL; } g_array_unref (type_and_id->id); 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; } qmi_message_pdc_delete_config_output_unref (output); operation_shutdown (TRUE); } static QmiMessagePdcActivateConfigInput * activate_config_input_create (const gchar *str) { QmiMessagePdcActivateConfigInput *input = NULL; QmiConfigTypeAndId *type_and_id; GError *error = NULL; type_and_id = parse_type_and_id (str); if (!type_and_id) return 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) || !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); input = NULL; } g_array_unref (type_and_id->id); g_slice_free (QmiConfigTypeAndId, type_and_id); 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 deactivate 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 = 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); g_error_free (error); 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; } 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; GError *error = NULL; type_and_id = parse_type_and_id (str); if (!type_and_id) return 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) || !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); input = NULL; } g_array_unref (type_and_id->id); g_slice_free (QmiConfigTypeAndId, type_and_id); return input; } static void set_selected_config_ready_indication_deactivation (QmiClientPdc *client, QmiIndicationPdcSetSelectedConfigOutput *output) { GError *error = NULL; QmiMessagePdcDeactivateConfigInput *input; 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); g_error_free (error); 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 = 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; GError *error = NULL; type_and_id = parse_type_and_id (str); if (!type_and_id) return 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) || !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); qmi_message_pdc_set_selected_config_input_unref (input); input = NULL; } g_array_unref (type_and_id->id); g_slice_free (QmiConfigTypeAndId, type_and_id); 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; } /* 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); 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); return data; } static QmiMessagePdcLoadConfigInput * load_config_input_create_chunk (LoadConfigFileData *config_file) { QmiMessagePdcLoadConfigInput *input; GError *error = NULL; GArray *chunk; 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); g_error_free (error); qmi_message_pdc_load_config_input_unref (input); 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 %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, full_size, chunk, &error)) { g_printerr ("error: couldn't set chunk: '%s'\n", error->message); g_error_free (error); g_array_unref (chunk); qmi_message_pdc_load_config_input_unref (input); return NULL; } config_file->offset += chunk_size; g_array_unref (chunk); return input; } static QmiMessagePdcLoadConfigInput * load_config_input_create (const gchar *str) { LoadConfigFileData *config_file; QmiMessagePdcLoadConfigInput *input = NULL; 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 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 = 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); g_error_free (error); 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); g_error_free (error); 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); qmi_message_pdc_load_config_input_unref (input); } /******************************************************************************/ /* 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); /* Request to get all configs */ if (list_configs_str) { QmiMessagePdcListConfigsInput *input; QmiMessagePdcGetSelectedConfigInput *get_selected_config_input; g_debug ("Listing configs asynchronously..."); /* Results are reported via indications */ 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; } /* 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_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 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); qmi_message_pdc_delete_config_input_unref (input); return; } /* Request to activate config */ if (activate_config_str) { QmiMessagePdcSetSelectedConfigInput *input; 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 (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 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 (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 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 (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 (); }