diff --git a/configuration.nix b/configuration.nix index 27f6429..6131dd9 100644 --- a/configuration.nix +++ b/configuration.nix @@ -39,6 +39,7 @@ in { modemmanager-small = mm; satellite = prev.satellite.override { modemmanager = mm; }; maps = final.callPackage ./pkgs/maps {}; + qmi-nmea = final.callPackage ./pkgs/qmi-nmea {}; }) ]; diff --git a/default.nix b/default.nix index 06399a2..9800eab 100644 --- a/default.nix +++ b/default.nix @@ -7,4 +7,5 @@ let in { inherit (nixos.config.system) toplevel; inherit (nixos.config.mobile) outputs; + inherit (nixos) pkgs; } diff --git a/pkgs/qmi-nmea/Makefile b/pkgs/qmi-nmea/Makefile new file mode 100644 index 0000000..2795109 --- /dev/null +++ b/pkgs/qmi-nmea/Makefile @@ -0,0 +1,21 @@ +PREFIX?=/usr/local + +CFLAGS=\ + -DPACKAGE_VERSION=0.1 -I. \ + $(shell $(PKG_CONFIG) --cflags qmi-glib) \ + $(shell $(PKG_CONFIG) --cflags mbim-glib) \ + $(shell $(PKG_CONFIG) --cflags glib-2.0) + +LDFLAGS=\ + $(shell $(PKG_CONFIG) --libs qmi-glib) \ + $(shell $(PKG_CONFIG) --libs mbim-glib) \ + $(shell $(PKG_CONFIG) --libs glib-2.0) + +default: qmi-nmea + +SRCS=qmi-nmea.c qmicli-atr.c qmicli-dms.c qmicli-dpm.c qmicli-dsd.c qmicli-fox.c qmicli-gas.c qmicli-gms.c qmicli-helpers.c qmicli-imsa.c qmicli-ims.c qmicli-imsp.c qmicli-link-management.c qmicli-loc.c qmicli-nas.c qmicli-pbm.c qmicli-pdc.c qmicli-qmiwwan.c qmicli-qos.c qmicli-sar.c qmicli-uim.c qmicli-voice.c qmicli-wda.c qmicli-wds.c qmicli-wms.c + +qmi-nmea: $(patsubst %.c,%.o,$(SRCS)) + +install: + install -D -t $(PREFIX)/bin qmi-nmea diff --git a/pkgs/qmi-nmea/config.h b/pkgs/qmi-nmea/config.h new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pkgs/qmi-nmea/config.h @@ -0,0 +1 @@ + diff --git a/pkgs/qmi-nmea/default.nix b/pkgs/qmi-nmea/default.nix new file mode 100644 index 0000000..a07e97e --- /dev/null +++ b/pkgs/qmi-nmea/default.nix @@ -0,0 +1,16 @@ +{ + stdenv, + libqmi, + libmbim, + glib, + + pkg-config +}: +stdenv.mkDerivation { + name = "qmi-nmea"; + src = ./.; + nativeBuildInputs = [ pkg-config ]; + buildInputs = [ libqmi libmbim glib ]; + PACKAGE_VERSION="1"; + makeFlags = [ "PREFIX=${placeholder "out"}" ]; +} diff --git a/pkgs/qmi-nmea/qmi-common.h b/pkgs/qmi-nmea/qmi-common.h new file mode 100644 index 0000000..d59e5e7 --- /dev/null +++ b/pkgs/qmi-nmea/qmi-common.h @@ -0,0 +1,16 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2023 Fabio Porcedda + */ + +#ifndef _COMMON_QMI_COMMON_H_ +#define _COMMON_QMI_COMMON_H_ + +#include + +gchar *qmi_common_str_hex (gconstpointer mem, + gsize size, + gchar delimiter); + +#endif /* _COMMON_QMI_COMMON_H_ */ diff --git a/pkgs/qmi-nmea/qmi-nmea.c b/pkgs/qmi-nmea/qmi-nmea.c new file mode 100644 index 0000000..ea694f9 --- /dev/null +++ b/pkgs/qmi-nmea/qmi-nmea.c @@ -0,0 +1,1169 @@ +/* -*- 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) 2012-2023 Aleksander Morgado + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#if QMI_MBIM_QMUX_SUPPORTED +#include +#endif + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#define PROGRAM_NAME "qmicli" +#define PROGRAM_VERSION PACKAGE_VERSION + +/* Globals */ +static GMainLoop *loop; +static GCancellable *cancellable; +static QmiDevice *device; +static QmiClient *client; +static QmiService service; +static gboolean operation_status; +static gboolean expect_indications; +#if QMI_QRTR_SUPPORTED +static QrtrBus *qrtr_bus; +#endif + +/* Main options */ +static gchar *device_str; +static gboolean get_service_version_info_flag; +static gchar *device_set_instance_id_str; +static gboolean device_open_version_info_flag; +static gboolean device_open_sync_flag; +static gchar *device_open_net_str; +static gboolean device_open_proxy_flag; +#if QMI_MBIM_QMUX_SUPPORTED +static gboolean device_open_qmi_flag; +static gboolean device_open_mbim_flag; +static gboolean device_open_auto_flag; +#endif +static gchar *client_cid_str; +static gboolean client_no_release_cid_flag; +static gboolean verbose_flag; +static gboolean verbose_full_flag; +static gboolean silent_flag; +static gboolean version_flag; + +static GOptionEntry main_entries[] = { + { "device", 'd', 0, G_OPTION_ARG_STRING, &device_str, +#if QMI_QRTR_SUPPORTED + "Specify device path or QRTR URI (e.g. qrtr://0)", + "[PATH|URI]" +#else + "Specify device path", + "[PATH]" +#endif + }, + { "get-service-version-info", 0, 0, G_OPTION_ARG_NONE, &get_service_version_info_flag, + "Get service version info", + NULL + }, + { "device-set-instance-id", 0, 0, G_OPTION_ARG_STRING, &device_set_instance_id_str, + "Set instance ID", + "[Instance ID]" + }, + { "device-open-version-info", 0, 0, G_OPTION_ARG_NONE, &device_open_version_info_flag, + "Run version info check when opening device", + NULL + }, + { "device-open-sync", 0, 0, G_OPTION_ARG_NONE, &device_open_sync_flag, + "Run sync operation when opening device", + NULL + }, + { "device-open-proxy", 'p', 0, G_OPTION_ARG_NONE, &device_open_proxy_flag, + "Request to use the 'qmi-proxy' proxy", + NULL + }, +#if QMI_MBIM_QMUX_SUPPORTED + { "device-open-qmi", 0, 0, G_OPTION_ARG_NONE, &device_open_qmi_flag, + "Open a cdc-wdm device explicitly in QMI mode", + NULL + }, + { "device-open-mbim", 0, 0, G_OPTION_ARG_NONE, &device_open_mbim_flag, + "Open a cdc-wdm device explicitly in MBIM mode", + NULL + }, + { "device-open-auto", 0, 0, G_OPTION_ARG_NONE, &device_open_auto_flag, + "Open a cdc-wdm device in either QMI or MBIM mode (default)", + NULL + }, +#endif + { "device-open-net", 0, 0, G_OPTION_ARG_STRING, &device_open_net_str, + "Open device with specific link protocol and QoS flags", + "[net-802-3|net-raw-ip|net-qos-header|net-no-qos-header]" + }, + { "client-cid", 0, 0, G_OPTION_ARG_STRING, &client_cid_str, + "Use the given CID, don't allocate a new one", + "[CID]" + }, + { "client-no-release-cid", 0, 0, G_OPTION_ARG_NONE, &client_no_release_cid_flag, + "Do not release the CID when exiting", + NULL + }, + { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag, + "Run action with verbose logs, including the debug ones", + NULL + }, + { "verbose-full", 0, 0, G_OPTION_ARG_NONE, &verbose_full_flag, + "Run action with verbose logs, including the debug ones and personal info", + NULL + }, + { "silent", 0, 0, G_OPTION_ARG_NONE, &silent_flag, + "Run action with no logs; not even the error/warning ones", + NULL + }, + { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag, + "Print version", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +static gboolean +signals_handler (void) +{ + if (cancellable) { + /* Ignore consecutive requests of cancellation */ + if (!g_cancellable_is_cancelled (cancellable)) { + g_printerr ("cancelling the operation...\n"); + g_cancellable_cancel (cancellable); + /* Re-set the signal handler to allow main loop cancellation on + * second signal */ + return G_SOURCE_CONTINUE; + } + } + + if (loop && g_main_loop_is_running (loop)) { + g_printerr ("cancelling the main loop...\n"); + g_idle_add ((GSourceFunc) g_main_loop_quit, loop); + } + return G_SOURCE_REMOVE; +} + +static void +log_handler (const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer user_data) +{ + const gchar *log_level_str; + time_t now; + gchar time_str[64]; + struct tm *local_time; + gboolean err; + + /* Nothing to do if we're silent */ + if (silent_flag) + return; + + now = time ((time_t *) NULL); + local_time = localtime (&now); + strftime (time_str, 64, "%d %b %Y, %H:%M:%S", local_time); + err = FALSE; + + switch (log_level) { + case G_LOG_LEVEL_WARNING: + log_level_str = "-Warning **"; + err = TRUE; + break; + + case G_LOG_LEVEL_CRITICAL: + case G_LOG_LEVEL_ERROR: + log_level_str = "-Error **"; + err = TRUE; + break; + + case G_LOG_LEVEL_DEBUG: + log_level_str = "[Debug]"; + break; + + case G_LOG_LEVEL_MESSAGE: + case G_LOG_LEVEL_INFO: + log_level_str = ""; + break; + + case G_LOG_FLAG_FATAL: + case G_LOG_LEVEL_MASK: + case G_LOG_FLAG_RECURSION: + default: + g_assert_not_reached (); + } + + if (!verbose_flag && !verbose_full_flag && !err) + return; + + g_fprintf (err ? stderr : stdout, + "[%s] %s %s\n", + time_str, + log_level_str, + message); +} + +G_GNUC_NORETURN +static void +print_version_and_exit (void) +{ + g_print ("Copyright (C) 2012-2023 Aleksander Morgado, 2024 Daniel Barlow\n" + "License GPLv2+: GNU GPL version 2 or later \n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n" + "\n"); + exit (EXIT_SUCCESS); +} + +static gboolean +generic_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (!!device_set_instance_id_str + + get_service_version_info_flag); + + if (n_actions > 1) { + g_printerr ("error: too many generic actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +/*****************************************************************************/ +/* Report that indications are expected */ + +void +qmicli_expect_indications (void) +{ + expect_indications = TRUE; +} + +/*****************************************************************************/ +/* Running asynchronously */ + +static void +close_ready (QmiDevice *dev, + GAsyncResult *res) +{ + GError *error = NULL; + + if (!qmi_device_close_finish (dev, res, &error)) { + g_printerr ("error: couldn't close: %s\n", error->message); + g_error_free (error); + } else + g_debug ("Closed"); + + g_main_loop_quit (loop); +} + +static void +release_client_ready (QmiDevice *dev, + GAsyncResult *res) +{ + GError *error = NULL; + + if (!qmi_device_release_client_finish (dev, res, &error)) { + g_printerr ("error: couldn't release client: %s\n", error->message); + g_error_free (error); + } else + g_debug ("Client released"); + + qmi_device_close_async (dev, 10, NULL, (GAsyncReadyCallback) close_ready, NULL); +} + +void +qmicli_async_operation_done (gboolean reported_operation_status, + gboolean skip_cid_release) +{ + QmiDeviceReleaseClientFlags flags = QMI_DEVICE_RELEASE_CLIENT_FLAGS_NONE; + + /* Keep the result of the operation */ + operation_status = reported_operation_status; + + /* Cleanup cancellation */ + g_clear_object (&cancellable); + + /* If no client was allocated (e.g. generic action), just quit */ + if (!client) { + g_main_loop_quit (loop); + return; + } + + if (skip_cid_release) + g_debug ("Skipped CID release"); + else if (!client_no_release_cid_flag) + flags |= QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID; + else + g_print ("[%s] Client ID not released:\n" + "\tService: '%s'\n" + "\t CID: '%u'\n", + qmi_device_get_path_display (device), + qmi_service_get_string (service), + qmi_client_get_cid (client)); + + qmi_device_release_client (device, + client, + flags, + 10, + NULL, + (GAsyncReadyCallback)release_client_ready, + NULL); +} + +static void +allocate_client_ready (QmiDevice *dev, + GAsyncResult *res) +{ + GError *error = NULL; + + client = qmi_device_allocate_client_finish (dev, res, &error); + if (!client) { + g_printerr ("error: couldn't create client for the '%s' service: %s\n", + qmi_service_get_string (service), + error->message); + exit (EXIT_FAILURE); + } + + /* Run the service-specific action */ + switch (service) { + case QMI_SERVICE_DMS: +#if defined HAVE_QMI_SERVICE_DMS + qmicli_dms_run (dev, QMI_CLIENT_DMS (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_NAS: +#if defined HAVE_QMI_SERVICE_NAS + qmicli_nas_run (dev, QMI_CLIENT_NAS (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_WDS: +#if defined HAVE_QMI_SERVICE_WDS + qmicli_wds_run (dev, QMI_CLIENT_WDS (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_PBM: +#if defined HAVE_QMI_SERVICE_PBM + qmicli_pbm_run (dev, QMI_CLIENT_PBM (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_PDC: +#if defined HAVE_QMI_SERVICE_PDC + qmicli_pdc_run (dev, QMI_CLIENT_PDC (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_UIM: +#if defined HAVE_QMI_SERVICE_UIM + qmicli_uim_run (dev, QMI_CLIENT_UIM (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_WMS: +#if defined HAVE_QMI_SERVICE_WMS + qmicli_wms_run (dev, QMI_CLIENT_WMS (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_WDA: +#if defined HAVE_QMI_SERVICE_WDA + qmicli_wda_run (dev, QMI_CLIENT_WDA (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_VOICE: +#if defined HAVE_QMI_SERVICE_VOICE + qmicli_voice_run (dev, QMI_CLIENT_VOICE (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_LOC: +#if defined HAVE_QMI_SERVICE_LOC + qmicli_loc_run (dev, QMI_CLIENT_LOC (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_QOS: +#if defined HAVE_QMI_SERVICE_QOS + qmicli_qos_run (dev, QMI_CLIENT_QOS (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_GAS: +#if defined HAVE_QMI_SERVICE_GAS + qmicli_gas_run (dev, QMI_CLIENT_GAS (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_GMS: +#if defined HAVE_QMI_SERVICE_GMS + qmicli_gms_run (dev, QMI_CLIENT_GMS (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_DSD: +#if defined HAVE_QMI_SERVICE_DSD + qmicli_dsd_run (dev, QMI_CLIENT_DSD (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_SAR: +#if defined HAVE_QMI_SERVICE_SAR + qmicli_sar_run (dev, QMI_CLIENT_SAR (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_DPM: +#if defined HAVE_QMI_SERVICE_DPM + qmicli_dpm_run (dev, QMI_CLIENT_DPM (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_FOX: +#if defined HAVE_QMI_SERVICE_FOX + qmicli_fox_run (dev, QMI_CLIENT_FOX (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_ATR: +#if defined HAVE_QMI_SERVICE_ATR + qmicli_atr_run (dev, QMI_CLIENT_ATR (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_IMSP: +#if defined HAVE_QMI_SERVICE_IMSP + qmicli_imsp_run (dev, QMI_CLIENT_IMSP (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_IMSA: +#if defined HAVE_QMI_SERVICE_IMSA + qmicli_imsa_run (dev, QMI_CLIENT_IMSA (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_IMS: +#if defined HAVE_QMI_SERVICE_IMS + qmicli_ims_run (dev, QMI_CLIENT_IMS (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_UNKNOWN: + case QMI_SERVICE_CTL: + case QMI_SERVICE_AUTH: + case QMI_SERVICE_AT: + case QMI_SERVICE_CAT2: + case QMI_SERVICE_QCHAT: + case QMI_SERVICE_RMTFS: + case QMI_SERVICE_TEST: + case QMI_SERVICE_ADC: + case QMI_SERVICE_CSD: + case QMI_SERVICE_MFS: + case QMI_SERVICE_TIME: + case QMI_SERVICE_TS: + case QMI_SERVICE_TMD: + case QMI_SERVICE_SAP: + case QMI_SERVICE_TSYNC: + case QMI_SERVICE_RFSA: + case QMI_SERVICE_CSVT: + case QMI_SERVICE_QCMAP: + case QMI_SERVICE_IMSVT: + case QMI_SERVICE_COEX: + case QMI_SERVICE_STX: + case QMI_SERVICE_BIT: + case QMI_SERVICE_IMSRTP: + case QMI_SERVICE_RFRPE: + case QMI_SERVICE_SSCTL: + case QMI_SERVICE_CAT: + case QMI_SERVICE_RMS: + case QMI_SERVICE_FOTA: + case QMI_SERVICE_PDS: + case QMI_SERVICE_OMA: + case QMI_SERVICE_SSC: + default: + break; + } + g_assert_not_reached (); +} + +static void +device_allocate_client (QmiDevice *dev) +{ + guint8 cid = QMI_CID_NONE; + + if (client_cid_str) { + guint32 cid32; + + cid32 = atoi (client_cid_str); + if (!cid32 || cid32 > G_MAXUINT8) { + g_printerr ("error: invalid CID given '%s'\n", + client_cid_str); + exit (EXIT_FAILURE); + } + + cid = (guint8)cid32; + g_debug ("Reusing CID '%u'", cid); + } + + /* As soon as we get the QmiDevice, create a client for the requested + * service */ + qmi_device_allocate_client (dev, + service, + cid, + 10, + cancellable, + (GAsyncReadyCallback)allocate_client_ready, + NULL); +} + +static void +set_instance_id_ready (QmiDevice *dev, + GAsyncResult *res) +{ + GError *error = NULL; + guint16 link_id; + + if (!qmi_device_set_instance_id_finish (dev, res, &link_id, &error)) { + g_printerr ("error: couldn't set instance ID: %s\n", + error->message); + exit (EXIT_FAILURE); + } + + g_print ("[%s] Instance ID set:\n" + "\tLink ID: '%" G_GUINT16_FORMAT "'\n", + qmi_device_get_path_display (dev), + link_id); + + /* We're done now */ + qmicli_async_operation_done (TRUE, FALSE); +} + +static void +device_set_instance_id (QmiDevice *dev) +{ + gint instance_id; + + if (g_str_equal (device_set_instance_id_str, "0")) + instance_id = 0; + else { + instance_id = atoi (device_set_instance_id_str); + if (instance_id == 0) { + g_printerr ("error: invalid instance ID given: '%s'\n", device_set_instance_id_str); + exit (EXIT_FAILURE); + } else if (instance_id < 0 || instance_id > G_MAXUINT8) { + g_printerr ("error: given instance ID is out of range [0,%u]: '%s'\n", + G_MAXUINT8, + device_set_instance_id_str); + exit (EXIT_FAILURE); + } + } + + g_debug ("Setting instance ID '%d'...", instance_id); + qmi_device_set_instance_id (dev, + (guint8)instance_id, + 10, + cancellable, + (GAsyncReadyCallback)set_instance_id_ready, + NULL); +} + +static void +get_service_version_info_ready (QmiDevice *dev, + GAsyncResult *res) +{ + GError *error = NULL; + GArray *services; + guint i; + + services = qmi_device_get_service_version_info_finish (dev, res, &error); + if (!services) { + g_printerr ("error: couldn't get service version info: %s\n", + error->message); + exit (EXIT_FAILURE); + } + + g_print ("[%s] Supported versions:\n", + qmi_device_get_path_display (dev)); + for (i = 0; i < services->len; i++) { + QmiDeviceServiceVersionInfo *info; + const gchar *service_str; + + info = &g_array_index (services, QmiDeviceServiceVersionInfo, i); + service_str = qmi_service_get_string (info->service); + if (service_str) + g_print ("\t%s (%u.%u)\n", + service_str, + info->major_version, + info->minor_version); + else + g_print ("\tunknown [0x%02x] (%u.%u)\n", + info->service, + info->major_version, + info->minor_version); + } + g_array_unref (services); + + /* We're done now */ + qmicli_async_operation_done (TRUE, FALSE); +} + +static void +device_get_service_version_info (QmiDevice *dev) +{ + g_debug ("Getting service version info..."); + qmi_device_get_service_version_info (dev, + 10, + cancellable, + (GAsyncReadyCallback)get_service_version_info_ready, + NULL); +} + +static void +device_open_ready (QmiDevice *dev, + GAsyncResult *res) +{ + GError *error = NULL; + + if (!qmi_device_open_finish (dev, res, &error)) { + g_printerr ("error: couldn't open the QmiDevice: %s\n", + error->message); + exit (EXIT_FAILURE); + } + + g_debug ("QMI Device at '%s' ready", + qmi_device_get_path_display (dev)); + + if (device_set_instance_id_str) + device_set_instance_id (dev); + else if (get_service_version_info_flag) + device_get_service_version_info (dev); + else if (qmicli_link_management_options_enabled ()) + qmicli_link_management_run (dev, cancellable); + else if (qmicli_qmiwwan_options_enabled ()) + qmicli_qmiwwan_run (dev, cancellable); + else + device_allocate_client (dev); +} + +static void +device_new_ready (GObject *unused, + GAsyncResult *res) +{ + QmiDeviceOpenFlags open_flags = QMI_DEVICE_OPEN_FLAGS_NONE; + GError *error = NULL; + + device = qmi_device_new_finish (res, &error); + if (!device) { + g_printerr ("error: couldn't create QmiDevice: %s\n", + error->message); + exit (EXIT_FAILURE); + } + +#if QMI_MBIM_QMUX_SUPPORTED + if (device_open_mbim_flag + device_open_qmi_flag + device_open_auto_flag > 1) { + g_printerr ("error: cannot specify multiple mode flags to open device\n"); + exit (EXIT_FAILURE); + } +#endif + + /* Setup device open flags */ + if (device_open_version_info_flag) + open_flags |= QMI_DEVICE_OPEN_FLAGS_VERSION_INFO; + if (device_open_sync_flag) + open_flags |= QMI_DEVICE_OPEN_FLAGS_SYNC; + if (device_open_proxy_flag) + open_flags |= QMI_DEVICE_OPEN_FLAGS_PROXY; +#if QMI_MBIM_QMUX_SUPPORTED + if (device_open_mbim_flag) + open_flags |= QMI_DEVICE_OPEN_FLAGS_MBIM; + if (device_open_auto_flag || (!device_open_qmi_flag && !device_open_mbim_flag)) + open_flags |= QMI_DEVICE_OPEN_FLAGS_AUTO; +#endif + if (expect_indications) + open_flags |= QMI_DEVICE_OPEN_FLAGS_EXPECT_INDICATIONS; + if (device_open_net_str) { + if (!qmicli_read_device_open_flags_from_string (device_open_net_str, &open_flags) || + !qmicli_validate_device_open_flags (open_flags)) + exit (EXIT_FAILURE); + } + + /* Open the device */ + qmi_device_open (device, + open_flags, + 15, + cancellable, + (GAsyncReadyCallback)device_open_ready, + NULL); +} + +#if QMI_QRTR_SUPPORTED + +static void +bus_new_ready (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GError) error = NULL; + guint node_id; + QrtrNode *node; + + node_id = GPOINTER_TO_UINT (user_data); + + qrtr_bus = qrtr_bus_new_finish (res, &error); + if (!qrtr_bus) { + g_printerr ("error: couldn't access QRTR bus: %s\n", error->message); + exit (EXIT_FAILURE); + } + + node = qrtr_bus_peek_node (qrtr_bus, node_id); + if (!node) { + g_printerr ("error: node with id %u not found in QRTR bus\n", node_id); + exit (EXIT_FAILURE); + } + + qmi_device_new_from_node (node, + cancellable, + (GAsyncReadyCallback)device_new_ready, + NULL); +} + +#endif + +static gboolean +make_device (GFile *file) +{ + g_autofree gchar *id = NULL; + + id = g_file_get_path (file); + if (id) { + /* Describes a local device file. */ + qmi_device_new (file, + cancellable, + (GAsyncReadyCallback)device_new_ready, + NULL); + return TRUE; + } + +#if QMI_QRTR_SUPPORTED + { + guint32 node_id; + + id = g_file_get_uri (file); + if (qrtr_get_node_for_uri (id, &node_id)) { + qrtr_bus_new (1000, /* ms */ + cancellable, + (GAsyncReadyCallback)bus_new_ready, + GUINT_TO_POINTER (node_id)); + return TRUE; + } + + g_printerr ("error: URI is neither a local file path nor a QRTR node: %s\n", id); + return FALSE; + } +#else + g_printerr ("error: URI is not a local file path: %s\n", id); + return FALSE; +#endif +} + +/*****************************************************************************/ + +static void +parse_actions (void) +{ + guint actions_enabled = 0; + + if (generic_options_enabled ()) { + service = QMI_SERVICE_CTL; + actions_enabled++; + } + + if (qmicli_link_management_options_enabled ()) { + service = QMI_SERVICE_UNKNOWN; + actions_enabled++; + } + + if (qmicli_qmiwwan_options_enabled ()) { + service = QMI_SERVICE_UNKNOWN; + actions_enabled++; + } + +#if defined HAVE_QMI_SERVICE_DMS + if (qmicli_dms_options_enabled ()) { + service = QMI_SERVICE_DMS; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_NAS + if (qmicli_nas_options_enabled ()) { + service = QMI_SERVICE_NAS; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_WDS + if (qmicli_wds_options_enabled ()) { + service = QMI_SERVICE_WDS; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_PBM + if (qmicli_pbm_options_enabled ()) { + service = QMI_SERVICE_PBM; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_PDC + if (qmicli_pdc_options_enabled ()) { + service = QMI_SERVICE_PDC; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_UIM + if (qmicli_uim_options_enabled ()) { + service = QMI_SERVICE_UIM; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_SAR + if (qmicli_sar_options_enabled ()) { + service = QMI_SERVICE_SAR; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_WMS + if (qmicli_wms_options_enabled ()) { + service = QMI_SERVICE_WMS; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_WDA + if (qmicli_wda_options_enabled ()) { + service = QMI_SERVICE_WDA; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_VOICE + if (qmicli_voice_options_enabled ()) { + service = QMI_SERVICE_VOICE; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_LOC + if (qmicli_loc_options_enabled ()) { + service = QMI_SERVICE_LOC; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_QOS + if (qmicli_qos_options_enabled ()) { + service = QMI_SERVICE_QOS; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_GAS + if (qmicli_gas_options_enabled ()) { + service = QMI_SERVICE_GAS; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_GMS + if (qmicli_gms_options_enabled ()) { + service = QMI_SERVICE_GMS; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_DSD + if (qmicli_dsd_options_enabled ()) { + service = QMI_SERVICE_DSD; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_DPM + if (qmicli_dpm_options_enabled ()) { + service = QMI_SERVICE_DPM; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_FOX + if (qmicli_fox_options_enabled ()) { + service = QMI_SERVICE_FOX; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_ATR + if (qmicli_atr_options_enabled ()) { + service = QMI_SERVICE_ATR; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_IMSP + if (qmicli_imsp_options_enabled ()) { + service = QMI_SERVICE_IMSP; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_IMSA + if (qmicli_imsa_options_enabled ()) { + service = QMI_SERVICE_IMSA; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_IMS + if (qmicli_ims_options_enabled ()) { + service = QMI_SERVICE_IMS; + actions_enabled++; + } +#endif + + /* Cannot mix actions from different services */ + if (actions_enabled > 1) { + g_printerr ("error: cannot execute multiple actions of different services\n"); + exit (EXIT_FAILURE); + } + + /* No options? */ + if (actions_enabled == 0) { + g_printerr ("error: no actions specified\n"); + exit (EXIT_FAILURE); + } + + /* Go on! */ +} + +int main (int argc, char **argv) +{ + GError *error = NULL; + GFile *file; + GOptionContext *context; + + setlocale (LC_ALL, ""); + + /* Setup option context, process it and destroy it */ + context = g_option_context_new ("- Control QMI devices"); +#if defined HAVE_QMI_SERVICE_DMS + g_option_context_add_group (context, qmicli_dms_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_NAS + g_option_context_add_group (context, qmicli_nas_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_WDS + g_option_context_add_group (context, qmicli_wds_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_PBM + g_option_context_add_group (context, qmicli_pbm_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_PDC + g_option_context_add_group (context, qmicli_pdc_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_UIM + g_option_context_add_group (context, qmicli_uim_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_SAR + g_option_context_add_group (context, qmicli_sar_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_WMS + g_option_context_add_group (context, qmicli_wms_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_WDA + g_option_context_add_group (context, qmicli_wda_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_VOICE + g_option_context_add_group (context, qmicli_voice_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_LOC + g_option_context_add_group (context, qmicli_loc_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_QOS + g_option_context_add_group (context, qmicli_qos_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_GAS + g_option_context_add_group (context, qmicli_gas_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_GMS + g_option_context_add_group (context, qmicli_gms_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_DSD + g_option_context_add_group (context, qmicli_dsd_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_DPM + g_option_context_add_group (context, qmicli_dpm_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_FOX + g_option_context_add_group (context, qmicli_fox_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_ATR + g_option_context_add_group (context, qmicli_atr_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_IMSP + g_option_context_add_group (context, qmicli_imsp_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_IMSA + g_option_context_add_group (context, qmicli_imsa_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_IMS + g_option_context_add_group (context, qmicli_ims_get_option_group ()); +#endif + g_option_context_add_group (context, qmicli_link_management_get_option_group ()); + g_option_context_add_group (context, qmicli_qmiwwan_get_option_group ()); + g_option_context_add_main_entries (context, main_entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) { + g_printerr ("error: %s\n", + error->message); + exit (EXIT_FAILURE); + } + g_option_context_free (context); + + if (version_flag) + print_version_and_exit (); + + g_log_set_handler (NULL, G_LOG_LEVEL_MASK, log_handler, NULL); + g_log_set_handler ("Qmi", G_LOG_LEVEL_MASK, log_handler, NULL); + + if (verbose_flag && verbose_full_flag) { + g_printerr ("error: cannot specify --verbose and --verbose-full at the same time\n"); + exit (EXIT_FAILURE); + } else if (verbose_flag) { + qmi_utils_set_traces_enabled (TRUE); + qmi_utils_set_show_personal_info (FALSE); + } else if (verbose_full_flag) { + qmi_utils_set_traces_enabled (TRUE); + qmi_utils_set_show_personal_info (TRUE); + } + +#if QMI_MBIM_QMUX_SUPPORTED + /* libmbim logging */ + g_log_set_handler ("Mbim", G_LOG_LEVEL_MASK, log_handler, NULL); + if (verbose_flag) { + mbim_utils_set_traces_enabled (TRUE); +#if MBIM_CHECK_VERSION(1,27,6) + mbim_utils_set_show_personal_info (FALSE); +#endif + } else if (verbose_full_flag) { + mbim_utils_set_traces_enabled (TRUE); +#if MBIM_CHECK_VERSION(1,27,6) + mbim_utils_set_show_personal_info (TRUE); +#endif + } +#endif + +#if QMI_QRTR_SUPPORTED + /* libqrtr-glib logging */ + g_log_set_handler ("Qrtr", G_LOG_LEVEL_MASK, log_handler, NULL); +#endif + + /* No device path given? */ + if (!device_str) { + g_printerr ("error: no device path specified\n"); + exit (EXIT_FAILURE); + } + + /* Build new GFile from the commandline arg */ + file = g_file_new_for_commandline_arg (device_str); + + parse_actions (); + + /* Create requirements for async options */ + cancellable = g_cancellable_new (); + loop = g_main_loop_new (NULL, FALSE); + + /* Setup signals */ + g_unix_signal_add (SIGINT, (GSourceFunc) signals_handler, NULL); + g_unix_signal_add (SIGHUP, (GSourceFunc) signals_handler, NULL); + g_unix_signal_add (SIGTERM, (GSourceFunc) signals_handler, NULL); + + /* Launch QmiDevice creation */ + if (!make_device (file)) + return EXIT_FAILURE; + + g_main_loop_run (loop); + + if (cancellable) + g_object_unref (cancellable); + if (client) + g_object_unref (client); + if (device) + g_object_unref (device); + g_main_loop_unref (loop); + g_object_unref (file); + + return (operation_status ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/pkgs/qmi-nmea/qmicli b/pkgs/qmi-nmea/qmicli new file mode 100644 index 0000000..fa75668 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli @@ -0,0 +1,155 @@ +# qmicli(1) completion -*- shell-script -*- + +_qmicli() +{ + local cur prev words cword split + _init_completion -s || return + + case $prev in + -d|--device) + _filedir + return 0 + ;; + '--device-set-instance-id') + COMPREPLY=( $(compgen -W "[Instance-ID]" -- $cur) ) + return 0 + ;; + '--device-open-net') + COMPREPLY=( $(compgen -W "[net-802-3|net-raw-ip|net-qos-header|net-no-qos-header]" -- $cur) ) + return 0 + ;; + '--client-cid') + COMPREPLY=( $(compgen -W "[CID]" -- $cur) ) + return 0 + ;; + '--dms-uim-set-pin-protection') + COMPREPLY=( $(compgen -W "[(PIN|PIN2),(disable|enable),(current-PIN)]" -- $cur) ) + return 0 + ;; + '--dms-uim-verify-pin') + COMPREPLY=( $(compgen -W "[(PIN|PIN2),(current-PIN)]" -- $cur) ) + return 0 + ;; + '--dms-uim-unblock-pin') + COMPREPLY=( $(compgen -W "[(PIN|PIN2),(PUK),(new-PIN)]" -- $cur) ) + return 0 + ;; + '--dms-uim-change-pin') + COMPREPLY=( $(compgen -W "[(PIN|PIN2),(old-PIN),(new-PIN)]" -- $cur) ) + return 0 + ;; + '--dms-uim-get-ck-status') + COMPREPLY=( $(compgen -W "[(pn|pu|pp|pc|pf)]" -- $cur) ) + return 0 + ;; + '--dms-uim-set-ck-protection') + COMPREPLY=( $(compgen -W "[(pn|pu|pp|pc|pf),(disable),(key)]" -- $cur) ) + return 0 + ;; + '--dms-uim-unblock-ck') + COMPREPLY=( $(compgen -W "[(pn|pu|pp|pc|pf),(key)]" -- $cur) ) + return 0 + ;; + '--dms-set-operating-mode') + COMPREPLY=( $(compgen -W "[(Operating-Mode)]" -- $cur) ) + return 0 + ;; + '--dms-activate-automatic') + COMPREPLY=( $(compgen -W "[Activation-Code]" -- $cur) ) + return 0 + ;; + '--dms-activate-manual') + COMPREPLY=( $(compgen -W "[SPC,SID,MDN,MIN]" -- $cur) ) + return 0 + ;; + '--dms-set-user-lock-state') + COMPREPLY=( $(compgen -W "[(disable|enable),(current-lock-code)]" -- $cur) ) + return 0 + ;; + '--dms-set-user-lock-code') + COMPREPLY=( $(compgen -W "[(old-lock-code),(new-lock-code)]" -- $cur) ) + return 0 + ;; + '--dms-write-user-data') + COMPREPLY=( $(compgen -W "[(User-data)]" -- $cur) ) + return 0 + ;; + '--dms-restore-factory-defaults') + COMPREPLY=( $(compgen -W "[(Service-Programming-Code)]" -- $cur) ) + return 0 + ;; + '--dms-validate-service-programming-code') + COMPREPLY=( $(compgen -W "[(Service-Programming-Code)]" -- $cur) ) + return 0 + ;; + '--dms-select-stored-image') + COMPREPLY=( $(compgen -W "[modem#,pri#]" -- $cur) ) + return 0 + ;; + '--dms-delete-stored-image') + COMPREPLY=( $(compgen -W "[modem#,pri#]" -- $cur) ) + return 0 + ;; + '--nas-get-tx-rx-info') + COMPREPLY=( $(compgen -W "[(Radio-Interface)]" -- $cur) ) + return 0 + ;; + '--nas-set-system-selection-preference') + COMPREPLY=( $(compgen -W "[cdma-1x|cdma-1xevdo|gsm|umts|lte|td-scdma]" -- $cur) ) + return 0 + ;; + '--wds-start-network') + COMPREPLY=( $(compgen -W "[(APN),(PAP|CHAP|BOTH),(Username),(Password)]" -- $cur) ) + return 0 + ;; + '--wds-stop-network') + COMPREPLY=( $(compgen -W "[Packet-data-handle]" -- $cur) ) + return 0 + ;; + '--wds-get-profile-list') + COMPREPLY=( $(compgen -W "[3gpp|3gpp2]" -- $cur) ) + return 0 + ;; + '--wds-get-default-settings') + COMPREPLY=( $(compgen -W "[3gpp|3gpp2]" -- $cur) ) + return 0 + ;; + '--wds-bind-mux-data-port') + COMPREPLY=( $(compgen -W "[(MuxId),(Ep-Iface-Number)]" -- $cur) ) + return 0 + ;; + '--wds-set-ip-family') + COMPREPLY=( $(compgen -W "[4|6]" -- $cur) ) + return 0 + ;; + '--uim-read-transparent') + COMPREPLY=( $(compgen -W "[0xNNNN,0xNNNN,...]" -- $cur) ) + return 0 + ;; + '--uim-get-file-attributes') + COMPREPLY=( $(compgen -W "[0xNNNN,0xNNNN,...]" -- $cur) ) + return 0 + ;; + '--wda-set-data-format') + COMPREPLY=( $(compgen -W "[raw-ip|802-3]" -- $cur) ) + return 0 + ;; + '-V'|'--version') + return 0 + ;; + '-h'|'--help'|'--help-all'|'--help-dms'|'--help-nas'|'--help-wds'|'--help-pbm'|'--help-uim'|'--help-wda') + return 0 + ;; + esac + + $split && return 0 + + if [[ $cur == -* ]]; then + COMPREPLY=( $( compgen -W '$( _parse_help "$1" --help-all )' -- "$cur" ) ) + [[ $COMPREPLY == *= ]] && compopt -o nospace + return 0 + fi +} && +complete -F _qmicli qmicli + +# ex: ts=4 sw=4 et filetype=sh diff --git a/pkgs/qmi-nmea/qmicli-atr.c b/pkgs/qmi-nmea/qmicli-atr.c new file mode 100644 index 0000000..7a4ea94 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-atr.c @@ -0,0 +1,413 @@ +/* -*- 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) 2023 Daniele Palmas + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_ATR + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientAtr *client; + GCancellable *cancellable; + guint timeout_id; + guint received_indication_id; + gboolean monitor; +} Context; +static Context *ctx; + +/* Options */ +static gchar *send_str; +static gchar *send_only_str; +static gboolean monitor_indications_flag; +static gboolean noop_flag; + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_ATR_SEND && defined HAVE_QMI_INDICATION_ATR_RECEIVED + { "atr-send", 0, 0, G_OPTION_ARG_STRING, &send_str, + "Send an AT command and wait for the reply", + "[AT command]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_ATR_SEND + { "atr-send-only", 0, 0, G_OPTION_ARG_STRING, &send_only_str, + "Send an AT command without waiting for the reply", + "[AT command]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_ATR_SEND && defined HAVE_QMI_INDICATION_ATR_RECEIVED + { "atr-monitor", 0, 0, G_OPTION_ARG_NONE, &monitor_indications_flag, + "Watch for unsolicited indications", + NULL + }, +#endif + { "atr-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release an ATR client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_atr_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("atr", + "ATR options:", + "Show AT Relay Service options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_atr_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (!!send_str + + !!send_only_str + + monitor_indications_flag + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many ATR actions requested\n"); + exit (EXIT_FAILURE); + } + + /* Actions that require receiving QMI indication messages must specify that + * indications are expected. */ + if (!!send_str || monitor_indications_flag) + qmicli_expect_indications (); + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->timeout_id) + g_source_remove (context->timeout_id); + + if (context->received_indication_id) + g_signal_handler_disconnect (context->client, context->received_indication_id); + + if (context->cancellable) + g_object_unref (context->cancellable); + if (context->device) + g_object_unref (context->device); + if (context->client) + g_object_unref (context->client); + g_slice_free (Context, context); +} + +static void +operation_shutdown (gboolean operation_status) +{ + /* Cleanup context and finish async operation */ + context_free (ctx); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_ATR_SEND && defined HAVE_QMI_INDICATION_ATR_RECEIVED + +/******************************************************************************/ +/* Send */ + +static QmiMessageAtrSendInput * +send_input_create (const gchar *str) +{ + QmiMessageAtrSendInput *input = NULL; + + input = qmi_message_atr_send_input_new (); + qmi_message_atr_send_input_set_message (input, str, NULL); + return input; +} + +static gboolean +send_timed_out (void) +{ + ctx->timeout_id = 0; + g_printerr ("error: operation failed: timeout\n"); + operation_shutdown (FALSE); + return G_SOURCE_REMOVE; +} + +static gboolean +is_final_response (const gchar *reply) +{ + /* The following regexes are taken from MM serial parser */ + GRegexCompileFlags flags = G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW | G_REGEX_OPTIMIZE; + g_autoptr(GRegex) regex_ok = NULL; + g_autoptr(GRegex) regex_connect = NULL; + g_autoptr(GRegex) regex_cme_error = NULL; + g_autoptr(GRegex) regex_cms_error = NULL; + g_autoptr(GRegex) regex_unknown_error = NULL; + g_autoptr(GRegex) regex_connect_failed = NULL; + + regex_ok = g_regex_new ("\\r\\nOK(\\r\\n)+", flags, 0, NULL); + regex_connect = g_regex_new ("\\r\\nCONNECT.*\\r\\n", flags, 0, NULL); + regex_cme_error = g_regex_new ("\\r\\n\\+CME ERROR*\\r\\n", flags, 0, NULL); + regex_cms_error = g_regex_new ("\\r\\n\\+CMS ERROR*\\r\\n", flags, 0, NULL); + regex_unknown_error = g_regex_new ("\\r\\n(ERROR)|(COMMAND NOT SUPPORT)\\r\\n", flags, 0, NULL); + regex_connect_failed = g_regex_new ("\\r\\n(NO CARRIER)|(BUSY)|(NO ANSWER)|(NO DIALTONE)\\r\\n", flags, 0, NULL); + + if (g_regex_match_full (regex_ok, + reply, strlen (reply), + 0, 0, NULL, NULL) || + g_regex_match_full (regex_unknown_error, + reply, strlen (reply), + 0, 0, NULL, NULL) || + g_regex_match_full (regex_cme_error, + reply, strlen (reply), + 0, 0, NULL, NULL) || + g_regex_match_full (regex_cms_error, + reply, strlen (reply), + 0, 0, NULL, NULL) || + g_regex_match_full (regex_connect, + reply, strlen (reply), + 0, 0, NULL, NULL) || + g_regex_match_full (regex_connect_failed, + reply, strlen (reply), + 0, 0, NULL, NULL)) + return TRUE; + + return FALSE; +} + +static void +indication_received (QmiClientAtr *client, + QmiIndicationAtrReceivedOutput *output) +{ + const gchar *received; + g_autoptr(GError) error = NULL; + + if (!qmi_indication_atr_received_output_get_message (output, &received, &error)) { + g_printerr ("error: couldn't get indication message: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (ctx->monitor) { + /* When monitoring we don't want to print final responses + * e.g. the initial ATE0 reply or the ones for the commands sent with + * atr-send-only using the same cid + * The comparison with "ATE0" is for avoid printing the echo of the + * mandatory initial command */ + if (!is_final_response (received) && g_strcmp0 (received, "ATE0\r")) + g_print ("%s", received); + } else { + /* No need to print an additional '\n', since the indication already has '\r\n' */ + g_print ("%s", received); + /* The reply can arrive with multiple indications, so we need to check if the + * indication has the final response */ + if (is_final_response (received)) { + g_print ("Successfully received final response\n"); + operation_shutdown (TRUE); + } + } +} + +static void +monitoring_cancelled (GCancellable *cancellable) +{ + operation_shutdown (TRUE); +} + +static void +send_ready (QmiClientAtr *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageAtrSendOutput) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_atr_send_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_atr_send_output_get_result (output, &error)) { + g_printerr ("error: couldn't send AT command: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (ctx->monitor) { + /* User can use Ctrl+C to cancel the monitoring at any time */ + g_cancellable_connect (ctx->cancellable, + G_CALLBACK (monitoring_cancelled), + NULL, + NULL); + g_print ("Monitoring unsolicited indications: press Ctrl+C to stop\n"); + } else + /* Wait for the response asynchronously: 120 seconds should be enough also + * for long-lasting commands (e.g. AT+COPS=?) */ + ctx->timeout_id = g_timeout_add_seconds (120, + (GSourceFunc) send_timed_out, + NULL); + + ctx->received_indication_id = g_signal_connect (ctx->client, + "received", + G_CALLBACK (indication_received), + NULL); +} + +#endif /* HAVE_QMI_MESSAGE_ATR_SEND + * HAVE_QMI_INDICATION_ATR_RECEIVED */ + +#if defined HAVE_QMI_MESSAGE_ATR_SEND + +/******************************************************************************/ +/* Send and don't wait for the reply */ + +static void +send_only_ready (QmiClientAtr *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageAtrSendOutput) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_atr_send_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_atr_send_output_get_result (output, &error)) { + g_printerr ("error: couldn't send AT command: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("Successfully sent AT command\n"); + operation_shutdown (TRUE); +} + +static void +generic_send (const gchar *cmd, + GAsyncReadyCallback cb) +{ + g_autofree gchar *at_cmd = NULL; + g_autoptr(QmiMessageAtrSendInput) input = NULL; + + g_debug ("Asynchronously sending AT command..."); + + at_cmd = g_strconcat (cmd, "\r", NULL); + input = send_input_create (at_cmd); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_atr_send (ctx->client, + input, + 10, + ctx->cancellable, + cb, + NULL); +} + +#endif /* HAVE_QMI_MESSAGE_ATR_SEND */ + +/******************************************************************************/ +/* Common */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_atr_run (QmiDevice *device, + QmiClientAtr *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new0 (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_ATR_SEND && defined HAVE_QMI_INDICATION_ATR_RECEIVED + if (send_str) { + generic_send (send_str, (GAsyncReadyCallback)send_ready); + return; + } + + if (monitor_indications_flag) { + /* To really open the AT communication and receive unsolicited + * indications it is mandatory to send an initial AT command. + * The command ATE0 has been chosen so that, if needed, commands + * can be sent by a different qmicli instance reusing the monitoring + * cid with --atr-send-only. This avoids also polluting the monitoring + * operation with the comands echo */ + ctx->monitor = TRUE; + generic_send ("ATE0", (GAsyncReadyCallback)send_ready); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_ATR_SEND + if (send_only_str) { + generic_send (send_only_str, (GAsyncReadyCallback)send_only_ready); + return; + } +#endif + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_ATR */ diff --git a/pkgs/qmi-nmea/qmicli-dms.c b/pkgs/qmi-nmea/qmicli-dms.c new file mode 100644 index 0000000..afba1e1 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-dms.c @@ -0,0 +1,5465 @@ +/* -*- 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) 2012-2017 Aleksander Morgado + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_DMS + +#undef VALIDATE_UNKNOWN +#define VALIDATE_UNKNOWN(str) (str ? str : "unknown") + +#undef VALIDATE_MASK_NONE +#define VALIDATE_MASK_NONE(str) (str ? str : "none") + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientDms *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gboolean get_ids_flag; +static gboolean get_capabilities_flag; +static gboolean get_manufacturer_flag; +static gboolean get_model_flag; +static gboolean get_revision_flag; +static gboolean get_msisdn_flag; +static gboolean get_power_state_flag; +static gchar *uim_set_pin_protection_str; +static gchar *uim_verify_pin_str; +static gchar *uim_unblock_pin_str; +static gchar *uim_change_pin_str; +static gboolean uim_get_pin_status_flag; +static gboolean uim_get_iccid_flag; +static gboolean uim_get_imsi_flag; +static gboolean uim_get_state_flag; +static gchar *uim_get_ck_status_str; +static gchar *uim_set_ck_protection_str; +static gchar *uim_unblock_ck_str; +static gboolean get_hardware_revision_flag; +static gboolean get_operating_mode_flag; +static gchar *set_operating_mode_str; +static gboolean get_time_flag; +static gboolean get_prl_version_flag; +static gboolean get_activation_state_flag; +static gchar *activate_automatic_str; +static gchar *activate_manual_str; +static gboolean get_user_lock_state_flag; +static gchar *set_user_lock_state_str; +static gchar *set_user_lock_code_str; +static gboolean read_user_data_flag; +static gchar *write_user_data_str; +static gboolean read_eri_file_flag; +static gchar *restore_factory_defaults_str; +static gchar *validate_service_programming_code_str; +static gboolean set_firmware_id_flag; +static gboolean get_band_capabilities_flag; +static gboolean get_factory_sku_flag; +static gboolean list_stored_images_flag; +static gchar *select_stored_image_str; +static gchar *delete_stored_image_str; +static gboolean get_firmware_preference_flag; +static gchar *set_firmware_preference_str; +static gboolean get_boot_image_download_mode_flag; +static gchar *set_boot_image_download_mode_str; +static gboolean get_software_version_flag; +static gboolean set_fcc_authentication_flag; +static gboolean get_supported_messages_flag; +static gchar *hp_change_device_mode_str; +static gboolean swi_get_current_firmware_flag; +static gboolean swi_get_usb_composition_flag; +static gchar *swi_set_usb_composition_str; +static gchar *dell_change_device_mode_str; /* deprecated */ +static gchar *foxconn_change_device_mode_str; +static gchar *dell_get_firmware_version_str; /* deprecated */ +static gchar *foxconn_get_firmware_version_str; +static gint foxconn_set_fcc_authentication_int = -1; +static gchar *foxconn_set_fcc_authentication_v2_str; +static gchar *get_mac_address_str; +static gboolean reset_flag; +static gboolean noop_flag; + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_DMS_GET_IDS + { "dms-get-ids", 0, 0, G_OPTION_ARG_NONE, &get_ids_flag, + "Get IDs", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_CAPABILITIES + { "dms-get-capabilities", 0, 0, G_OPTION_ARG_NONE, &get_capabilities_flag, + "Get capabilities", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_MANUFACTURER + { "dms-get-manufacturer", 0, 0, G_OPTION_ARG_NONE, &get_manufacturer_flag, + "Get manufacturer", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_MODEL + { "dms-get-model", 0, 0, G_OPTION_ARG_NONE, &get_model_flag, + "Get model", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_REVISION + { "dms-get-revision", 0, 0, G_OPTION_ARG_NONE, &get_revision_flag, + "Get revision", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_MSISDN + { "dms-get-msisdn", 0, 0, G_OPTION_ARG_NONE, &get_msisdn_flag, + "Get MSISDN", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_POWER_STATE + { "dms-get-power-state", 0, 0, G_OPTION_ARG_NONE, &get_power_state_flag, + "Get power state", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_UIM_SET_PIN_PROTECTION + { "dms-uim-set-pin-protection", 0, 0, G_OPTION_ARG_STRING, &uim_set_pin_protection_str, + "Set PIN protection in the UIM", + "[(PIN|PIN2),(disable|enable),(current PIN)]", + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_UIM_VERIFY_PIN + { "dms-uim-verify-pin", 0, 0, G_OPTION_ARG_STRING, &uim_verify_pin_str, + "Verify PIN", + "[(PIN|PIN2),(current PIN)]", + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_UIM_UNBLOCK_PIN + { "dms-uim-unblock-pin", 0, 0, G_OPTION_ARG_STRING, &uim_unblock_pin_str, + "Unblock PIN", + "[(PIN|PIN2),(PUK),(new PIN)]", + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_UIM_CHANGE_PIN + { "dms-uim-change-pin", 0, 0, G_OPTION_ARG_STRING, &uim_change_pin_str, + "Change PIN", + "[(PIN|PIN2),(old PIN),(new PIN)]", + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_UIM_GET_PIN_STATUS + { "dms-uim-get-pin-status", 0, 0, G_OPTION_ARG_NONE, &uim_get_pin_status_flag, + "Get PIN status", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_UIM_GET_ICCID + { "dms-uim-get-iccid", 0, 0, G_OPTION_ARG_NONE, &uim_get_iccid_flag, + "Get ICCID", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_UIM_GET_IMSI + { "dms-uim-get-imsi", 0, 0, G_OPTION_ARG_NONE, &uim_get_imsi_flag, + "Get IMSI", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_UIM_GET_STATE + { "dms-uim-get-state", 0, 0, G_OPTION_ARG_NONE, &uim_get_state_flag, + "Get UIM State", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_UIM_GET_CK_STATUS + { "dms-uim-get-ck-status", 0, 0, G_OPTION_ARG_STRING, &uim_get_ck_status_str, + "Get CK Status", + "[(pn|pu|pp|pc|pf)]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_UIM_SET_CK_PROTECTION + { "dms-uim-set-ck-protection", 0, 0, G_OPTION_ARG_STRING, &uim_set_ck_protection_str, + "Disable CK protection", + "[(pn|pu|pp|pc|pf),(disable),(key)]" + }, +#endif + { "dms-uim-unblock-ck", 0, 0, G_OPTION_ARG_STRING, &uim_unblock_ck_str, + "Unblock CK", + "[(pn|pu|pp|pc|pf),(key)]" + }, +#if defined HAVE_QMI_MESSAGE_DMS_GET_HARDWARE_REVISION + { "dms-get-hardware-revision", 0, 0, G_OPTION_ARG_NONE, &get_hardware_revision_flag, + "Get the HW revision", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_OPERATING_MODE + { "dms-get-operating-mode", 0, 0, G_OPTION_ARG_NONE, &get_operating_mode_flag, + "Get the device operating mode", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_SET_OPERATING_MODE + { "dms-set-operating-mode", 0, 0, G_OPTION_ARG_STRING, &set_operating_mode_str, + "Set the device operating mode", + "[(Operating mode)]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_TIME + { "dms-get-time", 0, 0, G_OPTION_ARG_NONE, &get_time_flag, + "Get the device time", + NULL, + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_PRL_VERSION + { "dms-get-prl-version", 0, 0, G_OPTION_ARG_NONE, &get_prl_version_flag, + "Get the PRL version", + NULL, + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_ACTIVATION_STATE + { "dms-get-activation-state", 0, 0, G_OPTION_ARG_NONE, &get_activation_state_flag, + "Get the state of the service activation", + NULL, + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_ACTIVATE_AUTOMATIC + { "dms-activate-automatic", 0, 0, G_OPTION_ARG_STRING, &activate_automatic_str, + "Request automatic service activation", + "[Activation Code]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_ACTIVATE_MANUAL + { "dms-activate-manual", 0, 0, G_OPTION_ARG_STRING, &activate_manual_str, + "Request manual service activation", + "[SPC,SID,MDN,MIN]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_USER_LOCK_STATE + { "dms-get-user-lock-state", 0, 0, G_OPTION_ARG_NONE, &get_user_lock_state_flag, + "Get the state of the user lock", + NULL, + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_SET_USER_LOCK_STATE + { "dms-set-user-lock-state", 0, 0, G_OPTION_ARG_STRING, &set_user_lock_state_str, + "Set the state of the user lock", + "[(disable|enable),(current lock code)]", + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_SET_USER_LOCK_CODE + { "dms-set-user-lock-code", 0, 0, G_OPTION_ARG_STRING, &set_user_lock_code_str, + "Change the user lock code", + "[(old lock code),(new lock code)]", + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_READ_USER_DATA + { "dms-read-user-data", 0, 0, G_OPTION_ARG_NONE, &read_user_data_flag, + "Read user data", + NULL, + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_WRITE_USER_DATA + { "dms-write-user-data", 0, 0, G_OPTION_ARG_STRING, &write_user_data_str, + "Write user data", + "[(User data)]", + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_READ_ERI_FILE + { "dms-read-eri-file", 0, 0, G_OPTION_ARG_NONE, &read_eri_file_flag, + "Read ERI file", + NULL, + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_RESTORE_FACTORY_DEFAULTS + { "dms-restore-factory-defaults", 0, 0, G_OPTION_ARG_STRING, &restore_factory_defaults_str, + "Restore factory defaults", + "[(Service Programming Code)]", + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_VALIDATE_SERVICE_PROGRAMMING_CODE + { "dms-validate-service-programming-code", 0, 0, G_OPTION_ARG_STRING, &validate_service_programming_code_str, + "Validate the Service Programming Code", + "[(Service Programming Code)]", + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_ID + { "dms-set-firmware-id", 0, 0, G_OPTION_ARG_NONE, &set_firmware_id_flag, + "Set firmware id", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_BAND_CAPABILITIES + { "dms-get-band-capabilities", 0, 0, G_OPTION_ARG_NONE, &get_band_capabilities_flag, + "Get band capabilities", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_FACTORY_SKU + { "dms-get-factory-sku", 0, 0, G_OPTION_ARG_NONE, &get_factory_sku_flag, + "Get factory stock keeping unit", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_LIST_STORED_IMAGES && defined HAVE_QMI_MESSAGE_DMS_GET_STORED_IMAGE_INFO + { "dms-list-stored-images", 0, 0, G_OPTION_ARG_NONE, &list_stored_images_flag, + "List stored images", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_PREFERENCE && \ + defined HAVE_QMI_MESSAGE_DMS_LIST_STORED_IMAGES + { "dms-select-stored-image", 0, 0, G_OPTION_ARG_STRING, &select_stored_image_str, + "Select stored image", + "[modem#,pri#] where # is the index" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_PREFERENCE && \ + defined HAVE_QMI_MESSAGE_DMS_LIST_STORED_IMAGES && \ + defined HAVE_QMI_MESSAGE_DMS_DELETE_STORED_IMAGE + { "dms-delete-stored-image", 0, 0, G_OPTION_ARG_STRING, &delete_stored_image_str, + "Delete stored image", + "[modem#|pri#] where # is the index" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_FIRMWARE_PREFERENCE + { "dms-get-firmware-preference", 0, 0, G_OPTION_ARG_NONE, &get_firmware_preference_flag, + "Get firmware preference", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_PREFERENCE + { "dms-set-firmware-preference", 0, 0, G_OPTION_ARG_STRING, &set_firmware_preference_str, + "Set firmware preference (required keys: firmware-version, config-version, carrier; optional keys: modem-storage-index, override-download=yes)", + "[\"key=value,...\"]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_BOOT_IMAGE_DOWNLOAD_MODE + { "dms-get-boot-image-download-mode", 0, 0, G_OPTION_ARG_NONE, &get_boot_image_download_mode_flag, + "Get boot image download mode", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_SET_BOOT_IMAGE_DOWNLOAD_MODE + { "dms-set-boot-image-download-mode", 0, 0, G_OPTION_ARG_STRING, &set_boot_image_download_mode_str, + "Set boot image download mode", + "[normal|boot-and-recovery]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_SOFTWARE_VERSION + { "dms-get-software-version", 0, 0, G_OPTION_ARG_NONE, &get_software_version_flag, + "Get software version", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_SET_FCC_AUTHENTICATION + { "dms-set-fcc-authentication", 0, 0, G_OPTION_ARG_NONE, &set_fcc_authentication_flag, + "Set FCC authentication", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_SUPPORTED_MESSAGES + { "dms-get-supported-messages", 0, 0, G_OPTION_ARG_NONE, &get_supported_messages_flag, + "Get supported messages", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_HP_CHANGE_DEVICE_MODE + { "dms-hp-change-device-mode", 0, 0, G_OPTION_ARG_STRING, &hp_change_device_mode_str, + "Change device mode (HP specific)", + "[fastboot]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_SWI_GET_CURRENT_FIRMWARE + { "dms-swi-get-current-firmware", 0, 0, G_OPTION_ARG_NONE, &swi_get_current_firmware_flag, + "Get Current Firmware (Sierra Wireless specific)", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_SWI_GET_USB_COMPOSITION + { "dms-swi-get-usb-composition", 0, 0, G_OPTION_ARG_NONE, &swi_get_usb_composition_flag, + "Get current and supported USB compositions (Sierra Wireless specific)", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_SWI_SET_USB_COMPOSITION + { "dms-swi-set-usb-composition", 0, 0, G_OPTION_ARG_STRING, &swi_set_usb_composition_str, + "Set USB composition (Sierra Wireless specific)", + "[#]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_FOXCONN_CHANGE_DEVICE_MODE + { "dms-foxconn-change-device-mode", 0, 0, G_OPTION_ARG_STRING, &foxconn_change_device_mode_str, + "Change device mode (Foxconn specific)", + "[fastboot-ota|fastboot-online]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_FOXCONN_GET_FIRMWARE_VERSION + { "dms-foxconn-get-firmware-version", 0, 0, G_OPTION_ARG_STRING, &foxconn_get_firmware_version_str, + "Get firmware version (Foxconn specific)", + "[firmware-mcfg-apps|firmware-mcfg|apps]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_FOXCONN_SET_FCC_AUTHENTICATION + { "dms-foxconn-set-fcc-authentication", 0, 0, G_OPTION_ARG_INT, &foxconn_set_fcc_authentication_int, + "Set FCC authentication (Foxconn specific)", + "[magic]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_FOXCONN_SET_FCC_AUTHENTICATION_V2 + { "dms-foxconn-set-fcc-authentication-v2", 0, 0, G_OPTION_ARG_STRING, &foxconn_set_fcc_authentication_v2_str, + "Set FCC authentication (Foxconn specific, v2)", + "[magic-string,magic-number]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_GET_MAC_ADDRESS + { "dms-get-mac-address", 0, 0, G_OPTION_ARG_STRING, &get_mac_address_str, + "Get default MAC address", + "[wlan|bt]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_RESET + { "dms-reset", 0, 0, G_OPTION_ARG_NONE, &reset_flag, + "Reset the service state", + NULL + }, +#endif + { "dms-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a DMS client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + /* deprecated entries (hidden in --help) */ +#if defined HAVE_QMI_MESSAGE_DMS_FOXCONN_CHANGE_DEVICE_MODE + { "dms-dell-change-device-mode", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &dell_change_device_mode_str, + "Change device mode (DELL specific)", + "[fastboot-ota|fastboot-online]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DMS_FOXCONN_GET_FIRMWARE_VERSION + { "dms-dell-get-firmware-version", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &dell_get_firmware_version_str, + "Get firmware version (DELL specific)", + "[firmware-mcfg-apps|firmware-mcfg|apps]" + }, +#endif + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_dms_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("dms", + "DMS options:", + "Show Device Management Service options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_dms_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (get_ids_flag + + get_capabilities_flag + + get_manufacturer_flag + + get_model_flag + + get_revision_flag + + get_msisdn_flag + + get_power_state_flag + + !!uim_set_pin_protection_str + + !!uim_verify_pin_str + + !!uim_unblock_pin_str + + !!uim_change_pin_str + + uim_get_pin_status_flag + + uim_get_iccid_flag + + uim_get_imsi_flag + + uim_get_state_flag + + !!uim_get_ck_status_str + + !!uim_set_ck_protection_str + + !!uim_unblock_ck_str + + get_hardware_revision_flag + + get_operating_mode_flag + + !!set_operating_mode_str + + get_time_flag + + get_prl_version_flag + + get_activation_state_flag + + !!activate_automatic_str + + !!activate_manual_str + + get_user_lock_state_flag + + !!set_user_lock_state_str + + !!set_user_lock_code_str + + read_user_data_flag + + !!write_user_data_str + + read_eri_file_flag + + !!restore_factory_defaults_str + + !!validate_service_programming_code_str + + set_firmware_id_flag + + get_band_capabilities_flag + + get_factory_sku_flag + + list_stored_images_flag + + !!select_stored_image_str + + !!delete_stored_image_str + + get_firmware_preference_flag + + !!set_firmware_preference_str + + get_boot_image_download_mode_flag + + !!set_boot_image_download_mode_str + + get_software_version_flag + + set_fcc_authentication_flag + + get_supported_messages_flag + + !!hp_change_device_mode_str + + swi_get_current_firmware_flag + + swi_get_usb_composition_flag + + !!swi_set_usb_composition_str + + !!dell_change_device_mode_str + + !!foxconn_change_device_mode_str + + !!dell_get_firmware_version_str + + !!foxconn_get_firmware_version_str + + (foxconn_set_fcc_authentication_int >= 0) + + !!foxconn_set_fcc_authentication_v2_str + + !!get_mac_address_str + + reset_flag + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many DMS actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->cancellable) + g_object_unref (context->cancellable); + if (context->device) + g_object_unref (context->device); + if (context->client) + g_object_unref (context->client); + g_slice_free (Context, context); +} + +static void +operation_shutdown (gboolean operation_status) +{ + /* Cleanup context and finish async operation */ + context_free (ctx); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_DMS_FOXCONN_CHANGE_DEVICE_MODE || \ + defined HAVE_QMI_MESSAGE_DMS_FOXCONN_CHANGE_DEVICE_MODE + +static void +operation_shutdown_skip_cid_release (gboolean operation_status) +{ + /* Cleanup context and finish async operation. Explicitly ask not to release + * the client CID. This is so that the qmicli operation doesn't fail after + * this step, e.g. if the device just reboots after the action. */ + context_free (ctx); + qmicli_async_operation_done (operation_status, TRUE); +} + +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_IDS + +static void +get_ids_ready (QmiClientDms *client, + GAsyncResult *res) +{ + const gchar *esn = NULL; + const gchar *imei = NULL; + const gchar *meid = NULL; + const gchar *imei_software_version = NULL; + QmiMessageDmsGetIdsOutput *output; + GError *error = NULL; + + output = qmi_client_dms_get_ids_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_dms_get_ids_output_get_result (output, &error)) { + g_printerr ("error: couldn't get IDs: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_ids_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_ids_output_get_esn (output, &esn, NULL); + qmi_message_dms_get_ids_output_get_imei (output, &imei, NULL); + qmi_message_dms_get_ids_output_get_meid (output, &meid, NULL); + + g_print ("[%s] Device IDs retrieved:\n" + "\t ESN: '%s'\n" + "\t IMEI: '%s'\n" + "\t MEID: '%s'\n", + qmi_device_get_path_display (ctx->device), + VALIDATE_UNKNOWN (esn), + VALIDATE_UNKNOWN (imei), + VALIDATE_UNKNOWN (meid)); + + if (qmi_message_dms_get_ids_output_get_imei_software_version (output, + &imei_software_version, + NULL)) { + g_print("\tIMEI SV: '%s'\n", imei_software_version); + } + + qmi_message_dms_get_ids_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_IDS */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_CAPABILITIES + +static void +get_capabilities_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsGetCapabilitiesOutput *output; + guint32 max_tx_channel_rate; + guint32 max_rx_channel_rate; + QmiDmsDataServiceCapability data_service_capability; + QmiDmsSimCapability sim_capability; + GArray *radio_interface_list; + GError *error = NULL; + GString *networks; + guint i; + + output = qmi_client_dms_get_capabilities_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_dms_get_capabilities_output_get_result (output, &error)) { + g_printerr ("error: couldn't get capabilities: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_capabilities_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_capabilities_output_get_info (output, + &max_tx_channel_rate, + &max_rx_channel_rate, + &data_service_capability, + &sim_capability, + &radio_interface_list, + NULL); + + networks = g_string_new (""); + for (i = 0; i < radio_interface_list->len; i++) { + g_string_append (networks, + qmi_dms_radio_interface_get_string ( + g_array_index (radio_interface_list, + QmiDmsRadioInterface, + i))); + if (i != radio_interface_list->len - 1) + g_string_append (networks, ", "); + } + + g_print ("[%s] Device capabilities retrieved:\n" + "\tMax TX channel rate: '%u'\n" + "\tMax RX channel rate: '%u'\n" + "\t Data Service: '%s'\n" + "\t SIM: '%s'\n" + "\t Networks: '%s'\n", + qmi_device_get_path_display (ctx->device), + max_tx_channel_rate, + max_rx_channel_rate, + qmi_dms_data_service_capability_get_string (data_service_capability), + qmi_dms_sim_capability_get_string (sim_capability), + networks->str); + + g_string_free (networks, TRUE); + qmi_message_dms_get_capabilities_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_CAPABILITIES */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_MANUFACTURER + +static void +get_manufacturer_ready (QmiClientDms *client, + GAsyncResult *res) +{ + const gchar *str = NULL; + QmiMessageDmsGetManufacturerOutput *output; + GError *error = NULL; + + output = qmi_client_dms_get_manufacturer_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_dms_get_manufacturer_output_get_result (output, &error)) { + g_printerr ("error: couldn't get manufacturer: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_manufacturer_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_manufacturer_output_get_manufacturer (output, &str, NULL); + + g_print ("[%s] Device manufacturer retrieved:\n" + "\tManufacturer: '%s'\n", + qmi_device_get_path_display (ctx->device), + VALIDATE_UNKNOWN (str)); + + qmi_message_dms_get_manufacturer_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_MANUFACTURER */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_MODEL + +static void +get_model_ready (QmiClientDms *client, + GAsyncResult *res) +{ + const gchar *str = NULL; + QmiMessageDmsGetModelOutput *output; + GError *error = NULL; + + output = qmi_client_dms_get_model_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_dms_get_model_output_get_result (output, &error)) { + g_printerr ("error: couldn't get model: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_model_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_model_output_get_model (output, &str, NULL); + + g_print ("[%s] Device model retrieved:\n" + "\tModel: '%s'\n", + qmi_device_get_path_display (ctx->device), + VALIDATE_UNKNOWN (str)); + + qmi_message_dms_get_model_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_MODEL */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_REVISION + +static void +get_revision_ready (QmiClientDms *client, + GAsyncResult *res) +{ + const gchar *str = NULL; + QmiMessageDmsGetRevisionOutput *output; + GError *error = NULL; + + output = qmi_client_dms_get_revision_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_dms_get_revision_output_get_result (output, &error)) { + g_printerr ("error: couldn't get revision: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_revision_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_revision_output_get_revision (output, &str, NULL); + + g_print ("[%s] Device revision retrieved:\n" + "\tRevision: '%s'\n", + qmi_device_get_path_display (ctx->device), + VALIDATE_UNKNOWN (str)); + + qmi_message_dms_get_revision_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_REVISION */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_MSISDN + +static void +get_msisdn_ready (QmiClientDms *client, + GAsyncResult *res) +{ + const gchar *str = NULL; + QmiMessageDmsGetMsisdnOutput *output; + GError *error = NULL; + + output = qmi_client_dms_get_msisdn_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_dms_get_msisdn_output_get_result (output, &error)) { + g_printerr ("error: couldn't get MSISDN: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_msisdn_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_msisdn_output_get_msisdn (output, &str, NULL); + + g_print ("[%s] Device MSISDN retrieved:\n" + "\tMSISDN: '%s'\n", + qmi_device_get_path_display (ctx->device), + VALIDATE_UNKNOWN (str)); + + qmi_message_dms_get_msisdn_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_MSISDN */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_POWER_STATE + +static void +get_power_state_ready (QmiClientDms *client, + GAsyncResult *res) +{ + g_autofree gchar *power_state_str = NULL; + guint8 power_state_flags; + guint8 battery_level; + QmiMessageDmsGetPowerStateOutput *output; + GError *error = NULL; + + output = qmi_client_dms_get_power_state_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_dms_get_power_state_output_get_result (output, &error)) { + g_printerr ("error: couldn't get power state: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_power_state_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_power_state_output_get_info (output, + &power_state_flags, + &battery_level, + NULL); + power_state_str = qmi_dms_power_state_build_string_from_mask ((QmiDmsPowerState)power_state_flags); + + g_print ("[%s] Device power state retrieved:\n" + "\tPower state: '%s'\n" + "\tBattery level: '%u %%'\n", + qmi_device_get_path_display (ctx->device), + VALIDATE_MASK_NONE (power_state_str), + (guint)battery_level); + + qmi_message_dms_get_power_state_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_POWER_STATE */ + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_SET_PIN_PROTECTION + +static QmiMessageDmsUimSetPinProtectionInput * +uim_set_pin_protection_input_create (const gchar *str) +{ + QmiMessageDmsUimSetPinProtectionInput *input = NULL; + g_auto(GStrv) split = NULL; + QmiDmsUimPinId pin_id; + gboolean enable_disable; + gchar *current_pin; + + /* Prepare inputs. + * Format of the string is: + * "[(PIN|PIN2),(disable|enable),(current PIN)]" + */ + split = g_strsplit (str, ",", -1); + if (qmicli_read_dms_uim_pin_id_from_string (split[0], &pin_id) && + qmicli_read_enable_disable_from_string (split[1], &enable_disable) && + qmicli_read_non_empty_string (split[2], "current PIN", ¤t_pin)) { + GError *error = NULL; + + input = qmi_message_dms_uim_set_pin_protection_input_new (); + if (!qmi_message_dms_uim_set_pin_protection_input_set_info ( + input, + pin_id, + enable_disable, + current_pin, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_uim_set_pin_protection_input_unref (input); + input = NULL; + } + } + + return input; +} + +static void +uim_set_pin_protection_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsUimSetPinProtectionOutput *output; + GError *error = NULL; + + output = qmi_client_dms_uim_set_pin_protection_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_dms_uim_set_pin_protection_output_get_result (output, &error)) { + guint8 verify_retries_left; + guint8 unblock_retries_left; + + g_printerr ("error: couldn't set PIN protection: %s\n", error->message); + g_error_free (error); + + if (qmi_message_dms_uim_set_pin_protection_output_get_pin_retries_status ( + output, + &verify_retries_left, + &unblock_retries_left, + NULL)) { + g_printerr ("[%s] Retries left:\n" + "\tVerify: %u\n" + "\tUnblock: %u\n", + qmi_device_get_path_display (ctx->device), + verify_retries_left, + unblock_retries_left); + } + + qmi_message_dms_uim_set_pin_protection_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] PIN protection updated\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_uim_set_pin_protection_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_UIM_SET_PIN_PROTECTION */ + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_VERIFY_PIN + +static QmiMessageDmsUimVerifyPinInput * +uim_verify_pin_input_create (const gchar *str) +{ + QmiMessageDmsUimVerifyPinInput *input = NULL; + g_auto(GStrv) split = NULL; + QmiDmsUimPinId pin_id; + gchar *current_pin; + + /* Prepare inputs. + * Format of the string is: + * "[(PIN|PIN2),(current PIN)]" + */ + split = g_strsplit (str, ",", -1); + if (qmicli_read_dms_uim_pin_id_from_string (split[0], &pin_id) && + qmicli_read_non_empty_string (split[1], "current PIN", ¤t_pin)) { + GError *error = NULL; + + input = qmi_message_dms_uim_verify_pin_input_new (); + if (!qmi_message_dms_uim_verify_pin_input_set_info ( + input, + pin_id, + current_pin, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_uim_verify_pin_input_unref (input); + input = NULL; + } + } + + return input; +} + +static void +uim_verify_pin_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsUimVerifyPinOutput *output; + GError *error = NULL; + + output = qmi_client_dms_uim_verify_pin_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_dms_uim_verify_pin_output_get_result (output, &error)) { + guint8 verify_retries_left; + guint8 unblock_retries_left; + + g_printerr ("error: couldn't verify PIN: %s\n", error->message); + g_error_free (error); + + if (qmi_message_dms_uim_verify_pin_output_get_pin_retries_status ( + output, + &verify_retries_left, + &unblock_retries_left, + NULL)) { + g_printerr ("[%s] Retries left:\n" + "\tVerify: %u\n" + "\tUnblock: %u\n", + qmi_device_get_path_display (ctx->device), + verify_retries_left, + unblock_retries_left); + } + + qmi_message_dms_uim_verify_pin_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] PIN verified successfully\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_uim_verify_pin_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_UIM_VERIFY_PIN */ + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_UNBLOCK_PIN + +static QmiMessageDmsUimUnblockPinInput * +uim_unblock_pin_input_create (const gchar *str) +{ + QmiMessageDmsUimUnblockPinInput *input = NULL; + g_auto(GStrv) split = NULL; + QmiDmsUimPinId pin_id; + gchar *puk; + gchar *new_pin; + + /* Prepare inputs. + * Format of the string is: + * "[(PIN|PIN2),(PUK),(new PIN)]" + */ + split = g_strsplit (str, ",", -1); + if (qmicli_read_dms_uim_pin_id_from_string (split[0], &pin_id) && + qmicli_read_non_empty_string (split[1], "PUK", &puk) && + qmicli_read_non_empty_string (split[2], "new PIN", &new_pin)) { + GError *error = NULL; + + input = qmi_message_dms_uim_unblock_pin_input_new (); + if (!qmi_message_dms_uim_unblock_pin_input_set_info ( + input, + pin_id, + puk, + new_pin, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_uim_unblock_pin_input_unref (input); + input = NULL; + } + } + + return input; +} + +static void +uim_unblock_pin_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsUimUnblockPinOutput *output; + GError *error = NULL; + + output = qmi_client_dms_uim_unblock_pin_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_dms_uim_unblock_pin_output_get_result (output, &error)) { + guint8 verify_retries_left; + guint8 unblock_retries_left; + + g_printerr ("error: couldn't unblock PIN: %s\n", error->message); + g_error_free (error); + + if (qmi_message_dms_uim_unblock_pin_output_get_pin_retries_status ( + output, + &verify_retries_left, + &unblock_retries_left, + NULL)) { + g_printerr ("[%s] Retries left:\n" + "\tVerify: %u\n" + "\tUnblock: %u\n", + qmi_device_get_path_display (ctx->device), + verify_retries_left, + unblock_retries_left); + } + + qmi_message_dms_uim_unblock_pin_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] PIN unblocked successfully\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_uim_unblock_pin_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_UIM_UNBLOCK_PIN */ + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_CHANGE_PIN + +static QmiMessageDmsUimChangePinInput * +uim_change_pin_input_create (const gchar *str) +{ + QmiMessageDmsUimChangePinInput *input = NULL; + g_auto(GStrv) split = NULL; + QmiDmsUimPinId pin_id; + gchar *old_pin; + gchar *new_pin; + + /* Prepare inputs. + * Format of the string is: + * "[(PIN|PIN2),(old PIN),(new PIN)]" + */ + split = g_strsplit (str, ",", -1); + if (qmicli_read_dms_uim_pin_id_from_string (split[0], &pin_id) && + qmicli_read_non_empty_string (split[1], "old PIN", &old_pin) && + qmicli_read_non_empty_string (split[2], "new PIN", &new_pin)) { + GError *error = NULL; + + input = qmi_message_dms_uim_change_pin_input_new (); + if (!qmi_message_dms_uim_change_pin_input_set_info ( + input, + pin_id, + old_pin, + new_pin, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_uim_change_pin_input_unref (input); + input = NULL; + } + } + + return input; +} + +static void +uim_change_pin_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsUimChangePinOutput *output; + GError *error = NULL; + + output = qmi_client_dms_uim_change_pin_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_dms_uim_change_pin_output_get_result (output, &error)) { + guint8 verify_retries_left; + guint8 unblock_retries_left; + + g_printerr ("error: couldn't change PIN: %s\n", error->message); + g_error_free (error); + + if (qmi_message_dms_uim_change_pin_output_get_pin_retries_status ( + output, + &verify_retries_left, + &unblock_retries_left, + NULL)) { + g_printerr ("[%s] Retries left:\n" + "\tVerify: %u\n" + "\tUnblock: %u\n", + qmi_device_get_path_display (ctx->device), + verify_retries_left, + unblock_retries_left); + } + + qmi_message_dms_uim_change_pin_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] PIN changed successfully\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_uim_change_pin_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_UIM_CHANGE_PIN */ + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_GET_PIN_STATUS + +static void +uim_get_pin_status_ready (QmiClientDms *client, + GAsyncResult *res) +{ + guint8 verify_retries_left; + guint8 unblock_retries_left; + QmiDmsUimPinStatus current_status; + QmiMessageDmsUimGetPinStatusOutput *output; + GError *error = NULL; + + output = qmi_client_dms_uim_get_pin_status_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_dms_uim_get_pin_status_output_get_result (output, &error)) { + g_printerr ("error: couldn't get PIN status: %s\n", error->message); + g_error_free (error); + qmi_message_dms_uim_get_pin_status_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] PIN status retrieved successfully\n", + qmi_device_get_path_display (ctx->device)); + + if (qmi_message_dms_uim_get_pin_status_output_get_pin1_status ( + output, + ¤t_status, + &verify_retries_left, + &unblock_retries_left, + NULL)) { + g_print ("[%s] PIN1:\n" + "\tStatus: %s\n" + "\tVerify: %u\n" + "\tUnblock: %u\n", + qmi_device_get_path_display (ctx->device), + qmi_dms_uim_pin_status_get_string (current_status), + verify_retries_left, + unblock_retries_left); + } + + if (qmi_message_dms_uim_get_pin_status_output_get_pin2_status ( + output, + ¤t_status, + &verify_retries_left, + &unblock_retries_left, + NULL)) { + g_print ("[%s] PIN2:\n" + "\tStatus: %s\n" + "\tVerify: %u\n" + "\tUnblock: %u\n", + qmi_device_get_path_display (ctx->device), + qmi_dms_uim_pin_status_get_string (current_status), + verify_retries_left, + unblock_retries_left); + } + + qmi_message_dms_uim_get_pin_status_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_UIM_GET_PIN_STATUS */ + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_GET_ICCID + +static void +uim_get_iccid_ready (QmiClientDms *client, + GAsyncResult *res) +{ + const gchar *str = NULL; + QmiMessageDmsUimGetIccidOutput *output; + GError *error = NULL; + + output = qmi_client_dms_uim_get_iccid_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_dms_uim_get_iccid_output_get_result (output, &error)) { + g_printerr ("error: couldn't get ICCID: %s\n", error->message); + g_error_free (error); + qmi_message_dms_uim_get_iccid_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_uim_get_iccid_output_get_iccid (output, &str, NULL); + + g_print ("[%s] UIM ICCID retrieved:\n" + "\tICCID: '%s'\n", + qmi_device_get_path_display (ctx->device), + VALIDATE_UNKNOWN (str)); + + qmi_message_dms_uim_get_iccid_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_UIM_GET_ICCID */ + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_GET_IMSI + +static void +uim_get_imsi_ready (QmiClientDms *client, + GAsyncResult *res) +{ + const gchar *str = NULL; + QmiMessageDmsUimGetImsiOutput *output; + GError *error = NULL; + + output = qmi_client_dms_uim_get_imsi_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_dms_uim_get_imsi_output_get_result (output, &error)) { + g_printerr ("error: couldn't get IMSI: %s\n", error->message); + g_error_free (error); + qmi_message_dms_uim_get_imsi_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_uim_get_imsi_output_get_imsi (output, &str, NULL); + + g_print ("[%s] UIM IMSI retrieved:\n" + "\tIMSI: '%s'\n", + qmi_device_get_path_display (ctx->device), + VALIDATE_UNKNOWN (str)); + + qmi_message_dms_uim_get_imsi_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_UIM_GET_IMSI */ + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_GET_STATE + +static void +uim_get_state_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiDmsUimState state; + QmiMessageDmsUimGetStateOutput *output; + GError *error = NULL; + + output = qmi_client_dms_uim_get_state_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_dms_uim_get_state_output_get_result (output, &error)) { + g_printerr ("error: couldn't get UIM state: %s\n", error->message); + g_error_free (error); + qmi_message_dms_uim_get_state_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_uim_get_state_output_get_state (output, &state, NULL); + + g_print ("[%s] UIM state retrieved:\n" + "\tState: '%s'\n", + qmi_device_get_path_display (ctx->device), + qmi_dms_uim_state_get_string (state)); + + qmi_message_dms_uim_get_state_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_UIM_GET_STATE */ + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_GET_CK_STATUS + +static QmiMessageDmsUimGetCkStatusInput * +uim_get_ck_status_input_create (const gchar *str) +{ + QmiMessageDmsUimGetCkStatusInput *input = NULL; + QmiDmsUimFacility facility; + + if (qmicli_read_dms_uim_facility_from_string (str, &facility)) { + GError *error = NULL; + + input = qmi_message_dms_uim_get_ck_status_input_new (); + if (!qmi_message_dms_uim_get_ck_status_input_set_facility ( + input, + facility, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_uim_get_ck_status_input_unref (input); + input = NULL; + } + } + + return input; +} + +static void +uim_get_ck_status_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsUimGetCkStatusOutput *output; + GError *error = NULL; + QmiDmsUimFacilityState state; + guint8 verify_retries_left; + guint8 unblock_retries_left; + gboolean blocking; + + output = qmi_client_dms_uim_get_ck_status_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_dms_uim_get_ck_status_output_get_result (output, &error)) { + g_printerr ("error: couldn't get UIM CK status: %s\n", error->message); + g_error_free (error); + qmi_message_dms_uim_get_ck_status_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_uim_get_ck_status_output_get_ck_status ( + output, + &state, + &verify_retries_left, + &unblock_retries_left, + NULL); + + g_print ("[%s] UIM facility state retrieved:\n" + "\tState: '%s'\n", + qmi_device_get_path_display (ctx->device), + qmi_dms_uim_facility_state_get_string (state)); + g_print ("[%s] Retries left:\n" + "\tVerify: %u\n" + "\tUnblock: %u\n", + qmi_device_get_path_display (ctx->device), + verify_retries_left, + unblock_retries_left); + + if (qmi_message_dms_uim_get_ck_status_output_get_operation_blocking_facility ( + output, + &blocking, + NULL) && + blocking) { + g_print ("[%s] Facility is blocking operation\n", + qmi_device_get_path_display (ctx->device)); + } + + qmi_message_dms_uim_get_ck_status_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_UIM_GET_CK_STATUS */ + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_SET_CK_PROTECTION + +static QmiMessageDmsUimSetCkProtectionInput * +uim_set_ck_protection_input_create (const gchar *str) +{ + QmiMessageDmsUimSetCkProtectionInput *input = NULL; + g_auto(GStrv) split = NULL; + QmiDmsUimFacility facility; + gboolean enable_disable; + gchar *key; + + /* Prepare inputs. + * Format of the string is: + * "[(facility),disable,(key)]" + */ + split = g_strsplit (str, ",", -1); + if (qmicli_read_dms_uim_facility_from_string (split[0], &facility) && + qmicli_read_enable_disable_from_string (split[1], &enable_disable) && + qmicli_read_non_empty_string (split[2], "control key", &key)) { + + /* We should only allow 'disable' here */ + if (enable_disable) { + g_printerr ("error: only 'disable' action is allowed\n"); + } else { + GError *error = NULL; + + input = qmi_message_dms_uim_set_ck_protection_input_new (); + if (!qmi_message_dms_uim_set_ck_protection_input_set_facility ( + input, + facility, + (QmiDmsUimFacilityState)enable_disable, /* 0 == DISABLE */ + key, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_uim_set_ck_protection_input_unref (input); + input = NULL; + } + } + } + + return input; +} + +static void +uim_set_ck_protection_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsUimSetCkProtectionOutput *output; + GError *error = NULL; + + output = qmi_client_dms_uim_set_ck_protection_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_dms_uim_set_ck_protection_output_get_result (output, &error)) { + guint8 verify_retries_left; + + g_printerr ("error: couldn't set UIM CK protection: %s\n", error->message); + g_error_free (error); + + if (qmi_message_dms_uim_set_ck_protection_output_get_verify_retries_left ( + output, + &verify_retries_left, + NULL)) { + g_printerr ("[%s] Retries left:\n" + "\tVerify: %u\n", + qmi_device_get_path_display (ctx->device), + verify_retries_left); + } + + qmi_message_dms_uim_set_ck_protection_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] UIM CK protection set\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_uim_set_ck_protection_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_UIM_SET_CK_PROTECTION */ + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_UNBLOCK_CK + +static QmiMessageDmsUimUnblockCkInput * +uim_unblock_ck_input_create (const gchar *str) +{ + QmiMessageDmsUimUnblockCkInput *input = NULL; + g_auto(GStrv) split = NULL; + QmiDmsUimFacility facility; + gchar *key; + + /* Prepare inputs. + * Format of the string is: + * "[(facility),(key)]" + */ + split = g_strsplit (str, ",", -1); + if (qmicli_read_dms_uim_facility_from_string (split[0], &facility) && + qmicli_read_non_empty_string (split[1], "control key", &key)) { + GError *error = NULL; + + input = qmi_message_dms_uim_unblock_ck_input_new (); + if (!qmi_message_dms_uim_unblock_ck_input_set_facility ( + input, + facility, + key, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_uim_unblock_ck_input_unref (input); + input = NULL; + } + } + + return input; +} + +static void +uim_unblock_ck_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsUimUnblockCkOutput *output; + GError *error = NULL; + + output = qmi_client_dms_uim_unblock_ck_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_dms_uim_unblock_ck_output_get_result (output, &error)) { + guint8 unblock_retries_left; + + g_printerr ("error: couldn't unblock CK: %s\n", error->message); + g_error_free (error); + + if (qmi_message_dms_uim_unblock_ck_output_get_unblock_retries_left ( + output, + &unblock_retries_left, + NULL)) { + g_printerr ("[%s] Retries left:\n" + "\tUnblock: %u\n", + qmi_device_get_path_display (ctx->device), + unblock_retries_left); + } + + qmi_message_dms_uim_unblock_ck_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] UIM CK unblocked\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_uim_unblock_ck_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_UIM_UNBLOCK_CK */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_HARDWARE_REVISION + +static void +get_hardware_revision_ready (QmiClientDms *client, + GAsyncResult *res) +{ + const gchar *str = NULL; + QmiMessageDmsGetHardwareRevisionOutput *output; + GError *error = NULL; + + output = qmi_client_dms_get_hardware_revision_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_dms_get_hardware_revision_output_get_result (output, &error)) { + g_printerr ("error: couldn't get the HW revision: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_hardware_revision_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_hardware_revision_output_get_revision (output, &str, NULL); + + g_print ("[%s] Hardware revision retrieved:\n" + "\tRevision: '%s'\n", + qmi_device_get_path_display (ctx->device), + VALIDATE_UNKNOWN (str)); + + qmi_message_dms_get_hardware_revision_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_HARDWARE_REVISION */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_OPERATING_MODE + +static void +get_operating_mode_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsGetOperatingModeOutput *output; + QmiDmsOperatingMode mode; + gboolean hw_restricted = FALSE; + GError *error = NULL; + + output = qmi_client_dms_get_operating_mode_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_dms_get_operating_mode_output_get_result (output, &error)) { + g_printerr ("error: couldn't get the HW revision: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_operating_mode_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_operating_mode_output_get_mode (output, &mode, NULL); + + g_print ("[%s] Operating mode retrieved:\n" + "\tMode: '%s'\n", + qmi_device_get_path_display (ctx->device), + qmi_dms_operating_mode_get_string (mode)); + + if (mode == QMI_DMS_OPERATING_MODE_OFFLINE || mode == QMI_DMS_OPERATING_MODE_LOW_POWER) { + QmiDmsOfflineReason reason; + + if (qmi_message_dms_get_operating_mode_output_get_offline_reason (output, &reason, NULL)) { + g_autofree gchar *reason_str = NULL; + + reason_str = qmi_dms_offline_reason_build_string_from_mask (reason); + g_print ("\tReason: '%s'\n", VALIDATE_MASK_NONE (reason_str)); + } + } + + qmi_message_dms_get_operating_mode_output_get_hardware_restricted_mode (output, &hw_restricted, NULL); + g_print ("\tHW restricted: '%s'\n", hw_restricted ? "yes" : "no"); + + qmi_message_dms_get_operating_mode_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_OPERATING_MODE */ + +#if defined HAVE_QMI_MESSAGE_DMS_SET_OPERATING_MODE + +static QmiMessageDmsSetOperatingModeInput * +set_operating_mode_input_create (const gchar *str) +{ + QmiMessageDmsSetOperatingModeInput *input = NULL; + QmiDmsOperatingMode mode; + + if (qmicli_read_dms_operating_mode_from_string (str, &mode)) { + GError *error = NULL; + + input = qmi_message_dms_set_operating_mode_input_new (); + if (!qmi_message_dms_set_operating_mode_input_set_mode ( + input, + mode, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_set_operating_mode_input_unref (input); + input = NULL; + } + } + + return input; +} + +static void +set_operating_mode_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsSetOperatingModeOutput *output; + GError *error = NULL; + + output = qmi_client_dms_set_operating_mode_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_dms_set_operating_mode_output_get_result (output, &error)) { + g_printerr ("error: couldn't set operating mode: %s\n", error->message); + g_error_free (error); + qmi_message_dms_set_operating_mode_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Operating mode set successfully\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_set_operating_mode_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_SET_OPERATING_MODE */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_TIME + +static void +get_time_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsGetTimeOutput *output; + guint64 time_count; + QmiDmsTimeSource time_source; + GError *error = NULL; + gchar *str; + GTimeZone *time_zone; + GDateTime *gpstime_epoch; + GDateTime *computed_epoch; + + output = qmi_client_dms_get_time_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_dms_get_time_output_get_result (output, &error)) { + g_printerr ("error: couldn't get the device time: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_time_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_time_output_get_device_time ( + output, + &time_count, + &time_source, + NULL); + + /* January 6th 1980 */ + time_zone = g_time_zone_new_utc (); + gpstime_epoch = g_date_time_new (time_zone, 1980, 1, 6, 0, 0, 0.0); + + computed_epoch = g_date_time_add_seconds (gpstime_epoch, ((gdouble) time_count / 1000.0)); + str = g_date_time_format (computed_epoch, "%F %T"); + g_print ("[%s] Time retrieved:\n" + "\tTime count: '%" G_GUINT64_FORMAT " (x 1.25ms): %s'\n" + "\tTime source: '%s'\n", + qmi_device_get_path_display (ctx->device), + time_count, str, + qmi_dms_time_source_get_string (time_source)); + g_free (str); + g_date_time_unref (computed_epoch); + + if (qmi_message_dms_get_time_output_get_system_time ( + output, + &time_count, + NULL)){ + computed_epoch = g_date_time_add_seconds (gpstime_epoch, ((gdouble) time_count / 1000.0)); + str = g_date_time_format (computed_epoch, "%F %T"); + g_print ("\tSystem time: '%" G_GUINT64_FORMAT " (ms): %s'\n", + time_count, str); + g_free (str); + g_date_time_unref (computed_epoch); + } + + if (qmi_message_dms_get_time_output_get_user_time ( + output, + &time_count, + NULL)){ + computed_epoch = g_date_time_add_seconds (gpstime_epoch, ((gdouble) time_count / 1000.0)); + str = g_date_time_format (computed_epoch, "%F %T"); + g_print ("\tUser time: '%" G_GUINT64_FORMAT " (ms): %s'\n", + time_count, str); + g_free (str); + g_date_time_unref (computed_epoch); + } + + g_date_time_unref (gpstime_epoch); + g_time_zone_unref (time_zone); + + qmi_message_dms_get_time_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_TIME */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_PRL_VERSION + +static void +get_prl_version_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsGetPrlVersionOutput *output; + guint16 prl_version; + gboolean prl_only; + GError *error = NULL; + + output = qmi_client_dms_get_prl_version_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_dms_get_prl_version_output_get_result (output, &error)) { + g_printerr ("error: couldn't get the PRL version: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_prl_version_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_prl_version_output_get_version ( + output, + &prl_version, + NULL); + + g_print ("[%s] PRL version retrieved:\n" + "\tPRL version: '%" G_GUINT16_FORMAT "'\n", + qmi_device_get_path_display (ctx->device), + prl_version); + + if (qmi_message_dms_get_prl_version_output_get_prl_only_preference ( + output, + &prl_only, + NULL)){ + g_print ("\tPRL only preference: '%s'\n", + prl_only ? "yes" : "no"); + } + + qmi_message_dms_get_prl_version_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_PRL_VERSION */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_ACTIVATION_STATE + +static void +get_activation_state_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsGetActivationStateOutput *output; + QmiDmsActivationState activation_state; + GError *error = NULL; + + output = qmi_client_dms_get_activation_state_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_dms_get_activation_state_output_get_result (output, &error)) { + g_printerr ("error: couldn't get the state of the service activation: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_activation_state_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_activation_state_output_get_info ( + output, + &activation_state, + NULL); + + g_print ("[%s] Activation state retrieved:\n" + "\tState: '%s'\n", + qmi_device_get_path_display (ctx->device), + qmi_dms_activation_state_get_string (activation_state)); + + qmi_message_dms_get_activation_state_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_ACTIVATION_STATE */ + +#if defined HAVE_QMI_MESSAGE_DMS_ACTIVATE_MANUAL + +static QmiMessageDmsActivateManualInput * +activate_manual_input_create (const gchar *str) +{ + QmiMessageDmsActivateManualInput *input; + g_auto(GStrv) split = NULL; + GError *error = NULL; + gulong split_1_int; + + split = g_strsplit (str, ",", -1); + if (g_strv_length (split) != 4) { + g_printerr ("error: incorrect number of arguments given\n"); + return NULL; + } + + split_1_int = strtoul (split[1], NULL, 10); + if (split_1_int > G_MAXUINT16) { + g_printerr ("error: invalid SID given '%s'\n", + split[1]); + return NULL; + } + + input = qmi_message_dms_activate_manual_input_new (); + if (!qmi_message_dms_activate_manual_input_set_info ( + input, + split[0], + (guint16)split_1_int, + split[2], + split[3], + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_activate_manual_input_unref (input); + input = NULL; + } + + return input; +} + +static void +activate_manual_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsActivateManualOutput *output; + GError *error = NULL; + + output = qmi_client_dms_activate_manual_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_dms_activate_manual_output_get_result (output, &error)) { + g_printerr ("error: couldn't request manual service activation: %s\n", error->message); + g_error_free (error); + qmi_message_dms_activate_manual_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_activate_manual_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_ACTIVATE_MANUAL */ + +#if defined HAVE_QMI_MESSAGE_DMS_ACTIVATE_AUTOMATIC + +static QmiMessageDmsActivateAutomaticInput * +activate_automatic_input_create (const gchar *str) +{ + QmiMessageDmsActivateAutomaticInput *input; + GError *error = NULL; + + input = qmi_message_dms_activate_automatic_input_new (); + if (!qmi_message_dms_activate_automatic_input_set_activation_code ( + input, + str, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_activate_automatic_input_unref (input); + input = NULL; + } + + return input; +} + +static void +activate_automatic_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsActivateAutomaticOutput *output; + GError *error = NULL; + + output = qmi_client_dms_activate_automatic_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_dms_activate_automatic_output_get_result (output, &error)) { + g_printerr ("error: couldn't request automatic service activation: %s\n", error->message); + g_error_free (error); + qmi_message_dms_activate_automatic_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_activate_automatic_output_unref (output); + operation_shutdown (TRUE); +} + +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_USER_LOCK_STATE + +static void +get_user_lock_state_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsGetUserLockStateOutput *output; + gboolean enabled; + GError *error = NULL; + + output = qmi_client_dms_get_user_lock_state_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_dms_get_user_lock_state_output_get_result (output, &error)) { + g_printerr ("error: couldn't get the state of the user lock: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_user_lock_state_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_user_lock_state_output_get_enabled ( + output, + &enabled, + NULL); + + g_print ("[%s] User lock state retrieved:\n" + "\tEnabled: '%s'\n", + qmi_device_get_path_display (ctx->device), + enabled ? "yes" : "no"); + + qmi_message_dms_get_user_lock_state_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_USER_LOCK_STATE */ + +#if defined HAVE_QMI_MESSAGE_DMS_SET_USER_LOCK_STATE + +static QmiMessageDmsSetUserLockStateInput * +set_user_lock_state_input_create (const gchar *str) +{ + QmiMessageDmsSetUserLockStateInput *input = NULL; + g_auto(GStrv) split = NULL; + gboolean enable_disable; + gchar *code; + + /* Prepare inputs. + * Format of the string is: + * "[(disable|enable),(current lock code)]" + */ + split = g_strsplit (str, ",", -1); + + if (qmicli_read_enable_disable_from_string (split[0], &enable_disable) && + qmicli_read_non_empty_string (split[1], "current lock code", &code)) { + GError *error = NULL; + + input = qmi_message_dms_set_user_lock_state_input_new (); + if (!qmi_message_dms_set_user_lock_state_input_set_info ( + input, + enable_disable, + code, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_set_user_lock_state_input_unref (input); + input = NULL; + } + } + + return input; +} + +static void +set_user_lock_state_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsSetUserLockStateOutput *output; + GError *error = NULL; + + output = qmi_client_dms_set_user_lock_state_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_dms_set_user_lock_state_output_get_result (output, &error)) { + g_printerr ("error: couldn't set state of the user lock: %s\n", error->message); + g_error_free (error); + qmi_message_dms_set_user_lock_state_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] User lock state updated\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_set_user_lock_state_output_unref (output); + operation_shutdown (TRUE); +} + +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_SET_USER_LOCK_CODE + +static QmiMessageDmsSetUserLockCodeInput * +set_user_lock_code_input_create (const gchar *str) +{ + QmiMessageDmsSetUserLockCodeInput *input = NULL; + g_auto(GStrv) split = NULL; + gchar *old_code; + gchar *new_code; + + /* Prepare inputs. + * Format of the string is: + * "[(old lock code),(new lock code)]" + */ + split = g_strsplit (str, ",", -1); + if (qmicli_read_non_empty_string (split[0], "old lock code", &old_code) && + qmicli_read_non_empty_string (split[1], "new lock code", &new_code)) { + GError *error = NULL; + + input = qmi_message_dms_set_user_lock_code_input_new (); + if (!qmi_message_dms_set_user_lock_code_input_set_info ( + input, + old_code, + new_code, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_set_user_lock_code_input_unref (input); + input = NULL; + } + } + + return input; +} + +static void +set_user_lock_code_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsSetUserLockCodeOutput *output; + GError *error = NULL; + + output = qmi_client_dms_set_user_lock_code_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_dms_set_user_lock_code_output_get_result (output, &error)) { + g_printerr ("error: couldn't change user lock code: %s\n", error->message); + g_error_free (error); + qmi_message_dms_set_user_lock_code_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] User lock code changed\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_set_user_lock_code_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_SET_USER_LOCK_CODE */ + +#if defined HAVE_QMI_MESSAGE_DMS_READ_USER_DATA + +static void +read_user_data_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsReadUserDataOutput *output; + GArray *user_data = NULL; + gchar *user_data_printable; + GError *error = NULL; + + output = qmi_client_dms_read_user_data_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_dms_read_user_data_output_get_result (output, &error)) { + g_printerr ("error: couldn't read user data: %s\n", error->message); + g_error_free (error); + qmi_message_dms_read_user_data_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_read_user_data_output_get_user_data ( + output, + &user_data, + NULL); + user_data_printable = qmicli_get_raw_data_printable (user_data, 80, "\t\t"); + + g_print ("[%s] User data read:\n" + "\tSize: '%u' bytes\n" + "\tContents:\n" + "%s", + qmi_device_get_path_display (ctx->device), + user_data->len, + user_data_printable); + g_free (user_data_printable); + + qmi_message_dms_read_user_data_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_READ_USER_DATA */ + +#if defined HAVE_QMI_MESSAGE_DMS_WRITE_USER_DATA + +static QmiMessageDmsWriteUserDataInput * +write_user_data_input_create (const gchar *str) +{ + QmiMessageDmsWriteUserDataInput *input; + GArray *array; + GError *error = NULL; + + /* Prepare inputs. Just assume we'll get some text string here, although + * nobody said this had to be text. Read User Data actually treats the + * contents of the user data as raw binary data. */ + array = g_array_sized_new (FALSE, FALSE, 1, strlen (str)); + g_array_insert_vals (array, 0, str, strlen (str)); + input = qmi_message_dms_write_user_data_input_new (); + if (!qmi_message_dms_write_user_data_input_set_user_data ( + input, + array, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_write_user_data_input_unref (input); + input = NULL; + } + g_array_unref (array); + + return input; +} + +static void +write_user_data_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsWriteUserDataOutput *output; + GError *error = NULL; + + output = qmi_client_dms_write_user_data_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_dms_write_user_data_output_get_result (output, &error)) { + g_printerr ("error: couldn't write user data: %s\n", error->message); + g_error_free (error); + qmi_message_dms_write_user_data_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] User data written", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_write_user_data_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_WRITE_USER_DATA */ + +#if defined HAVE_QMI_MESSAGE_DMS_READ_ERI_FILE + +static void +read_eri_file_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsReadEriFileOutput *output; + GArray *eri_file = NULL; + gchar *eri_file_printable; + GError *error = NULL; + + output = qmi_client_dms_read_eri_file_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_dms_read_eri_file_output_get_result (output, &error)) { + g_printerr ("error: couldn't read eri file: %s\n", error->message); + g_error_free (error); + qmi_message_dms_read_eri_file_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_read_eri_file_output_get_eri_file ( + output, + &eri_file, + NULL); + eri_file_printable = qmicli_get_raw_data_printable (eri_file, 80, "\t\t"); + + g_print ("[%s] ERI file read:\n" + "\tSize: '%u' bytes\n" + "\tContents:\n" + "%s", + qmi_device_get_path_display (ctx->device), + eri_file->len, + eri_file_printable); + g_free (eri_file_printable); + + qmi_message_dms_read_eri_file_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_READ_ERI_FILE */ + +#if defined HAVE_QMI_MESSAGE_DMS_RESTORE_FACTORY_DEFAULTS + +static QmiMessageDmsRestoreFactoryDefaultsInput * +restore_factory_defaults_input_create (const gchar *str) +{ + QmiMessageDmsRestoreFactoryDefaultsInput *input; + GError *error = NULL; + + input = qmi_message_dms_restore_factory_defaults_input_new (); + if (!qmi_message_dms_restore_factory_defaults_input_set_service_programming_code ( + input, + str, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_restore_factory_defaults_input_unref (input); + input = NULL; + } + + return input; +} + +static void +restore_factory_defaults_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsRestoreFactoryDefaultsOutput *output; + GError *error = NULL; + + output = qmi_client_dms_restore_factory_defaults_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_dms_restore_factory_defaults_output_get_result (output, &error)) { + g_printerr ("error: couldn't restores factory defaults: %s\n", error->message); + g_error_free (error); + qmi_message_dms_restore_factory_defaults_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Factory defaults restored\n" + "Device needs to get power-cycled for reset to take effect.\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_restore_factory_defaults_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_RESTORE_FACTORY_DEFAULTS */ + +#if defined HAVE_QMI_MESSAGE_DMS_VALIDATE_SERVICE_PROGRAMMING_CODE + +static QmiMessageDmsValidateServiceProgrammingCodeInput * +validate_service_programming_code_input_create (const gchar *str) +{ + QmiMessageDmsValidateServiceProgrammingCodeInput *input; + GError *error = NULL; + + input = qmi_message_dms_validate_service_programming_code_input_new (); + if (!qmi_message_dms_validate_service_programming_code_input_set_service_programming_code ( + input, + str, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_validate_service_programming_code_input_unref (input); + input = NULL; + } + + return input; +} + +static void +validate_service_programming_code_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsValidateServiceProgrammingCodeOutput *output; + GError *error = NULL; + + output = qmi_client_dms_validate_service_programming_code_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_dms_validate_service_programming_code_output_get_result (output, &error)) { + g_printerr ("error: couldn't validate Service Programming Code: %s\n", error->message); + g_error_free (error); + qmi_message_dms_validate_service_programming_code_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Service Programming Code validated\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_validate_service_programming_code_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_VALIDATE_SERVICE_PROGRAMMING_CODE */ + +#if defined HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_ID + +static void +set_firmware_id_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsSetFirmwareIdOutput *output; + GError *error = NULL; + + output = qmi_client_dms_set_firmware_id_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_dms_set_firmware_id_output_get_result (output, &error)) { + g_printerr ("error: couldn't set firmware id: %s\n", error->message); + g_error_free (error); + qmi_message_dms_set_firmware_id_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Firmware id set\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_set_firmware_id_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_ID */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_BAND_CAPABILITIES + +static void +get_band_capabilities_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsGetBandCapabilitiesOutput *output; + QmiDmsBandCapability band_capability; + QmiDmsLteBandCapability lte_band_capability; + GArray *extended_lte_band_capability; + GArray *nr5g_band_capability; + GError *error = NULL; + + output = qmi_client_dms_get_band_capabilities_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_dms_get_band_capabilities_output_get_result (output, &error)) { + g_printerr ("error: couldn't get band capabilities: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_band_capabilities_output_unref (output); + operation_shutdown (FALSE); + return; + } + + if (qmi_message_dms_get_band_capabilities_output_get_band_capability ( + output, + &band_capability, + NULL)) { + g_autofree gchar *str = NULL; + + str = qmi_dms_band_capability_build_string_from_mask (band_capability); + g_print ("[%s] Device band capabilities retrieved:\n" + "\tBands: '%s'\n", + qmi_device_get_path_display (ctx->device), + VALIDATE_MASK_NONE (str)); + } + + if (qmi_message_dms_get_band_capabilities_output_get_lte_band_capability ( + output, + <e_band_capability, + NULL)) { + g_autofree gchar *str = NULL; + + str = qmi_dms_lte_band_capability_build_string_from_mask (lte_band_capability); + g_print ("\tLTE bands: '%s'\n", VALIDATE_MASK_NONE (str)); + } + + if (qmi_message_dms_get_band_capabilities_output_get_extended_lte_band_capability ( + output, + &extended_lte_band_capability, + NULL)) { + guint i; + + g_print ("\tLTE bands (extended): '"); + for (i = 0; i < extended_lte_band_capability->len; i++) + g_print ("%s%" G_GUINT16_FORMAT, + i == 0 ? "" : ", ", + g_array_index (extended_lte_band_capability, guint16, i)); + g_print ("'\n"); + } + + if (qmi_message_dms_get_band_capabilities_output_get_nr5g_band_capability ( + output, + &nr5g_band_capability, + NULL)) { + guint i; + + g_print ("\tNR5G bands: '"); + for (i = 0; i < nr5g_band_capability->len; i++) + g_print ("%s%" G_GUINT16_FORMAT, + i == 0 ? "" : ", ", + g_array_index (nr5g_band_capability, guint16, i)); + g_print ("'\n"); + } + + qmi_message_dms_get_band_capabilities_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_BAND_CAPABILITIES */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_FACTORY_SKU + +static void +get_factory_sku_ready (QmiClientDms *client, + GAsyncResult *res) +{ + const gchar *str = NULL; + QmiMessageDmsGetFactorySkuOutput *output; + GError *error = NULL; + + output = qmi_client_dms_get_factory_sku_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_dms_get_factory_sku_output_get_result (output, &error)) { + g_printerr ("error: couldn't get factory SKU: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_factory_sku_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_factory_sku_output_get_sku (output, &str, NULL); + + g_print ("[%s] Device factory SKU retrieved:\n" + "\tSKU: '%s'\n", + qmi_device_get_path_display (ctx->device), + VALIDATE_UNKNOWN (str)); + + qmi_message_dms_get_factory_sku_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_FACTORY_SKU */ + +#if defined HAVE_QMI_MESSAGE_DMS_LIST_STORED_IMAGES && \ + defined HAVE_QMI_MESSAGE_DMS_GET_STORED_IMAGE_INFO + +typedef struct { + QmiMessageDmsListStoredImagesOutput *list_images_output; + guint i; + guint j; +} ListImagesContext; + +static void +list_images_context_free (ListImagesContext *operation_ctx) +{ + qmi_message_dms_list_stored_images_output_unref (operation_ctx->list_images_output); + g_slice_free (ListImagesContext, operation_ctx); +} + +static void get_image_info (ListImagesContext *operation_ctx); + +static void +print_image_info (ListImagesContext *operation_ctx, + QmiMessageDmsGetStoredImageInfoOutput *output) +{ + GArray *array; + QmiMessageDmsListStoredImagesOutputListImage *image; + QmiMessageDmsListStoredImagesOutputListImageSublistSublistElement *subimage; + g_autofree gchar *unique_id_str = NULL; + + qmi_message_dms_list_stored_images_output_get_list ( + operation_ctx->list_images_output, + &array, + NULL); + + image = &g_array_index (array, QmiMessageDmsListStoredImagesOutputListImage, operation_ctx->i); + subimage = &g_array_index (image->sublist, + QmiMessageDmsListStoredImagesOutputListImageSublistSublistElement, + operation_ctx->j); + + unique_id_str = qmicli_get_firmware_image_unique_id_printable (subimage->unique_id); + + g_print ("%s" + "\t\t[%s%u]\n" + "\t\tUnique ID: '%s'\n" + "\t\tBuild ID: '%s'\n", + operation_ctx->j == image->index_of_running_image ? "\t\t>>>>>>>>>> [CURRENT] <<<<<<<<<<\n" : "", + qmi_dms_firmware_image_type_get_string (image->type), + operation_ctx->j, + unique_id_str, + subimage->build_id); + + if (subimage->storage_index != 255) + g_print ("\t\tStorage index: '%u'\n", subimage->storage_index); + + if (subimage->failure_count != 255) + g_print ("\t\tFailure count: '%u'\n", subimage->failure_count); + + if (output) { + /* Boot version (optional) */ + { + guint16 boot_major_version; + guint16 boot_minor_version; + + if (qmi_message_dms_get_stored_image_info_output_get_boot_version ( + output, + &boot_major_version, + &boot_minor_version, + NULL)) { + g_print ("\t\tBoot version: '%u.%u'\n", + boot_major_version, + boot_minor_version); + } + } + + /* PRI version (optional) */ + { + guint32 pri_version; + const gchar *pri_info; + + if (qmi_message_dms_get_stored_image_info_output_get_pri_version ( + output, + &pri_version, + &pri_info, + NULL)) { + g_print ("\t\tPRI version: '%u'\n" + "\t\tPRI info: '%s'\n", + pri_version, + pri_info); + } + } + + /* OEM lock ID (optional) */ + { + guint32 lock_id; + + if (qmi_message_dms_get_stored_image_info_output_get_oem_lock_id ( + output, + &lock_id, + NULL)) { + g_print ("\t\tOEM lock ID: '%u'\n", + lock_id); + } + } + } + g_print ("\n"); +} + +static void +get_stored_image_info_ready (QmiClientDms *client, + GAsyncResult *res, + ListImagesContext *operation_ctx) +{ + + g_autoptr(QmiMessageDmsGetStoredImageInfoOutput) output = NULL; + + output = qmi_client_dms_get_stored_image_info_finish (client, res, NULL); + if (output && !qmi_message_dms_get_stored_image_info_output_get_result (output, NULL)) + g_clear_pointer (&output, qmi_message_dms_get_stored_image_info_output_unref); + + print_image_info (operation_ctx, output); + + /* Go on to the next one */ + operation_ctx->j++; + get_image_info (operation_ctx); +} + +static void +get_image_info (ListImagesContext *operation_ctx) +{ + GArray *array; + QmiMessageDmsListStoredImagesOutputListImage *image; + QmiMessageDmsListStoredImagesOutputListImageSublistSublistElement *subimage; + g_autoptr(QmiMessageDmsGetStoredImageInfoInput) input = NULL; + + qmi_message_dms_list_stored_images_output_get_list ( + operation_ctx->list_images_output, + &array, + NULL); + + if (operation_ctx->i >= array->len) { + /* We're done */ + list_images_context_free (operation_ctx); + operation_shutdown (TRUE); + return; + } + + image = &g_array_index (array, + QmiMessageDmsListStoredImagesOutputListImage, + operation_ctx->i); + + if (operation_ctx->j >= image->sublist->len) { + /* No more images in the sublist, go to next image type */ + operation_ctx->j = 0; + operation_ctx->i++; + get_image_info (operation_ctx); + return; + } + + /* Print info of the image type */ + if (operation_ctx->j == 0) { + g_print ("\t[%u] Type: '%s'\n" + "\t Maximum: '%u'\n" + "\n", + operation_ctx->i, + qmi_dms_firmware_image_type_get_string (image->type), + image->maximum_images); + } + + subimage = &g_array_index (image->sublist, + QmiMessageDmsListStoredImagesOutputListImageSublistSublistElement, + operation_ctx->j); + + /* Query image info */ + input = qmi_message_dms_get_stored_image_info_input_new (); + qmi_message_dms_get_stored_image_info_input_set_image_details (input, + image->type, + subimage->unique_id, + subimage->build_id, + NULL); + qmi_client_dms_get_stored_image_info (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_stored_image_info_ready, + operation_ctx); +} + +static void +list_stored_images_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsListStoredImagesOutput *output; + GError *error = NULL; + ListImagesContext *operation_ctx; + + output = qmi_client_dms_list_stored_images_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_dms_list_stored_images_output_get_result (output, &error)) { + g_printerr ("error: couldn't list stored images: %s\n", error->message); + g_error_free (error); + qmi_message_dms_list_stored_images_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Device list of stored images retrieved:\n\n", + qmi_device_get_path_display (ctx->device)); + + operation_ctx = g_slice_new0 (ListImagesContext); + operation_ctx->list_images_output = output; + operation_ctx->i = 0; + operation_ctx->j = 0; + + get_image_info (operation_ctx); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_LIST_STORED_IMAGES && HAVE_QMI_MESSAGE_DMS_GET_STORED_IMAGE_INFO */ + +#if defined HAVE_QMI_MESSAGE_DMS_LIST_STORED_IMAGES && \ + defined HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_PREFERENCE + +typedef struct { + gint modem_index; + gint pri_index; +} GetStoredImageContext; + +typedef struct { + GArray *modem_unique_id; + gchar *modem_build_id; + GArray *pri_unique_id; + gchar *pri_build_id; +} GetStoredImageResult; + +static void +get_stored_image_context_free (GetStoredImageContext *operation_ctx) +{ + g_slice_free (GetStoredImageContext, operation_ctx); +} + +static void +get_stored_image_result_free (GetStoredImageResult *result) +{ + if (result) { + g_clear_pointer (&result->modem_unique_id, g_array_unref); + g_free (result->modem_build_id); + g_clear_pointer (&result->pri_unique_id, g_array_unref); + g_free (result->pri_build_id); + g_slice_free (GetStoredImageResult, result); + } +} + +static void +get_stored_image_finish (QmiClientDms *client, + GAsyncResult *res, + GArray **modem_unique_id, + gchar **modem_build_id, + GArray **pri_unique_id, + gchar **pri_build_id) +{ + GetStoredImageResult *result; + GError *error = NULL; + + result = g_task_propagate_pointer (G_TASK (res), &error); + + /* The operation always returns a result */ + g_assert (result); + g_assert_no_error (error); + + /* Simply pass ownership to caller */ + *modem_unique_id = result->modem_unique_id; + *modem_build_id = result->modem_build_id; + *pri_unique_id = result->pri_unique_id; + *pri_build_id = result->pri_build_id; + + g_slice_free (GetStoredImageResult, result); +} + +static void +get_stored_image_list_stored_images_ready (QmiClientDms *client, + GAsyncResult *res, + GTask *task) +{ + GetStoredImageContext *operation_ctx; + GetStoredImageResult *result; + GArray *array; + QmiMessageDmsListStoredImagesOutput *output; + GError *error = NULL; + guint i; + + output = qmi_client_dms_list_stored_images_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + g_error_free (error); + g_object_unref (task); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_dms_list_stored_images_output_get_result (output, &error)) { + g_printerr ("error: couldn't list stored images: %s\n", error->message); + g_error_free (error); + qmi_message_dms_list_stored_images_output_unref (output); + g_object_unref (task); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_list_stored_images_output_get_list ( + output, + &array, + NULL); + + operation_ctx = g_task_get_task_data (task); + + /* A single result struct is used for all iterations */ + result = g_slice_new0 (GetStoredImageResult); + + for (i = 0; i < array->len; i++) { + QmiMessageDmsListStoredImagesOutputListImageSublistSublistElement *subimage; + QmiMessageDmsListStoredImagesOutputListImage *image; + gchar *unique_id_str; + gint image_index; + + image = &g_array_index (array, + QmiMessageDmsListStoredImagesOutputListImage, + i); + + if (image->type == QMI_DMS_FIRMWARE_IMAGE_TYPE_MODEM) + image_index = operation_ctx->modem_index; + else if (image->type == QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI) + image_index = operation_ctx->pri_index; + else + g_assert_not_reached (); + + /* If not looking for the specific image type, go on */ + if (image_index < 0) + continue; + + if ((guint)image_index >= image->sublist->len) { + g_printerr ("error: couldn't find '%s' image at index '%d'\n", + qmi_dms_firmware_image_type_get_string (image->type), + image_index); + qmi_message_dms_list_stored_images_output_unref (output); + g_object_unref (task); + operation_shutdown (FALSE); + get_stored_image_result_free (result); + return; + } + + subimage = &g_array_index (image->sublist, + QmiMessageDmsListStoredImagesOutputListImageSublistSublistElement, + image_index); + + unique_id_str = qmicli_get_firmware_image_unique_id_printable (subimage->unique_id); + g_debug ("Found [%s%d]: Unique ID: '%s', Build ID: '%s'", + qmi_dms_firmware_image_type_get_string (image->type), + image_index, + unique_id_str, + subimage->build_id); + g_free (unique_id_str); + + if (image->type == QMI_DMS_FIRMWARE_IMAGE_TYPE_MODEM) { + result->modem_unique_id = g_array_ref (subimage->unique_id); + result->modem_build_id = g_strdup (subimage->build_id); + } else if (image->type == QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI) { + result->pri_unique_id = g_array_ref (subimage->unique_id); + result->pri_build_id = g_strdup (subimage->build_id); + } else + g_assert_not_reached (); + } + + /* Complete */ + g_task_return_pointer (task, result, (GDestroyNotify)get_stored_image_result_free); + g_object_unref (task); + qmi_message_dms_list_stored_images_output_unref (output); +} + +static void +get_stored_image (QmiClientDms *client, + const gchar *str, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GetStoredImageContext *operation_ctx; + g_auto(GStrv) split = NULL; + GTask *task; + guint i = 0; + gint modem_index = -1; + gint pri_index = -1; + + split = g_strsplit (str, ",", -1); + while (split[i]) { + QmiDmsFirmwareImageType type; + guint image_index; + + if (i >= 3) { + g_printerr ("A maximum of 2 images should be given: '%s'\n", str); + operation_shutdown (FALSE); + return; + } + + if (!qmicli_read_firmware_id_from_string (split[i], &type, &image_index)) { + g_printerr ("Couldn't parse input string as firmware index info: '%s'\n", str); + operation_shutdown (FALSE); + return; + } + + if (type == QMI_DMS_FIRMWARE_IMAGE_TYPE_MODEM) { + if (modem_index >= 0) { + g_printerr ("Cannot handle two 'modem' type firmware indices: '%s'\n", str); + operation_shutdown (FALSE); + return; + } + modem_index = (gint)image_index; + } else if (type == QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI) { + if (pri_index >= 0) { + g_printerr ("Cannot handle two 'pri' type firmware indices: '%s'\n", str); + operation_shutdown (FALSE); + return; + } + pri_index = (gint)image_index; + } + + i++; + } + + operation_ctx = g_slice_new (GetStoredImageContext); + operation_ctx->modem_index = modem_index; + operation_ctx->pri_index = pri_index; + + task = g_task_new (client, NULL, callback, user_data); + g_task_set_task_data (task, operation_ctx, (GDestroyNotify)get_stored_image_context_free); + + qmi_client_dms_list_stored_images ( + ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_stored_image_list_stored_images_ready, + task); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_LIST_STORED_IMAGES + * HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_PREFERENCE */ + +#if defined HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_PREFERENCE + +/* Note: + * used by both --dms-set-firmware-preference and --dms-select-stored-image + * But only one single symbol check, as the select operation already + * requires SET_FIRMWARE_PREFERENCE. + */ +static void +dms_set_firmware_preference_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsSetFirmwarePreferenceOutput *output; + GError *error = NULL; + GArray *array; + GString *pending_images = NULL; + + output = qmi_client_dms_set_firmware_preference_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_dms_set_firmware_preference_output_get_result (output, &error)) { + g_printerr ("error: couldn't select stored image: %s\n", error->message); + g_error_free (error); + qmi_message_dms_set_firmware_preference_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Firmware preference successfully selected\n" + "\n" + "\tYou may want to power-cycle the modem now, or just set it offline and reset it:\n" + "\t\t$> sudo qmicli ... --dms-set-operating-mode=offline\n" + "\t\t$> sudo qmicli ... --dms-set-operating-mode=reset\n" + "\n", + qmi_device_get_path_display (ctx->device)); + + /* do we need to download a new modem and/or pri image? */ + if (qmi_message_dms_set_firmware_preference_output_get_image_download_list (output, &array, NULL) && array->len) { + guint i; + QmiDmsFirmwareImageType type; + + pending_images = g_string_new (""); + for (i = 0; i < array->len; i++) { + type = g_array_index (array, QmiDmsFirmwareImageType, i); + g_string_append (pending_images, qmi_dms_firmware_image_type_get_string (type)); + if (i < array->len -1) + g_string_append (pending_images, ", "); + } + } + + if (pending_images) { + g_print ("\tAfter reset, the modem will wait in QDL mode for new firmware.\n" + "\tImages to download: '%s'\n" + "\n", + pending_images->str); + g_string_free (pending_images, TRUE); + } else { + /* If we're selecting an already stored image, or if we don't need any + * more images to be downloaded, we're done. */ + g_print ("\tNo new images are required to be downloaded.\n" + "\n" + "\tYou should check that the modem|pri image pair is valid by checking the current operating mode:\n" + "\t\t$> sudo qmicli .... --dms-get-operating-mode\n" + "\tIf the Mode is reported as 'online', you're good to go.\n" + "\tIf the Mode is reported as 'offline' with a 'pri-version-incompatible' reason, you chose an incorrect pair\n" + "\n"); + } + + qmi_message_dms_set_firmware_preference_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_PREFERENCE */ + +#if defined HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_PREFERENCE && \ + defined HAVE_QMI_MESSAGE_DMS_LIST_STORED_IMAGES + +static void +get_stored_image_select_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsSetFirmwarePreferenceInput *input; + GArray *array; + QmiMessageDmsSetFirmwarePreferenceInputListImage modem_image_id; + QmiMessageDmsSetFirmwarePreferenceInputListImage pri_image_id; + + modem_image_id.type = QMI_DMS_FIRMWARE_IMAGE_TYPE_MODEM; + pri_image_id.type = QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI; + + get_stored_image_finish (client, + res, + &modem_image_id.unique_id, + &modem_image_id.build_id, + &pri_image_id.unique_id, + &pri_image_id.build_id); + + if (!modem_image_id.unique_id || !modem_image_id.build_id || + !pri_image_id.unique_id || !pri_image_id.build_id) { + g_printerr ("error: must specify a pair of 'modem' and 'pri' images to select\n"); + operation_shutdown (FALSE); + return; + } + + array = g_array_sized_new (FALSE, FALSE, sizeof (QmiMessageDmsSetFirmwarePreferenceInputListImage), 2); + g_array_append_val (array, modem_image_id); + g_array_append_val (array, pri_image_id); + + input = qmi_message_dms_set_firmware_preference_input_new (); + qmi_message_dms_set_firmware_preference_input_set_list (input, array, NULL); + + qmi_client_dms_set_firmware_preference ( + client, + input, + 10, + NULL, + (GAsyncReadyCallback)dms_set_firmware_preference_ready, + NULL); + qmi_message_dms_set_firmware_preference_input_unref (input); + + g_free (modem_image_id.build_id); + g_array_unref (modem_image_id.unique_id); + g_free (pri_image_id.build_id); + g_array_unref (pri_image_id.unique_id); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_PREFERENCE + * HAVE_QMI_MESSAGE_DMS_LIST_STORED_IMAGES */ + +#if defined HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_PREFERENCE && \ + defined HAVE_QMI_MESSAGE_DMS_LIST_STORED_IMAGES && \ + defined HAVE_QMI_MESSAGE_DMS_DELETE_STORED_IMAGE + +static void +delete_stored_image_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsDeleteStoredImageOutput *output; + GError *error = NULL; + + output = qmi_client_dms_delete_stored_image_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_dms_delete_stored_image_output_get_result (output, &error)) { + g_printerr ("error: couldn't delete stored image: %s\n", error->message); + g_error_free (error); + qmi_message_dms_delete_stored_image_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Stored image successfully deleted\n", + qmi_device_get_path_display (ctx->device)); + qmi_message_dms_delete_stored_image_output_unref (output); + operation_shutdown (TRUE); +} + +static void +get_stored_image_delete_ready (QmiClientDms *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageDmsDeleteStoredImageInput) input = NULL; + g_autoptr(GArray) modem_unique_id = NULL; + g_autofree gchar *modem_build_id = NULL; + g_autoptr(GArray) pri_unique_id = NULL; + g_autofree gchar *pri_build_id = NULL; + + get_stored_image_finish (client, + res, + &modem_unique_id, + &modem_build_id, + &pri_unique_id, + &pri_build_id); + + if (modem_unique_id && modem_build_id && + pri_unique_id && pri_build_id) { + g_printerr ("error: cannot specify multiple images to delete\n"); + operation_shutdown (FALSE); + return; + } + + input = qmi_message_dms_delete_stored_image_input_new (); + if (modem_unique_id && modem_build_id) + qmi_message_dms_delete_stored_image_input_set_image_details (input, + QMI_DMS_FIRMWARE_IMAGE_TYPE_MODEM, + modem_unique_id, + modem_build_id, + NULL); + else if (pri_unique_id && pri_build_id) + qmi_message_dms_delete_stored_image_input_set_image_details (input, + QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI, + pri_unique_id, + pri_build_id, + NULL); + else { + g_printerr ("error: didn't specify correctly an image to delete\n"); + operation_shutdown (FALSE); + return; + } + + qmi_client_dms_delete_stored_image ( + client, + input, + 10, + NULL, + (GAsyncReadyCallback)delete_stored_image_ready, + NULL); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_PREFERENCE + * HAVE_QMI_MESSAGE_DMS_LIST_STORED_IMAGES + * HAVE_QMI_MESSAGE_DMS_DELETE_STORED_IMAGE */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_FIRMWARE_PREFERENCE + +static void +dms_get_firmware_preference_ready (QmiClientDms *client, + GAsyncResult *res) +{ + GError *error = NULL; + QmiMessageDmsGetFirmwarePreferenceOutput *output; + GArray *array; + guint i; + + output = qmi_client_dms_get_firmware_preference_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_dms_get_firmware_preference_output_get_result (output, &error)) { + g_printerr ("error: couldn't get firmware preference: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_firmware_preference_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_firmware_preference_output_get_list (output, &array, NULL); + + g_print ("firmware preference successfully retrieved:\n"); + + if (array->len > 0) { + for (i = 0; i < array->len; i++) { + QmiMessageDmsGetFirmwarePreferenceOutputListImage *image; + gchar *unique_id_str; + + image = &g_array_index (array, QmiMessageDmsGetFirmwarePreferenceOutputListImage, i); + + unique_id_str = qmicli_get_firmware_image_unique_id_printable (image->unique_id); + + g_print ("[image %u]\n" + "\tImage type: '%s'\n" + "\tUnique ID: '%s'\n" + "\tBuild ID: '%s'\n", + i, + qmi_dms_firmware_image_type_get_string (image->type), + unique_id_str, + image->build_id); + + g_free (unique_id_str); + } + } else + g_print ("no images specified\n"); + + qmi_message_dms_get_firmware_preference_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_FIRMWARE_PREFERENCE */ + +#if defined HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_PREFERENCE + +typedef struct { + QmiMessageDmsSetFirmwarePreferenceInputListImage modem_image_id; + QmiMessageDmsSetFirmwarePreferenceInputListImage pri_image_id; +} SetFirmwarePreferenceContext; + +static void +set_firmware_preference_context_clear (SetFirmwarePreferenceContext *firmware_preference_ctx) +{ + g_clear_pointer (&firmware_preference_ctx->modem_image_id.unique_id, g_array_unref); + g_free (firmware_preference_ctx->modem_image_id.build_id); + + g_clear_pointer (&firmware_preference_ctx->pri_image_id.unique_id, g_array_unref); + g_free (firmware_preference_ctx->pri_image_id.build_id); +} + +typedef struct { + gchar *firmware_version; + gchar *config_version; + gchar *carrier; + gint modem_storage_index; + gboolean override_download; + gboolean override_download_set; +} SetFirmwarePreferenceProperties; + +static void +set_firmware_preference_properties_clear (SetFirmwarePreferenceProperties *props) +{ + g_free (props->firmware_version); + g_free (props->config_version); + g_free (props->carrier); +} + +static gboolean +set_firmware_preference_properties_handle (const gchar *key, + const gchar *value, + GError **error, + gpointer user_data) +{ + SetFirmwarePreferenceProperties *props = user_data; + + if (!value || !value[0]) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "key '%s' required a value", + key); + return FALSE; + } + + if (g_ascii_strcasecmp (key, "firmware-version") == 0 && !props->firmware_version) { + props->firmware_version = g_strdup (value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "config-version") == 0 && !props->config_version) { + props->config_version = g_strdup (value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "carrier") == 0 && !props->carrier) { + props->carrier = g_strdup (value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "override-download") == 0 && !props->override_download_set) { + if (!qmicli_read_yes_no_from_string (value, &(props->override_download))) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unknown override-download '%s'", + value); + return FALSE; + } + props->override_download_set = TRUE; + return TRUE; + } + + if (g_ascii_strcasecmp (key, "modem-storage-index") == 0 && props->modem_storage_index == -1) { + props->modem_storage_index = atoi (value); + if (props->modem_storage_index < 0 || props->modem_storage_index > G_MAXUINT8) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "invalid modem-storage-index '%s'", + value); + return FALSE; + } + return TRUE; + } + + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unrecognized or duplicate option '%s'", + key); + return FALSE; +} + +static QmiMessageDmsSetFirmwarePreferenceInput * +set_firmware_preference_input_create (const gchar *str, + SetFirmwarePreferenceContext *firmware_preference_ctx, + GError **error) +{ + g_autoptr(QmiMessageDmsSetFirmwarePreferenceInput) input = NULL; + g_autoptr(GArray) array = NULL; + + SetFirmwarePreferenceProperties props = { + .firmware_version = NULL, + .config_version = NULL, + .carrier = NULL, + .modem_storage_index = -1, + .override_download = FALSE, + .override_download_set = FALSE, + }; + + /* New key=value format */ + if (strchr (str, '=')) { + g_autoptr(GError) parse_error = NULL; + + if (!qmicli_parse_key_value_string (str, + &parse_error, + set_firmware_preference_properties_handle, + &props)) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "Couldn't parse input string: %s", parse_error->message); + set_firmware_preference_properties_clear (&props); + return NULL; + } + + if (!props.firmware_version || !props.config_version || !props.carrier) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "Missing mandatory parameters: 'firmware-version', 'config-version' and 'carrier' are mandatory"); + set_firmware_preference_properties_clear (&props); + return NULL; + } + } + /* Old non key=value format, like this: + * "[(firmware_version),(config_version),(carrier)]" + */ + else { + g_auto(GStrv) split = NULL; + + split = g_strsplit (str, ",", -1); + if (g_strv_length (split) != 3) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "Invalid format string, expected 3 elements: 'firmware-version', 'config-version' and 'carrier'"); + return NULL; + } + + props.firmware_version = g_strdup (split[0]); + props.config_version = g_strdup (split[1]); + props.carrier = g_strdup (split[2]); + } + + /* modem unique id is the fixed wildcard string '?_?' matching any pri. + * modem build id format is "(firmware_version)_?", matching any carrier */ + firmware_preference_ctx->modem_image_id.type = QMI_DMS_FIRMWARE_IMAGE_TYPE_MODEM; + firmware_preference_ctx->modem_image_id.unique_id = g_array_sized_new (FALSE, TRUE, 1, 16); + g_array_insert_vals (firmware_preference_ctx->modem_image_id.unique_id, 0, "?_?", 3); + g_array_set_size (firmware_preference_ctx->modem_image_id.unique_id, 16); + firmware_preference_ctx->modem_image_id.build_id = g_strdup_printf ("%s_?", props.firmware_version); + + /* pri unique id is the "(config_version)" input */ + firmware_preference_ctx->pri_image_id.type = QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI; + firmware_preference_ctx->pri_image_id.unique_id = g_array_sized_new (FALSE, TRUE, 1, 16); + g_array_insert_vals (firmware_preference_ctx->pri_image_id.unique_id, 0, props.config_version, strlen (props.config_version)); + g_array_set_size (firmware_preference_ctx->pri_image_id.unique_id, 16); + firmware_preference_ctx->pri_image_id.build_id = g_strdup_printf ("%s_%s", props.firmware_version, props.carrier); + + /* Create an array with both images, the contents of each image struct, + * though, aren't owned by the array (i.e. need to be disposed afterwards + * when no longer used). */ + array = g_array_sized_new (FALSE, FALSE, sizeof (QmiMessageDmsSetFirmwarePreferenceInputListImage), 2); + g_array_append_val (array, firmware_preference_ctx->modem_image_id); + g_array_append_val (array, firmware_preference_ctx->pri_image_id); + + /* The input bundle takes a reference to the array itself */ + input = qmi_message_dms_set_firmware_preference_input_new (); + qmi_message_dms_set_firmware_preference_input_set_list (input, array, NULL); + + /* Other optional settings */ + if (props.modem_storage_index >= 0) + qmi_message_dms_set_firmware_preference_input_set_modem_storage_index (input, (guint8)props.modem_storage_index, NULL); + if (props.override_download_set) + qmi_message_dms_set_firmware_preference_input_set_download_override (input, props.override_download, NULL); + + set_firmware_preference_properties_clear (&props); + return g_steal_pointer (&input); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_PREFERENCE */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_BOOT_IMAGE_DOWNLOAD_MODE + +static void +get_boot_image_download_mode_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsGetBootImageDownloadModeOutput *output; + GError *error = NULL; + QmiDmsBootImageDownloadMode mode; + + output = qmi_client_dms_get_boot_image_download_mode_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_dms_get_boot_image_download_mode_output_get_result (output, &error)) { + g_printerr ("error: couldn't get boot image download mode: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_boot_image_download_mode_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_boot_image_download_mode_output_get_mode (output, &mode, NULL); + + g_print ("[%s] Boot image download mode: %s\n", + qmi_device_get_path_display (ctx->device), + qmi_dms_boot_image_download_mode_get_string (mode)); + + qmi_message_dms_get_boot_image_download_mode_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_BOOT_IMAGE_DOWNLOAD_MODE */ + +#if defined HAVE_QMI_MESSAGE_DMS_SET_BOOT_IMAGE_DOWNLOAD_MODE + +static QmiMessageDmsSetBootImageDownloadModeInput * +set_boot_image_download_mode_input_create (const gchar *str) +{ + QmiMessageDmsSetBootImageDownloadModeInput *input = NULL; + QmiDmsBootImageDownloadMode mode; + GError *error = NULL; + + /* Prepare inputs. + * Format of the string is: + * [normal|boot-and-recovery] + */ + if (!qmicli_read_dms_boot_image_download_mode_from_string (str, &mode)) + return NULL; + + input = qmi_message_dms_set_boot_image_download_mode_input_new (); + if (!qmi_message_dms_set_boot_image_download_mode_input_set_mode (input, mode, &error)) { + g_printerr ("error: couldn't create input bundle: '%s'\n", error->message); + g_error_free (error); + qmi_message_dms_set_boot_image_download_mode_input_unref (input); + return NULL; + } + + return input; +} + +static void +set_boot_image_download_mode_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsSetBootImageDownloadModeOutput *output; + GError *error = NULL; + + output = qmi_client_dms_set_boot_image_download_mode_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_dms_set_boot_image_download_mode_output_get_result (output, &error)) { + g_printerr ("error: couldn't set boot image download mode: %s\n", error->message); + g_error_free (error); + qmi_message_dms_set_boot_image_download_mode_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Boot image download mode successfully set\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_set_boot_image_download_mode_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_SET_BOOT_IMAGE_DOWNLOAD_MODE */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_SOFTWARE_VERSION + +static void +get_software_version_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsGetSoftwareVersionOutput *output; + GError *error = NULL; + const gchar *version; + + output = qmi_client_dms_get_software_version_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_dms_get_software_version_output_get_result (output, &error)) { + g_printerr ("error: couldn't get boot image download mode: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_software_version_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_software_version_output_get_version (output, &version, NULL); + + g_print ("[%s] Software version: %s\n", + qmi_device_get_path_display (ctx->device), + version); + + qmi_message_dms_get_software_version_output_unref (output); + operation_shutdown (TRUE); +} + +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_SET_FCC_AUTHENTICATION + +static void +set_fcc_authentication_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsSetFccAuthenticationOutput *output; + GError *error = NULL; + + output = qmi_client_dms_set_fcc_authentication_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_dms_set_fcc_authentication_output_get_result (output, &error)) { + g_printerr ("error: couldn't set FCC authentication: %s\n", error->message); + g_error_free (error); + qmi_message_dms_set_fcc_authentication_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully set FCC authentication\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_set_fcc_authentication_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_SET_FCC_AUTHENTICATION */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_SUPPORTED_MESSAGES + +static void +get_supported_messages_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsGetSupportedMessagesOutput *output; + GError *error = NULL; + GArray *bytearray = NULL; + gchar *str; + + output = qmi_client_dms_get_supported_messages_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_dms_get_supported_messages_output_get_result (output, &error)) { + g_printerr ("error: couldn't get supported DMS messages: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_supported_messages_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got supported DMS messages:\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_get_supported_messages_output_get_list (output, &bytearray, NULL); + str = qmicli_get_supported_messages_list (bytearray ? (const guint8 *)bytearray->data : NULL, + bytearray ? bytearray->len : 0); + g_print ("%s", str); + g_free (str); + + qmi_message_dms_get_supported_messages_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_SUPPORTED_MESSAGES */ + +#if defined HAVE_QMI_MESSAGE_DMS_HP_CHANGE_DEVICE_MODE + +static QmiMessageDmsHpChangeDeviceModeInput * +hp_change_device_mode_input_create (const gchar *str) +{ + QmiMessageDmsHpChangeDeviceModeInput *input = NULL; + QmiDmsHpDeviceMode mode; + GError *error = NULL; + + if (!qmicli_read_dms_hp_device_mode_from_string (str, &mode)) { + g_printerr ("error: couldn't parse input HP device mode : '%s'\n", str); + return NULL; + } + + input = qmi_message_dms_hp_change_device_mode_input_new (); + if (!qmi_message_dms_hp_change_device_mode_input_set_mode (input, mode, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_hp_change_device_mode_input_unref (input); + return NULL; + } + + return input; +} + +static void +hp_change_device_mode_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsHpChangeDeviceModeOutput *output; + GError *error = NULL; + + output = qmi_client_dms_hp_change_device_mode_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_dms_hp_change_device_mode_output_get_result (output, &error)) { + g_printerr ("error: couldn't change HP device mode: %s\n", error->message); + g_error_free (error); + qmi_message_dms_hp_change_device_mode_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully changed HP device mode\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_hp_change_device_mode_output_unref (output); + + /* Changing the mode will end up power cycling the device right away, so + * just ignore any error from now on and don't even try to release the + * client CID */ + operation_shutdown_skip_cid_release (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_HP_CHANGE_DEVICE_MODE */ + +#if defined HAVE_QMI_MESSAGE_DMS_SWI_GET_CURRENT_FIRMWARE + +static void +swi_get_current_firmware_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsSwiGetCurrentFirmwareOutput *output; + GError *error = NULL; + const gchar *model = NULL; + const gchar *boot_version = NULL; + const gchar *amss_version = NULL; + const gchar *sku_id = NULL; + const gchar *package_id = NULL; + const gchar *carrier_id = NULL; + const gchar *pri_version = NULL; + const gchar *carrier = NULL; + const gchar *config_version = NULL; + + output = qmi_client_dms_swi_get_current_firmware_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_dms_swi_get_current_firmware_output_get_result (output, &error)) { + g_printerr ("error: couldn't get current firmware: %s\n", error->message); + g_error_free (error); + qmi_message_dms_swi_get_current_firmware_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_swi_get_current_firmware_output_get_model (output, &model, NULL); + qmi_message_dms_swi_get_current_firmware_output_get_boot_version (output, &boot_version, NULL); + qmi_message_dms_swi_get_current_firmware_output_get_amss_version (output, &amss_version, NULL); + qmi_message_dms_swi_get_current_firmware_output_get_sku_id (output, &sku_id, NULL); + qmi_message_dms_swi_get_current_firmware_output_get_package_id (output, &package_id, NULL); + qmi_message_dms_swi_get_current_firmware_output_get_carrier_id (output, &carrier_id, NULL); + qmi_message_dms_swi_get_current_firmware_output_get_pri_version (output, &pri_version, NULL); + qmi_message_dms_swi_get_current_firmware_output_get_carrier (output, &carrier, NULL); + qmi_message_dms_swi_get_current_firmware_output_get_config_version (output, &config_version, NULL); + + /* We'll consider it a success if we got at least one of the expected strings */ + if (!model && + !boot_version && + !amss_version && + !sku_id && + !package_id && + !carrier_id && + !pri_version && + !carrier && + !config_version) { + g_printerr ("error: couldn't get any of the current firmware fields\n"); + qmi_message_dms_swi_get_current_firmware_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully retrieved current firmware:\n", + qmi_device_get_path_display (ctx->device)); + g_print ("\tModel: %s\n", VALIDATE_UNKNOWN (model)); + g_print ("\tBoot version: %s\n", VALIDATE_UNKNOWN (boot_version)); + g_print ("\tAMSS version: %s\n", VALIDATE_UNKNOWN (amss_version)); + g_print ("\tSKU ID: %s\n", VALIDATE_UNKNOWN (sku_id)); + g_print ("\tPackage ID: %s\n", VALIDATE_UNKNOWN (package_id)); + g_print ("\tCarrier ID: %s\n", VALIDATE_UNKNOWN (carrier_id)); + g_print ("\tConfig version: %s\n", VALIDATE_UNKNOWN (config_version)); + + qmi_message_dms_swi_get_current_firmware_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_SWI_GET_CURRENT_FIRMWARE */ + +#if defined HAVE_QMI_MESSAGE_DMS_SWI_GET_USB_COMPOSITION + +static void +swi_get_usb_composition_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsSwiGetUsbCompositionOutput *output; + GError *error = NULL; + GArray *supported = NULL; + QmiDmsSwiUsbComposition current = QMI_DMS_SWI_USB_COMPOSITION_UNKNOWN; + guint i; + + output = qmi_client_dms_swi_get_usb_composition_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_dms_swi_get_usb_composition_output_get_result (output, &error)) { + g_printerr ("error: couldn't get USB composite modes: %s\n", error->message); + g_error_free (error); + qmi_message_dms_swi_get_usb_composition_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully retrieved USB compositions:\n", + qmi_device_get_path_display (ctx->device)); + + if (!qmi_message_dms_swi_get_usb_composition_output_get_current (output, ¤t, &error)) { + g_printerr ("error: couldn't get current USB composition: %s\n", error->message); + g_clear_error (&error); + } + + if (!qmi_message_dms_swi_get_usb_composition_output_get_supported (output, &supported, &error)) { + g_printerr ("error: couldn't get list of USB compositions: %s\n", error->message); + g_clear_error (&error); + } + + for (i = 0; i < supported->len; i++) { + QmiDmsSwiUsbComposition value; + + value = g_array_index (supported, QmiDmsSwiUsbComposition, i); + g_print ("\t%sUSB composition %s: %s\n", + (value == current ? "[*] " : " "), + qmi_dms_swi_usb_composition_get_string (value), + qmi_dms_swi_usb_composition_get_description (value)); + } + + qmi_message_dms_swi_get_usb_composition_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_SWI_GET_USB_COMPOSITION */ + +#if defined HAVE_QMI_MESSAGE_DMS_SWI_SET_USB_COMPOSITION + +static void +swi_set_usb_composition_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsSwiSetUsbCompositionOutput *output; + GError *error = NULL; + + output = qmi_client_dms_swi_set_usb_composition_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_dms_swi_set_usb_composition_output_get_result (output, &error)) { + g_printerr ("error: couldn't set USB composite modes: %s\n", error->message); + g_error_free (error); + qmi_message_dms_swi_set_usb_composition_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully set USB composition\n" + "\n" + "\tYou may want to power-cycle the modem now, or just set it offline and reset it:\n" + "\t\t$> sudo qmicli ... --dms-set-operating-mode=offline\n" + "\t\t$> sudo qmicli ... --dms-set-operating-mode=reset\n" + "\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_swi_set_usb_composition_output_unref (output); + operation_shutdown (TRUE); +} + +static QmiMessageDmsSwiSetUsbCompositionInput * +swi_set_usb_composition_input_create (const gchar *str) +{ + QmiMessageDmsSwiSetUsbCompositionInput *input = NULL; + QmiDmsSwiUsbComposition value; + GError *error = NULL; + + if (!qmicli_read_dms_swi_usb_composition_from_string (str, &value)) + return NULL; + + input = qmi_message_dms_swi_set_usb_composition_input_new (); + if (!qmi_message_dms_swi_set_usb_composition_input_set_current (input, value, &error)) { + g_printerr ("error: couldn't create input bundle: '%s'\n", error->message); + g_error_free (error); + qmi_message_dms_swi_set_usb_composition_input_unref (input); + return NULL; + } + + return input; +} + +#endif /* HAVE_QMI_MESSAGE_DMS_SWI_SET_USB_COMPOSITION */ + +#if defined HAVE_QMI_MESSAGE_DMS_FOXCONN_CHANGE_DEVICE_MODE + +static QmiMessageDmsFoxconnChangeDeviceModeInput * +foxconn_change_device_mode_input_create (const gchar *str) +{ + QmiMessageDmsFoxconnChangeDeviceModeInput *input = NULL; + QmiDmsFoxconnDeviceMode mode; + GError *error = NULL; + + if (!qmicli_read_dms_foxconn_device_mode_from_string (str, &mode)) { + g_printerr ("error: couldn't parse input foxconn device mode : '%s'\n", str); + return NULL; + } + + input = qmi_message_dms_foxconn_change_device_mode_input_new (); + if (!qmi_message_dms_foxconn_change_device_mode_input_set_mode (input, mode, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_foxconn_change_device_mode_input_unref (input); + return NULL; + } + + return input; +} + +static void +foxconn_change_device_mode_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsFoxconnChangeDeviceModeOutput *output; + GError *error = NULL; + + output = qmi_client_dms_foxconn_change_device_mode_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_dms_foxconn_change_device_mode_output_get_result (output, &error)) { + g_printerr ("error: couldn't change foxconn device mode: %s\n", error->message); + g_error_free (error); + qmi_message_dms_foxconn_change_device_mode_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully changed foxconn device mode\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_foxconn_change_device_mode_output_unref (output); + + /* Changing the mode will end up power cycling the device right away, so + * just ignore any error from now on and don't even try to release the + * client CID */ + operation_shutdown_skip_cid_release (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_FOXCONN_CHANGE_DEVICE_MODE */ + +#if defined HAVE_QMI_MESSAGE_DMS_FOXCONN_GET_FIRMWARE_VERSION + +static QmiMessageDmsFoxconnGetFirmwareVersionInput * +foxconn_get_firmware_version_input_create (const gchar *str) +{ + QmiMessageDmsFoxconnGetFirmwareVersionInput *input = NULL; + QmiDmsFoxconnFirmwareVersionType type; + GError *error = NULL; + + if (!qmicli_read_dms_foxconn_firmware_version_type_from_string (str, &type)) { + g_printerr ("error: couldn't parse input foxconn firmware version type : '%s'\n", str); + return NULL; + } + + input = qmi_message_dms_foxconn_get_firmware_version_input_new (); + if (!qmi_message_dms_foxconn_get_firmware_version_input_set_version_type (input, type, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_foxconn_get_firmware_version_input_unref (input); + return NULL; + } + + return input; +} + +static void +foxconn_get_firmware_version_ready (QmiClientDms *client, + GAsyncResult *res) +{ + const gchar *str = NULL; + QmiMessageDmsFoxconnGetFirmwareVersionOutput *output; + GError *error = NULL; + + output = qmi_client_dms_foxconn_get_firmware_version_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_dms_foxconn_get_firmware_version_output_get_result (output, &error)) { + g_printerr ("error: couldn't get foxconn firmware version: %s\n", error->message); + g_error_free (error); + qmi_message_dms_foxconn_get_firmware_version_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_foxconn_get_firmware_version_output_get_version (output, &str, NULL); + + g_print ("[%s] Firmware version retrieved:\n" + "\tVersion: '%s'\n", + qmi_device_get_path_display (ctx->device), + VALIDATE_UNKNOWN (str)); + + qmi_message_dms_foxconn_get_firmware_version_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_FOXCONN_GET_FIRMWARE_VERSION */ + +#if defined HAVE_QMI_MESSAGE_DMS_FOXCONN_SET_FCC_AUTHENTICATION + +static void +foxconn_set_fcc_authentication_ready (QmiClientDms *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageDmsFoxconnSetFccAuthenticationOutput) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_dms_foxconn_set_fcc_authentication_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_dms_foxconn_set_fcc_authentication_output_get_result (output, &error)) { + g_printerr ("error: couldn't run Foxconn FCC authentication: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully run Foxconn FCC authentication\n", + qmi_device_get_path_display (ctx->device)); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_FOXCONN_SET_FCC_AUTHENTICATION */ + +#if defined HAVE_QMI_MESSAGE_DMS_FOXCONN_SET_FCC_AUTHENTICATION_V2 + +static void +foxconn_set_fcc_authentication_v2_ready (QmiClientDms *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageDmsFoxconnSetFccAuthenticationV2Output) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_dms_foxconn_set_fcc_authentication_v2_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_dms_foxconn_set_fcc_authentication_v2_output_get_result (output, &error)) { + g_printerr ("error: couldn't run Foxconn FCC authentication: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully run Foxconn FCC authentication v2\n", + qmi_device_get_path_display (ctx->device)); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_FOXCONN_SET_FCC_AUTHENTICATION_V2 */ + +#if defined HAVE_QMI_MESSAGE_DMS_GET_MAC_ADDRESS + +static QmiMessageDmsGetMacAddressInput * +get_mac_address_input_create (const gchar *str) +{ + QmiMessageDmsGetMacAddressInput *input = NULL; + QmiDmsMacType device; + + if (qmicli_read_dms_mac_type_from_string (str, &device)) { + GError *error = NULL; + + input = qmi_message_dms_get_mac_address_input_new (); + if (!qmi_message_dms_get_mac_address_input_set_device ( + input, + device, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dms_get_mac_address_input_unref (input); + input = NULL; + } + } + + return input; +} + +static void +get_mac_address_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsGetMacAddressOutput *output; + GError *error = NULL; + gchar *mac_address_printable; + GArray *mac_address = NULL; + + output = qmi_client_dms_get_mac_address_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_dms_get_mac_address_output_get_result (output, &error)) { + g_printerr ("error: couldn't get mac address: %s\n", error->message); + g_error_free (error); + qmi_message_dms_get_mac_address_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dms_get_mac_address_output_get_mac_address (output, &mac_address, NULL); + mac_address_printable = qmicli_get_raw_data_printable (mac_address, 80, "\t\t"); + + g_print ("[%s] MAC address read:\n" + "\tSize: '%u' bytes\n" + "\tContents:\n" + "%s", + qmi_device_get_path_display (ctx->device), + mac_address->len, + mac_address_printable); + g_free (mac_address_printable); + + qmi_message_dms_get_mac_address_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_GET_MAC_ADDRESS */ + +#if defined HAVE_QMI_MESSAGE_DMS_RESET + +static void +reset_ready (QmiClientDms *client, + GAsyncResult *res) +{ + QmiMessageDmsResetOutput *output; + GError *error = NULL; + + output = qmi_client_dms_reset_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_dms_reset_output_get_result (output, &error)) { + g_printerr ("error: couldn't reset the DMS service: %s\n", error->message); + g_error_free (error); + qmi_message_dms_reset_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully performed DMS service reset\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_dms_reset_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DMS_RESET */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_dms_run (QmiDevice *device, + QmiClientDms *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_DMS_GET_IDS + if (get_ids_flag) { + g_debug ("Asynchronously getting IDs..."); + qmi_client_dms_get_ids (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_ids_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_CAPABILITIES + if (get_capabilities_flag) { + g_debug ("Asynchronously getting capabilities..."); + qmi_client_dms_get_capabilities (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_capabilities_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_MANUFACTURER + if (get_manufacturer_flag) { + g_debug ("Asynchronously getting manufacturer..."); + qmi_client_dms_get_manufacturer (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_manufacturer_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_MODEL + if (get_model_flag) { + g_debug ("Asynchronously getting model..."); + qmi_client_dms_get_model (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_model_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_REVISION + if (get_revision_flag) { + g_debug ("Asynchronously getting revision..."); + qmi_client_dms_get_revision (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_revision_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_MSISDN + if (get_msisdn_flag) { + g_debug ("Asynchronously getting msisdn..."); + qmi_client_dms_get_msisdn (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_msisdn_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_POWER_STATE + if (get_power_state_flag) { + g_debug ("Asynchronously getting power status..."); + qmi_client_dms_get_power_state (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_power_state_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_SET_PIN_PROTECTION + if (uim_set_pin_protection_str) { + QmiMessageDmsUimSetPinProtectionInput *input; + + g_debug ("Asynchronously setting PIN protection..."); + input = uim_set_pin_protection_input_create (uim_set_pin_protection_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dms_uim_set_pin_protection (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)uim_set_pin_protection_ready, + NULL); + qmi_message_dms_uim_set_pin_protection_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_VERIFY_PIN + if (uim_verify_pin_str) { + QmiMessageDmsUimVerifyPinInput *input; + + g_debug ("Asynchronously verifying PIN..."); + input = uim_verify_pin_input_create (uim_verify_pin_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dms_uim_verify_pin (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)uim_verify_pin_ready, + NULL); + qmi_message_dms_uim_verify_pin_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_UNBLOCK_PIN + if (uim_unblock_pin_str) { + QmiMessageDmsUimUnblockPinInput *input; + + g_debug ("Asynchronously unblocking PIN..."); + input = uim_unblock_pin_input_create (uim_unblock_pin_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dms_uim_unblock_pin (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)uim_unblock_pin_ready, + NULL); + qmi_message_dms_uim_unblock_pin_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_CHANGE_PIN + if (uim_change_pin_str) { + QmiMessageDmsUimChangePinInput *input; + + g_debug ("Asynchronously changing PIN..."); + input = uim_change_pin_input_create (uim_change_pin_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dms_uim_change_pin (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)uim_change_pin_ready, + NULL); + qmi_message_dms_uim_change_pin_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_GET_PIN_STATUS + if (uim_get_pin_status_flag) { + g_debug ("Asynchronously getting PIN status..."); + qmi_client_dms_uim_get_pin_status (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)uim_get_pin_status_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_GET_ICCID + if (uim_get_iccid_flag) { + g_debug ("Asynchronously getting UIM ICCID..."); + qmi_client_dms_uim_get_iccid (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)uim_get_iccid_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_GET_IMSI + if (uim_get_imsi_flag) { + g_debug ("Asynchronously getting UIM IMSI..."); + qmi_client_dms_uim_get_imsi (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)uim_get_imsi_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_GET_STATE + if (uim_get_state_flag) { + g_debug ("Asynchronously getting UIM state..."); + qmi_client_dms_uim_get_state (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)uim_get_state_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_HARDWARE_REVISION + if (get_hardware_revision_flag) { + g_debug ("Asynchronously getting hardware revision..."); + qmi_client_dms_get_hardware_revision (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_hardware_revision_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_OPERATING_MODE + if (get_operating_mode_flag) { + g_debug ("Asynchronously getting operating mode..."); + qmi_client_dms_get_operating_mode (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_operating_mode_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_SET_OPERATING_MODE + if (set_operating_mode_str) { + QmiMessageDmsSetOperatingModeInput *input; + + g_debug ("Asynchronously setting operating mode..."); + input = set_operating_mode_input_create (set_operating_mode_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dms_set_operating_mode (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_operating_mode_ready, + NULL); + qmi_message_dms_set_operating_mode_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_TIME + if (get_time_flag) { + g_debug ("Asynchronously getting time..."); + qmi_client_dms_get_time (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_time_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_PRL_VERSION + if (get_prl_version_flag) { + g_debug ("Asynchronously getting PRL version..."); + qmi_client_dms_get_prl_version (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_prl_version_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_ACTIVATION_STATE + if (get_activation_state_flag) { + g_debug ("Asynchronously getting activation state..."); + qmi_client_dms_get_activation_state (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_activation_state_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_ACTIVATE_AUTOMATIC + if (activate_automatic_str) { + QmiMessageDmsActivateAutomaticInput *input; + + g_debug ("Asynchronously requesting automatic activation..."); + input = activate_automatic_input_create (activate_automatic_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dms_activate_automatic (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)activate_automatic_ready, + NULL); + qmi_message_dms_activate_automatic_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_ACTIVATE_MANUAL + if (activate_manual_str) { + QmiMessageDmsActivateManualInput *input; + + g_debug ("Asynchronously requesting manual activation..."); + input = activate_manual_input_create (activate_manual_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dms_activate_manual (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)activate_manual_ready, + NULL); + qmi_message_dms_activate_manual_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_USER_LOCK_STATE + if (get_user_lock_state_flag) { + g_debug ("Asynchronously getting user lock state..."); + qmi_client_dms_get_user_lock_state (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_user_lock_state_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_SET_USER_LOCK_STATE + if (set_user_lock_state_str) { + QmiMessageDmsSetUserLockStateInput *input; + + g_debug ("Asynchronously setting user lock state..."); + input = set_user_lock_state_input_create (set_user_lock_state_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dms_set_user_lock_state (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_user_lock_state_ready, + NULL); + qmi_message_dms_set_user_lock_state_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_SET_USER_LOCK_CODE + if (set_user_lock_code_str) { + QmiMessageDmsSetUserLockCodeInput *input; + + g_debug ("Asynchronously changing user lock code..."); + input = set_user_lock_code_input_create (set_user_lock_code_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dms_set_user_lock_code (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_user_lock_code_ready, + NULL); + qmi_message_dms_set_user_lock_code_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_READ_USER_DATA + if (read_user_data_flag) { + g_debug ("Asynchronously reading user data..."); + qmi_client_dms_read_user_data (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)read_user_data_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_WRITE_USER_DATA + if (write_user_data_str) { + QmiMessageDmsWriteUserDataInput *input; + + g_debug ("Asynchronously writing user data..."); + input = write_user_data_input_create (write_user_data_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dms_write_user_data (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)write_user_data_ready, + NULL); + qmi_message_dms_write_user_data_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_READ_ERI_FILE + if (read_eri_file_flag) { + g_debug ("Asynchronously reading ERI file..."); + qmi_client_dms_read_eri_file (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)read_eri_file_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_RESTORE_FACTORY_DEFAULTS + if (restore_factory_defaults_str) { + QmiMessageDmsRestoreFactoryDefaultsInput *input; + + g_debug ("Asynchronously restoring factory defaults..."); + input = restore_factory_defaults_input_create (restore_factory_defaults_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dms_restore_factory_defaults (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)restore_factory_defaults_ready, + NULL); + qmi_message_dms_restore_factory_defaults_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_VALIDATE_SERVICE_PROGRAMMING_CODE + if (validate_service_programming_code_str) { + QmiMessageDmsValidateServiceProgrammingCodeInput *input; + + g_debug ("Asynchronously validating SPC..."); + input = validate_service_programming_code_input_create (validate_service_programming_code_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dms_validate_service_programming_code (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)validate_service_programming_code_ready, + NULL); + qmi_message_dms_validate_service_programming_code_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_ID + if (set_firmware_id_flag) { + g_debug ("Asynchronously setting firmware id..."); + qmi_client_dms_set_firmware_id (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_firmware_id_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_GET_CK_STATUS + if (uim_get_ck_status_str) { + QmiMessageDmsUimGetCkStatusInput *input; + + g_debug ("Asynchronously getting CK status..."); + input = uim_get_ck_status_input_create (uim_get_ck_status_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dms_uim_get_ck_status (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)uim_get_ck_status_ready, + NULL); + qmi_message_dms_uim_get_ck_status_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_SET_CK_PROTECTION + if (uim_set_ck_protection_str) { + QmiMessageDmsUimSetCkProtectionInput *input; + + g_debug ("Asynchronously setting CK protection..."); + input = uim_set_ck_protection_input_create (uim_set_ck_protection_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dms_uim_set_ck_protection (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)uim_set_ck_protection_ready, + NULL); + qmi_message_dms_uim_set_ck_protection_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_UIM_UNBLOCK_CK + if (uim_unblock_ck_str) { + QmiMessageDmsUimUnblockCkInput *input; + + g_debug ("Asynchronously unblocking CK..."); + input = uim_unblock_ck_input_create (uim_unblock_ck_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dms_uim_unblock_ck (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)uim_unblock_ck_ready, + NULL); + qmi_message_dms_uim_unblock_ck_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_BAND_CAPABILITIES + if (get_band_capabilities_flag) { + g_debug ("Asynchronously getting band capabilities..."); + qmi_client_dms_get_band_capabilities (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_band_capabilities_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_FACTORY_SKU + if (get_factory_sku_flag) { + g_debug ("Asynchronously getting factory SKU..."); + qmi_client_dms_get_factory_sku (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_factory_sku_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_LIST_STORED_IMAGES && defined HAVE_QMI_MESSAGE_DMS_GET_STORED_IMAGE_INFO + if (list_stored_images_flag) { + g_debug ("Asynchronously listing stored images..."); + qmi_client_dms_list_stored_images (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)list_stored_images_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_PREFERENCE && \ + defined HAVE_QMI_MESSAGE_DMS_LIST_STORED_IMAGES + if (select_stored_image_str) { + g_debug ("Asynchronously selecting stored image..."); + get_stored_image (ctx->client, + select_stored_image_str, + (GAsyncReadyCallback)get_stored_image_select_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_PREFERENCE && \ + defined HAVE_QMI_MESSAGE_DMS_LIST_STORED_IMAGES && \ + defined HAVE_QMI_MESSAGE_DMS_DELETE_STORED_IMAGE + if (delete_stored_image_str) { + g_debug ("Asynchronously deleting stored image..."); + get_stored_image (ctx->client, + delete_stored_image_str, + (GAsyncReadyCallback)get_stored_image_delete_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_FIRMWARE_PREFERENCE + if (get_firmware_preference_flag) { + qmi_client_dms_get_firmware_preference ( + client, + NULL, + 10, + NULL, + (GAsyncReadyCallback)dms_get_firmware_preference_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_SET_FIRMWARE_PREFERENCE + if (set_firmware_preference_str) { + g_autoptr(GError) error = NULL; + g_autoptr(QmiMessageDmsSetFirmwarePreferenceInput) input = NULL; + SetFirmwarePreferenceContext firmware_preference_ctx; + + memset (&firmware_preference_ctx, 0, sizeof (firmware_preference_ctx)); + + g_debug ("Asynchronously setting firmware preference..."); + input = set_firmware_preference_input_create (set_firmware_preference_str, &firmware_preference_ctx, &error); + if (!input) { + g_printerr ("error: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + qmi_client_dms_set_firmware_preference ( + client, + input, + 10, + NULL, + (GAsyncReadyCallback)dms_set_firmware_preference_ready, + NULL); + set_firmware_preference_context_clear (&firmware_preference_ctx); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_BOOT_IMAGE_DOWNLOAD_MODE + if (get_boot_image_download_mode_flag) { + g_debug ("Asynchronously getting boot image download mode..."); + qmi_client_dms_get_boot_image_download_mode (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_boot_image_download_mode_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_SET_BOOT_IMAGE_DOWNLOAD_MODE + if (set_boot_image_download_mode_str) { + QmiMessageDmsSetBootImageDownloadModeInput *input; + + g_debug ("Asynchronously setting boot image download mode..."); + input = set_boot_image_download_mode_input_create (set_boot_image_download_mode_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dms_set_boot_image_download_mode (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_boot_image_download_mode_ready, + NULL); + qmi_message_dms_set_boot_image_download_mode_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_SOFTWARE_VERSION + if (get_software_version_flag) { + g_debug ("Asynchronously getting software version..."); + qmi_client_dms_get_software_version (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_software_version_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_SET_FCC_AUTHENTICATION + if (set_fcc_authentication_flag) { + g_debug ("Asynchronously setting FCC auth..."); + qmi_client_dms_set_fcc_authentication (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_fcc_authentication_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_SUPPORTED_MESSAGES + if (get_supported_messages_flag) { + g_debug ("Asynchronously getting supported DMS messages..."); + qmi_client_dms_get_supported_messages (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_supported_messages_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_HP_CHANGE_DEVICE_MODE + if (hp_change_device_mode_str) { + QmiMessageDmsHpChangeDeviceModeInput *input; + + g_debug ("Asynchronously changing device mode (HP specific)..."); + + input = hp_change_device_mode_input_create (hp_change_device_mode_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_dms_hp_change_device_mode (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)hp_change_device_mode_ready, + NULL); + qmi_message_dms_hp_change_device_mode_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_SWI_GET_CURRENT_FIRMWARE + if (swi_get_current_firmware_flag) { + g_debug ("Asynchronously getting current firmware (Sierra Wireless specific)..."); + qmi_client_dms_swi_get_current_firmware (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)swi_get_current_firmware_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_SWI_GET_USB_COMPOSITION + if (swi_get_usb_composition_flag) { + g_debug ("Asynchronously getting USB compositionss (Sierra Wireless specific)..."); + qmi_client_dms_swi_get_usb_composition (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)swi_get_usb_composition_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_SWI_SET_USB_COMPOSITION + if (swi_set_usb_composition_str) { + QmiMessageDmsSwiSetUsbCompositionInput *input; + + g_debug ("Asynchronously setting USB composition (Sierra Wireless specific)..."); + + input = swi_set_usb_composition_input_create (swi_set_usb_composition_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_dms_swi_set_usb_composition (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)swi_set_usb_composition_ready, + NULL); + qmi_message_dms_swi_set_usb_composition_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_FOXCONN_CHANGE_DEVICE_MODE + if (foxconn_change_device_mode_str || dell_change_device_mode_str) { + QmiMessageDmsFoxconnChangeDeviceModeInput *input; + + g_debug ("Asynchronously changing device mode (Foxconn specific)..."); + + input = foxconn_change_device_mode_input_create (foxconn_change_device_mode_str ? foxconn_change_device_mode_str : dell_change_device_mode_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_dms_foxconn_change_device_mode (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)foxconn_change_device_mode_ready, + NULL); + qmi_message_dms_foxconn_change_device_mode_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_FOXCONN_GET_FIRMWARE_VERSION + if (foxconn_get_firmware_version_str || dell_get_firmware_version_str) { + QmiMessageDmsFoxconnGetFirmwareVersionInput *input; + + g_debug ("Asynchronously getting firmware version (Foxconn specific)..."); + + input = foxconn_get_firmware_version_input_create (foxconn_get_firmware_version_str ? foxconn_get_firmware_version_str : dell_get_firmware_version_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_dms_foxconn_get_firmware_version (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)foxconn_get_firmware_version_ready, + NULL); + qmi_message_dms_foxconn_get_firmware_version_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_FOXCONN_SET_FCC_AUTHENTICATION + if (foxconn_set_fcc_authentication_int >= 0) { + g_autoptr(QmiMessageDmsFoxconnSetFccAuthenticationInput) input = NULL; + + if (foxconn_set_fcc_authentication_int > 0xFF) { + g_printerr ("error: magic value out of [0,255] range\n"); + operation_shutdown (FALSE); + return; + } + + g_debug ("Asynchronously running Foxconn FCC authentication..."); + + input = qmi_message_dms_foxconn_set_fcc_authentication_input_new (); + qmi_message_dms_foxconn_set_fcc_authentication_input_set_value (input, (guint8)foxconn_set_fcc_authentication_int, NULL); + qmi_client_dms_foxconn_set_fcc_authentication (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)foxconn_set_fcc_authentication_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_FOXCONN_SET_FCC_AUTHENTICATION_V2 + if (foxconn_set_fcc_authentication_v2_str) { + g_autoptr(QmiMessageDmsFoxconnSetFccAuthenticationV2Input) input = NULL; + g_auto(GStrv) split = NULL; + gulong magic_number; + + split = g_strsplit (foxconn_set_fcc_authentication_v2_str, ",", -1); + if (g_strv_length (split) < 2) { + g_printerr ("error: missing fields\n"); + operation_shutdown (FALSE); + return; + } + if (g_strv_length (split) > 2) { + g_printerr ("error: too many fields given\n"); + operation_shutdown (FALSE); + return; + } + + magic_number = strtoul (split[1], NULL, 10); + if (magic_number > 0xFF) { + g_printerr ("error: magic number value out of [0,255] range\n"); + operation_shutdown (FALSE); + return; + } + + g_debug ("Asynchronously running Foxconn FCC authentication v2..."); + + input = qmi_message_dms_foxconn_set_fcc_authentication_v2_input_new (); + qmi_message_dms_foxconn_set_fcc_authentication_v2_input_set_magic_string (input, split[0], NULL); + qmi_message_dms_foxconn_set_fcc_authentication_v2_input_set_magic_number (input, magic_number, NULL); + qmi_client_dms_foxconn_set_fcc_authentication_v2 (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)foxconn_set_fcc_authentication_v2_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_GET_MAC_ADDRESS + if (get_mac_address_str) { + QmiMessageDmsGetMacAddressInput *input; + + g_debug ("Asynchronously getting MAC address..."); + + input = get_mac_address_input_create (get_mac_address_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_dms_get_mac_address (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_mac_address_ready, + NULL); + qmi_message_dms_get_mac_address_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DMS_RESET + if (reset_flag) { + g_debug ("Asynchronously resetting DMS service..."); + qmi_client_dms_reset (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)reset_ready, + NULL); + return; + } +#endif + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_DMS */ diff --git a/pkgs/qmi-nmea/qmicli-dpm.c b/pkgs/qmi-nmea/qmicli-dpm.c new file mode 100644 index 0000000..52a0e8c --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-dpm.c @@ -0,0 +1,544 @@ +/* -*- 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) 2021 Aleksander Morgado + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_DPM + +#define QMI_ENDPOINT_INTERFACE_NUMBER_UNDEFINED -1 + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientDpm *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gchar *open_port_str; +static gboolean close_port_flag; +static gboolean noop_flag; + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_DPM_OPEN_PORT + { "dpm-open-port", 0, 0, G_OPTION_ARG_STRING, &open_port_str, + "Open port (allowed-keys: ctrl-ep-type, ctrl-ep-iface-number, ctrl-port-name, hw-data-ep-type, hw-data-ep-iface-number, hw-data-rx-id, hw-data-tx-id, sw-data-ep-type, sw-data-ep-iface-number, sw-data-port-name)", + "[\"key=value,...\"]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DPM_CLOSE_PORT + { "dpm-close-port", 0, 0, G_OPTION_ARG_NONE, &close_port_flag, + "Close port", + NULL + }, +#endif + { "dpm-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a DPM client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_dpm_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("dpm", + "DPM options:", + "Show Data Port Mapper Service options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_dpm_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (!!open_port_str + + close_port_flag + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many DPM actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->cancellable) + g_object_unref (context->cancellable); + if (context->device) + g_object_unref (context->device); + if (context->client) + g_object_unref (context->client); + g_slice_free (Context, context); +} + +static void +operation_shutdown (gboolean operation_status) +{ + /* Cleanup context and finish async operation */ + context_free (ctx); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_DPM_OPEN_PORT + +static void +open_port_ready (QmiClientDpm *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageDpmOpenPortOutput) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_dpm_open_port_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_dpm_open_port_output_get_result (output, &error)) { + g_printerr ("error: couldn't open port: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("Successfully opened the port\n"); + operation_shutdown (TRUE); +} + +typedef struct { + /* control port item building */ + GArray *ctrl_ports; + QmiDataEndpointType ctrl_ep_type; + gint ctrl_ep_iface_number; + gchar *ctrl_port_name; + + /* hw data port item building */ + GArray *hw_data_ports; + QmiDataEndpointType hw_data_ep_type; + gint hw_data_ep_iface_number; + guint hw_data_rx_id; + guint hw_data_tx_id; + + /* sw data port item building */ + GArray *sw_data_ports; + QmiDataEndpointType sw_data_ep_type; + gint sw_data_ep_iface_number; + gchar *sw_data_port_name; +} OpenPortProperties; + +static void +reset_ctrl_port_item (OpenPortProperties *props) +{ + g_free (props->ctrl_port_name); + props->ctrl_port_name = NULL; + props->ctrl_ep_type = QMI_DATA_ENDPOINT_TYPE_UNKNOWN; + props->ctrl_ep_iface_number = QMI_ENDPOINT_INTERFACE_NUMBER_UNDEFINED; +} + +static void +ctrl_port_details_clear (QmiMessageDpmOpenPortInputControlPortsElement *details) +{ + g_free (details->port_name); +} + +static void +build_ctrl_port_item (OpenPortProperties *props) +{ + if ((props->ctrl_ep_type != QMI_DATA_ENDPOINT_TYPE_UNKNOWN) && + (props->ctrl_ep_iface_number != QMI_ENDPOINT_INTERFACE_NUMBER_UNDEFINED) && + (props->ctrl_port_name != NULL)) { + QmiMessageDpmOpenPortInputControlPortsElement details; + + details.port_name = g_strdup (props->ctrl_port_name); + details.endpoint_type = props->ctrl_ep_type; + details.interface_number = props->ctrl_ep_iface_number; + + if (!props->ctrl_ports) { + props->ctrl_ports = g_array_new (FALSE, FALSE, sizeof (QmiMessageDpmOpenPortInputControlPortsElement)); + g_array_set_clear_func (props->ctrl_ports, (GDestroyNotify)ctrl_port_details_clear); + } + + g_array_append_val (props->ctrl_ports, details); + reset_ctrl_port_item (props); + } +} + +static gboolean +check_unfinished_ctrl_port_item (OpenPortProperties *props, + GError **error) +{ + if ((props->ctrl_ep_type != QMI_DATA_ENDPOINT_TYPE_UNKNOWN) || + (props->ctrl_ep_iface_number != QMI_ENDPOINT_INTERFACE_NUMBER_UNDEFINED) || + (props->ctrl_port_name != NULL)) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "error: unfinished ctrl port item"); + return FALSE; + } + return TRUE; +} + +static void +reset_hw_data_port_item (OpenPortProperties *props) +{ + props->hw_data_rx_id = 0; + props->hw_data_tx_id = 0; + props->hw_data_ep_type = QMI_DATA_ENDPOINT_TYPE_UNKNOWN; + props->hw_data_ep_iface_number = QMI_ENDPOINT_INTERFACE_NUMBER_UNDEFINED; +} + +static void +build_hw_data_port_item (OpenPortProperties *props) +{ + if ((props->hw_data_ep_type != QMI_DATA_ENDPOINT_TYPE_UNKNOWN) && + (props->hw_data_ep_iface_number != QMI_ENDPOINT_INTERFACE_NUMBER_UNDEFINED) && + (props->hw_data_rx_id != 0) && + (props->hw_data_tx_id != 0)) { + QmiMessageDpmOpenPortInputHardwareDataPortsElement details; + + details.rx_endpoint_number = props->hw_data_rx_id; + details.tx_endpoint_number = props->hw_data_tx_id; + details.endpoint_type = props->hw_data_ep_type; + details.interface_number = props->hw_data_ep_iface_number; + + if (!props->hw_data_ports) + props->hw_data_ports = g_array_new (FALSE, FALSE, sizeof (QmiMessageDpmOpenPortInputHardwareDataPortsElement)); + + g_array_append_val (props->hw_data_ports, details); + reset_hw_data_port_item (props); + } +} + +static gboolean +check_unfinished_hw_data_port_item (OpenPortProperties *props, + GError **error) +{ + if ((props->hw_data_ep_type != QMI_DATA_ENDPOINT_TYPE_UNKNOWN) || + (props->hw_data_ep_iface_number != QMI_ENDPOINT_INTERFACE_NUMBER_UNDEFINED) || + (props->hw_data_rx_id != 0) || + (props->hw_data_tx_id != 0)) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "error: unfinished hw data port item"); + return FALSE; + } + return TRUE; +} + +static void +reset_sw_data_port_item (OpenPortProperties *props) +{ + g_free (props->sw_data_port_name); + props->sw_data_port_name = NULL; + props->sw_data_ep_type = QMI_DATA_ENDPOINT_TYPE_UNKNOWN; + props->sw_data_ep_iface_number = QMI_ENDPOINT_INTERFACE_NUMBER_UNDEFINED; +} + +static void +sw_data_port_details_clear (QmiMessageDpmOpenPortInputSoftwareDataPortsElement *details) +{ + g_free (details->port_name); +} + +static void +build_sw_data_port_item (OpenPortProperties *props) +{ + if ((props->sw_data_ep_type != QMI_DATA_ENDPOINT_TYPE_UNKNOWN) && + (props->sw_data_ep_iface_number != QMI_ENDPOINT_INTERFACE_NUMBER_UNDEFINED) && + (props->sw_data_port_name != NULL)) { + QmiMessageDpmOpenPortInputSoftwareDataPortsElement details; + + details.port_name = g_strdup (props->sw_data_port_name); + details.endpoint_type = props->sw_data_ep_type; + details.interface_number = props->sw_data_ep_iface_number; + + if (!props->sw_data_ports) { + props->sw_data_ports = g_array_new (FALSE, FALSE, sizeof (QmiMessageDpmOpenPortInputSoftwareDataPortsElement)); + g_array_set_clear_func (props->sw_data_ports, (GDestroyNotify)sw_data_port_details_clear); + } + + g_array_append_val (props->sw_data_ports, details); + reset_sw_data_port_item (props); + } +} + +static gboolean +check_unfinished_sw_data_port_item (OpenPortProperties *props, + GError **error) +{ + if ((props->sw_data_ep_type != QMI_DATA_ENDPOINT_TYPE_UNKNOWN) || + (props->sw_data_ep_iface_number != QMI_ENDPOINT_INTERFACE_NUMBER_UNDEFINED) || + (props->sw_data_port_name != NULL)) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "error: unfinished sw data port item"); + return FALSE; + } + return TRUE; +} + +static gboolean +open_port_properties_handle (const gchar *key, + const gchar *value, + GError **error, + gpointer user_data) +{ + OpenPortProperties *props = (OpenPortProperties *)user_data; + + if (!value || !value[0]) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "key '%s' requires a value", key); + return FALSE; + } + + /* control port item */ + if (g_ascii_strcasecmp (key, "ctrl-ep-type") == 0) { + if (!qmicli_read_data_endpoint_type_from_string (value, &(props->ctrl_ep_type))) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "Unrecognized Endpoint Type '%s'", value); + return FALSE; + } + build_ctrl_port_item (props); + return TRUE; + } + if (g_ascii_strcasecmp (key, "ctrl-ep-iface-number") == 0) { + props->ctrl_ep_iface_number = atoi (value); + build_ctrl_port_item (props); + return TRUE; + } + if (g_ascii_strcasecmp (key, "ctrl-port-name") == 0) { + g_clear_pointer (&props->ctrl_port_name, g_free); + props->ctrl_port_name = g_strdup (value); + build_ctrl_port_item (props); + return TRUE; + } + + /* hw data port item */ + if (g_ascii_strcasecmp (key, "hw-data-ep-type") == 0) { + if (!qmicli_read_data_endpoint_type_from_string (value, &(props->hw_data_ep_type))) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "Unrecognized Endpoint Type '%s'", value); + return FALSE; + } + build_hw_data_port_item (props); + return TRUE; + } + if (g_ascii_strcasecmp (key, "hw-data-ep-iface-number") == 0) { + props->hw_data_ep_iface_number = atoi (value); + build_hw_data_port_item (props); + return TRUE; + } + if (g_ascii_strcasecmp (key, "hw-data-rx-id") == 0) { + props->hw_data_rx_id = atoi (value); + build_hw_data_port_item (props); + return TRUE; + } + if (g_ascii_strcasecmp (key, "hw-data-tx-id") == 0) { + props->hw_data_tx_id = atoi (value); + build_hw_data_port_item (props); + return TRUE; + } + + /* sw data port item */ + if (g_ascii_strcasecmp (key, "sw-data-ep-type") == 0) { + if (!qmicli_read_data_endpoint_type_from_string (value, &(props->sw_data_ep_type))) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "Unrecognized Endpoint Type '%s'", value); + return FALSE; + } + build_sw_data_port_item (props); + return TRUE; + } + if (g_ascii_strcasecmp (key, "sw-data-ep-iface-number") == 0) { + props->sw_data_ep_iface_number = atoi (value); + build_sw_data_port_item (props); + return TRUE; + } + if (g_ascii_strcasecmp (key, "sw-data-port-name") == 0) { + g_clear_pointer (&props->sw_data_port_name, g_free); + props->sw_data_port_name = g_strdup (value); + build_sw_data_port_item (props); + return TRUE; + } + + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "Unrecognized option '%s'", key); + return FALSE; +} + +static gboolean +open_port_input_create (const gchar *str, + QmiMessageDpmOpenPortInput **out_input, + GError **error) +{ + QmiMessageDpmOpenPortInput *input; + OpenPortProperties props = { 0 }; + + reset_ctrl_port_item (&props); + reset_hw_data_port_item (&props); + reset_sw_data_port_item (&props); + + if (!qmicli_parse_key_value_string (str, error, open_port_properties_handle, &props)) + return FALSE; + + if (!check_unfinished_ctrl_port_item (&props, error) || + !check_unfinished_hw_data_port_item (&props, error) || + !check_unfinished_sw_data_port_item (&props, error)) + return FALSE; + + input = qmi_message_dpm_open_port_input_new (); + if (props.ctrl_ports) { + qmi_message_dpm_open_port_input_set_control_ports (input, props.ctrl_ports, NULL); + g_clear_pointer (&props.ctrl_ports, g_array_unref); + } + if (props.hw_data_ports) { + qmi_message_dpm_open_port_input_set_hardware_data_ports (input, props.hw_data_ports, NULL); + g_clear_pointer (&props.hw_data_ports, g_array_unref); + } + if (props.sw_data_ports) { + qmi_message_dpm_open_port_input_set_software_data_ports (input, props.sw_data_ports, NULL); + g_clear_pointer (&props.sw_data_ports, g_array_unref); + } + + *out_input = input; + return TRUE; +} + +#endif /* HAVE_QMI_MESSAGE_DPM_OPEN_PORT */ + +#if defined HAVE_QMI_MESSAGE_DPM_CLOSE_PORT + +static void +close_port_ready (QmiClientDpm *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageDpmClosePortOutput) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_dpm_close_port_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_dpm_close_port_output_get_result (output, &error)) { + g_printerr ("error: couldn't close port: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("Successfully closeed the port\n"); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DPM_CLOSE_PORT */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_dpm_run (QmiDevice *device, + QmiClientDpm *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_DPM_OPEN_PORT + if (open_port_str) { + g_autoptr(QmiMessageDpmOpenPortInput) input = NULL; + g_autoptr(GError) error = NULL; + + if (!open_port_input_create (open_port_str, &input, &error)) { + g_printerr ("error: couldn't process input arguments: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + qmi_client_dpm_open_port (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)open_port_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DPM_CLOSE_PORT + if (close_port_flag) { + qmi_client_dpm_close_port (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)close_port_ready, + NULL); + return; + } +#endif + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_DPM */ diff --git a/pkgs/qmi-nmea/qmicli-dsd.c b/pkgs/qmi-nmea/qmicli-dsd.c new file mode 100644 index 0000000..80636fb --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-dsd.c @@ -0,0 +1,415 @@ +/* -*- 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) 2019 Aleksander Morgado + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_DSD + +#undef VALIDATE_MASK_NONE +#define VALIDATE_MASK_NONE(str) (str ? str : "none") + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientDsd *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gchar *get_apn_info_str; +static gchar *set_apn_type_str; +static gboolean get_system_status_flag; +static gboolean noop_flag; + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_DSD_GET_APN_INFO + { "dsd-get-apn-info", 0, 0, G_OPTION_ARG_STRING, &get_apn_info_str, + "Gets the settings associated to a given APN type", + "[(type)]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DSD_SET_APN_TYPE + { "dsd-set-apn-type", 0, 0, G_OPTION_ARG_STRING, &set_apn_type_str, + "Sets the types associated to a given APN name", + "[(name), (type1|type2|type3...)]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_DSD_GET_SYSTEM_STATUS + { "dsd-get-system-status", 0, 0, G_OPTION_ARG_NONE, &get_system_status_flag, + "Gets system status", + NULL + }, +#endif + { "dsd-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a DSD client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_dsd_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("dsd", + "DSD options:", + "Show Data System Determination options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_dsd_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (!!get_apn_info_str + + !!set_apn_type_str + + get_system_status_flag + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many DSD actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->cancellable) + g_object_unref (context->cancellable); + if (context->device) + g_object_unref (context->device); + if (context->client) + g_object_unref (context->client); + g_slice_free (Context, context); +} + +static void +operation_shutdown (gboolean operation_status) +{ + /* Cleanup context and finish async operation */ + context_free (ctx); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_DSD_GET_APN_INFO + +static void +get_apn_info_ready (QmiClientDsd *client, + GAsyncResult *res) +{ + QmiMessageDsdGetApnInfoOutput *output; + GError *error = NULL; + const gchar *apn_name = NULL; + + output = qmi_client_dsd_get_apn_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_dsd_get_apn_info_output_get_result (output, &error)) { + g_printerr ("error: couldn't get APN info: %s\n", error->message); + g_error_free (error); + qmi_message_dsd_get_apn_info_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("APN info found:\n"); + if (qmi_message_dsd_get_apn_info_output_get_apn_name (output, &apn_name, NULL)) + g_print ("APN name: %s\n", apn_name); + else + g_print ("APN name: n/a\n"); + + qmi_message_dsd_get_apn_info_output_unref (output); + operation_shutdown (TRUE); +} + +static QmiMessageDsdGetApnInfoInput * +get_apn_info_input_create (const gchar *str) +{ + QmiMessageDsdGetApnInfoInput *input = NULL; + GError *error = NULL; + QmiDsdApnType apn_type; + + if (!qmicli_read_dsd_apn_type_from_string (str, &apn_type)) { + g_printerr ("error: couldn't parse input string as APN type: '%s'\n", str); + return NULL; + } + + input = qmi_message_dsd_get_apn_info_input_new (); + if (!qmi_message_dsd_get_apn_info_input_set_apn_type (input, apn_type, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dsd_get_apn_info_input_unref (input); + input = NULL; + } + + return input; +} + +#endif /* HAVE_QMI_MESSAGE_DSD_GET_APN_INFO */ + +#if defined HAVE_QMI_MESSAGE_DSD_SET_APN_TYPE + +static void +set_apn_type_ready (QmiClientDsd *client, + GAsyncResult *res) +{ + QmiMessageDsdSetApnTypeOutput *output; + GError *error = NULL; + + output = qmi_client_dsd_set_apn_type_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_dsd_set_apn_type_output_get_result (output, &error)) { + g_printerr ("error: couldn't set APN type: %s\n", error->message); + g_error_free (error); + qmi_message_dsd_set_apn_type_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("APN type set\n"); + + qmi_message_dsd_set_apn_type_output_unref (output); + operation_shutdown (TRUE); +} + +static QmiMessageDsdSetApnTypeInput * +set_apn_type_input_create (const gchar *str) +{ + QmiMessageDsdSetApnTypeInput *input = NULL; + GError *error = NULL; + QmiDsdApnTypePreference apn_type_preference; + gchar **split; + + split = g_strsplit_set (str, ",", 0); + if (g_strv_length (split) != 2) { + g_printerr ("input string requires 2 values, %u given: '%s'\n", g_strv_length (split), str); + g_strfreev (split); + return NULL; + } + + g_strstrip (split[0]); + g_strstrip (split[1]); + + if (!qmicli_read_dsd_apn_type_preference_from_string (split[1], &apn_type_preference)) { + g_printerr ("error: couldn't parse input string as APN type preference mask: '%s'\n", split[1]); + g_strfreev (split); + return NULL; + } + + input = qmi_message_dsd_set_apn_type_input_new (); + if (!qmi_message_dsd_set_apn_type_input_set_apn_type (input, split[0], apn_type_preference, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_dsd_set_apn_type_input_unref (input); + input = NULL; + } + + g_strfreev (split); + + return input; +} + +#endif /* HAVE_QMI_MESSAGE_DSD_SET_APN_TYPE */ + +#if defined HAVE_QMI_MESSAGE_DSD_GET_SYSTEM_STATUS + +static void +get_system_status_ready (QmiClientDsd *client, + GAsyncResult *res) +{ + QmiMessageDsdGetSystemStatusOutput *output; + GError *error = NULL; + GArray *available_systems = NULL; + guint i; + + output = qmi_client_dsd_get_system_status_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_dsd_get_system_status_output_get_result (output, &error)) { + g_printerr ("error: couldn't get system status: %s\n", error->message); + g_error_free (error); + qmi_message_dsd_get_system_status_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_dsd_get_system_status_output_get_available_systems (output, &available_systems, NULL); + + if (!available_systems || !available_systems->len) { + g_print ("No available data system\n"); + } else { + g_print ("Available data systems retrieved:\n"); + for (i = 0; i < available_systems->len; i++) { + QmiMessageDsdGetSystemStatusOutputAvailableSystemsSystem *system; + g_autofree gchar *so_mask_str = NULL; + + system = &g_array_index (available_systems, QmiMessageDsdGetSystemStatusOutputAvailableSystemsSystem, i); + so_mask_str = qmi_dsd_so_mask_build_string_from_mask ((QmiDsdSoMask)system->so_mask); + g_print ("System [%u]%s:\n" + "\tNetwork type: '%s'\n" + "\tRAT: '%s'\n" + "\tService option: '%s'\n", + i, + i > 0 ? "" : " (current preferred)", + qmi_dsd_data_system_network_type_get_string (system->technology), + qmi_dsd_radio_access_technology_get_string (system->rat), + VALIDATE_MASK_NONE (so_mask_str)); + } + } + + qmi_message_dsd_get_system_status_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_DSD_GET_SYSTEM_STATUS */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_dsd_run (QmiDevice *device, + QmiClientDsd *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_DSD_GET_APN_INFO + if (get_apn_info_str) { + QmiMessageDsdGetApnInfoInput *input; + + g_debug ("Asynchronously getting APN info..."); + input = get_apn_info_input_create (get_apn_info_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dsd_get_apn_info (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_apn_info_ready, + NULL); + qmi_message_dsd_get_apn_info_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DSD_SET_APN_TYPE + if (set_apn_type_str) { + QmiMessageDsdSetApnTypeInput *input; + + g_debug ("Asynchronously setting APN type..."); + input = set_apn_type_input_create (set_apn_type_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_dsd_set_apn_type (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_apn_type_ready, + NULL); + qmi_message_dsd_set_apn_type_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_DSD_GET_SYSTEM_STATUS + if (get_system_status_flag) { + g_debug ("Asynchronously getting system status..."); + qmi_client_dsd_get_system_status (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_system_status_ready, + NULL); + return; + } +#endif + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_DSD */ diff --git a/pkgs/qmi-nmea/qmicli-fox.c b/pkgs/qmi-nmea/qmicli-fox.c new file mode 100644 index 0000000..06579f7 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-fox.c @@ -0,0 +1,240 @@ +/* -*- 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) 2022 Freedom Liu + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_FOX + +#undef VALIDATE_UNKNOWN +#define VALIDATE_UNKNOWN(str) (str ? str : "unknown") + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientFox *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gchar *get_firmware_version_str; +static gboolean noop_flag; + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_FOX_GET_FIRMWARE_VERSION + { "fox-get-firmware-version", 0, 0, G_OPTION_ARG_STRING, &get_firmware_version_str, + "Get firmware version", + "[firmware-mcfg-apps|firmware-mcfg|apps]" + }, +#endif + { "fox-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a FOX client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_fox_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("fox", + "FOX options:", + "Show Foxconn Modem Service options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_fox_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (!!get_firmware_version_str + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many FOX actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->cancellable) + g_object_unref (context->cancellable); + if (context->device) + g_object_unref (context->device); + if (context->client) + g_object_unref (context->client); + g_slice_free (Context, context); +} + +static void +operation_shutdown (gboolean operation_status) +{ + /* Cleanup context and finish async operation */ + context_free (ctx); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_FOX_GET_FIRMWARE_VERSION + +static QmiMessageFoxGetFirmwareVersionInput * +get_firmware_version_input_create (const gchar *str) +{ + QmiMessageFoxGetFirmwareVersionInput *input = NULL; + QmiFoxFirmwareVersionType type; + GError *error = NULL; + + if (!qmicli_read_fox_firmware_version_type_from_string (str, &type)) { + g_printerr ("error: couldn't parse input firmware version type : '%s'\n", str); + return NULL; + } + + input = qmi_message_fox_get_firmware_version_input_new (); + if (!qmi_message_fox_get_firmware_version_input_set_version_type (input, type, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_fox_get_firmware_version_input_unref (input); + return NULL; + } + + return input; +} + +static void +get_firmware_version_ready (QmiClientFox *client, + GAsyncResult *res) +{ + const gchar *str = NULL; + QmiMessageFoxGetFirmwareVersionOutput *output; + GError *error = NULL; + + output = qmi_client_fox_get_firmware_version_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_fox_get_firmware_version_output_get_result (output, &error)) { + g_printerr ("error: couldn't get firmware version: %s\n", error->message); + g_error_free (error); + qmi_message_fox_get_firmware_version_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_fox_get_firmware_version_output_get_version (output, &str, NULL); + + g_print ("[%s] Firmware version retrieved:\n" + "\tVersion: '%s'\n", + qmi_device_get_path_display (ctx->device), + VALIDATE_UNKNOWN (str)); + + qmi_message_fox_get_firmware_version_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_FOX_GET_FIRMWARE_VERSION */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_fox_run (QmiDevice *device, + QmiClientFox *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_FOX_GET_FIRMWARE_VERSION + if (get_firmware_version_str) { + QmiMessageFoxGetFirmwareVersionInput *input; + + g_debug ("Asynchronously getting firmware version..."); + + input = get_firmware_version_input_create (get_firmware_version_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_fox_get_firmware_version (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_firmware_version_ready, + NULL); + qmi_message_fox_get_firmware_version_input_unref (input); + return; + } +#endif + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_FOX */ diff --git a/pkgs/qmi-nmea/qmicli-gas.c b/pkgs/qmi-nmea/qmicli-gas.c new file mode 100644 index 0000000..32cab52 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-gas.c @@ -0,0 +1,499 @@ +/* -*- 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) 2019 Andreas Kling + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "qmicli.h" + +#if defined HAVE_QMI_SERVICE_GAS + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientGas *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gboolean get_firmware_list_flag; +static gboolean get_active_firmware_flag; +static gint set_active_firmware_int = -1; +static gint set_usb_composition_int = -1; +static gboolean get_usb_composition_flag; +static gboolean get_ethernet_mac_address_flag; +static gboolean noop_flag; + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_GAS_DMS_SET_USB_COMPOSITION + { "gas-dms-set-usb-composition", 0, 0, G_OPTION_ARG_INT, &set_usb_composition_int, + "Sets the USB composition", + "[pid]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_GAS_DMS_GET_USB_COMPOSITION + { "gas-dms-get-usb-composition", 0, 0, G_OPTION_ARG_NONE, &get_usb_composition_flag, + "Gets the current USB composition", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_GAS_DMS_GET_FIRMWARE_LIST + { "gas-dms-get-firmware-list", 0, 0, G_OPTION_ARG_NONE, &get_firmware_list_flag, + "Gets the list of stored firmware", + NULL + }, + { "gas-dms-get-active-firmware", 0, 0, G_OPTION_ARG_NONE, &get_active_firmware_flag, + "Gets the currently active firmware", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_GAS_DMS_SET_ACTIVE_FIRMWARE + { "gas-dms-set-active-firmware", 0, 0, G_OPTION_ARG_INT, &set_active_firmware_int, + "Sets the active firmware index", + "[index]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_GAS_DMS_GET_ETHERNET_PDU_MAC_ADDRESS + { "gas-dms-get-ethernet-mac-address", 0, 0, G_OPTION_ARG_NONE, &get_ethernet_mac_address_flag, + "Gets the Ethernet PDU MAC address available in the modem", + NULL + }, +#endif + { "gas-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a GAS client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_gas_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("gas", + "GAS options:", + "Show General Application Service options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_gas_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = ((set_usb_composition_int >= 0) + + get_usb_composition_flag + + get_firmware_list_flag + + get_active_firmware_flag + + (set_active_firmware_int >= 0) + + get_ethernet_mac_address_flag + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many GAS actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->cancellable) + g_object_unref (context->cancellable); + if (context->device) + g_object_unref (context->device); + if (context->client) + g_object_unref (context->client); + g_slice_free (Context, context); +} + +static void +operation_shutdown (gboolean operation_status) +{ + /* Cleanup context and finish async operation */ + context_free (ctx); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_GAS_DMS_SET_USB_COMPOSITION + +static void +set_usb_composition_ready (QmiClientGas *client, + GAsyncResult *res) +{ + QmiMessageGasDmsSetUsbCompositionOutput *output; + GError *error = NULL; + + output = qmi_client_gas_dms_set_usb_composition_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_gas_dms_set_usb_composition_output_get_result (output, &error)) { + g_printerr ("error: unable to switch composition: %s\n", error->message); + g_error_free (error); + qmi_message_gas_dms_set_usb_composition_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully switched composition.\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_gas_dms_set_usb_composition_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_GAS_DMS_SET_USB_COMPOSITION */ + +#if defined HAVE_QMI_MESSAGE_GAS_DMS_GET_USB_COMPOSITION + +static void +get_usb_composition_ready (QmiClientGas *client, + GAsyncResult *res) +{ + QmiMessageGasDmsGetUsbCompositionOutput *output; + GError *error = NULL; + guint32 composition; + + output = qmi_client_gas_dms_get_usb_composition_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_gas_dms_get_usb_composition_output_get_result (output, &error)) { + g_printerr ("error: unable to get current composition: %s\n", error->message); + g_error_free (error); + qmi_message_gas_dms_get_usb_composition_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_gas_dms_get_usb_composition_output_get_usb_composition (output, &composition, NULL); + g_print ("[%s] Current composition is 0x%x\n", + qmi_device_get_path_display (ctx->device), + composition); + + qmi_message_gas_dms_get_usb_composition_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_GAS_DMS_GET_USB_COMPOSITION */ + +#if defined HAVE_QMI_MESSAGE_GAS_DMS_GET_FIRMWARE_LIST + +static void +print_firmware_listing (guint8 idx, + const gchar *name, + const gchar *version, + const gchar *pri_revision) +{ + g_print ("Firmware #%u:\n" + "\tIndex: %u\n" + "\tName: %s\n" + "\tVersion: %s\n" + "\tPRI revision: %s\n", + idx, + idx, + name, + version, + pri_revision); +} + +static void +get_firmware_list_ready (QmiClientGas *client, + GAsyncResult *res) +{ + QmiMessageGasDmsGetFirmwareListOutput *output; + GError *error = NULL; + guint8 idx; + const gchar *name; + const gchar *version; + const gchar *pri_revision; + + output = qmi_client_gas_dms_get_firmware_list_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_gas_dms_get_firmware_list_output_get_result (output, &error)) { + g_printerr ("error: couldn't get stored firmware list: %s\n", error->message); + g_error_free (error); + qmi_message_gas_dms_get_firmware_list_output_unref (output); + operation_shutdown (FALSE); + return; + } + + if (qmi_message_gas_dms_get_firmware_list_output_get_stored_firmware_1 (output, &idx, &name, &version, &pri_revision, NULL)) + print_firmware_listing (idx, name, version, pri_revision); + + if (qmi_message_gas_dms_get_firmware_list_output_get_stored_firmware_2 (output, &idx, &name, &version, &pri_revision, NULL)) + print_firmware_listing (idx, name, version, pri_revision); + + if (qmi_message_gas_dms_get_firmware_list_output_get_stored_firmware_3 (output, &idx, &name, &version, &pri_revision, NULL)) + print_firmware_listing (idx, name, version, pri_revision); + + if (qmi_message_gas_dms_get_firmware_list_output_get_stored_firmware_4 (output, &idx, &name, &version, &pri_revision, NULL)) + print_firmware_listing (idx, name, version, pri_revision); + + qmi_message_gas_dms_get_firmware_list_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_GAS_DMS_GET_FIRMWARE_LIST */ + +#if defined HAVE_QMI_MESSAGE_GAS_DMS_SET_ACTIVE_FIRMWARE + +static void +set_active_firmware_ready (QmiClientGas *client, + GAsyncResult *res) +{ + QmiMessageGasDmsSetActiveFirmwareOutput *output; + GError *error = NULL; + + output = qmi_client_gas_dms_set_active_firmware_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_gas_dms_set_active_firmware_output_get_result (output, &error)) { + g_printerr ("error: couldn't set active firmware list: %s\n", error->message); + g_error_free (error); + qmi_message_gas_dms_set_active_firmware_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("Successfully set the active firmware.\n"); + + qmi_message_gas_dms_set_active_firmware_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_GAS_DMS_SET_ACTIVE_FIRMWARE */ + +#if defined HAVE_QMI_MESSAGE_GAS_DMS_GET_ETHERNET_PDU_MAC_ADDRESS + +static void +print_mac_address (GArray *address) +{ + g_autofree gchar *str = NULL; + + str = qmi_common_str_hex (address->data, address->len, ':'); + g_print ("%s\n", str); +} + +static void +get_ethernet_pdu_mac_address_ready (QmiClientGas *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageGasDmsGetEthernetPduMacAddressOutput) output; + g_autoptr(GError) error = NULL; + GArray *address; + + output = qmi_client_gas_dms_get_ethernet_pdu_mac_address_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_gas_dms_get_ethernet_pdu_mac_address_output_get_result (output, &error)) { + g_printerr ("error: couldn't get Ethernet PDU MAC address: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (qmi_message_gas_dms_get_ethernet_pdu_mac_address_output_get_mac_address_0 (output, &address, &error)) { + g_print ("Ethernet MAC address 0: "); + print_mac_address (address); + } else { + g_printerr ("error: couldn't get Ethernet PDU MAC address 0: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (qmi_message_gas_dms_get_ethernet_pdu_mac_address_output_get_mac_address_1 (output, &address, NULL)) { + g_print ("Ethernet MAC address 1: "); + print_mac_address (address); + } + + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_GAS_DMS_GET_ETHERNET_PDU_MAC_ADDRESS */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_gas_run (QmiDevice *device, + QmiClientGas *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_GAS_DMS_SET_USB_COMPOSITION + if (set_usb_composition_int >= 0) { + QmiMessageGasDmsSetUsbCompositionInput *input; + + input = qmi_message_gas_dms_set_usb_composition_input_new (); + qmi_message_gas_dms_set_usb_composition_input_set_usb_composition (input, set_usb_composition_int, NULL); + qmi_message_gas_dms_set_usb_composition_input_set_endpoint_type (input, QMI_GAS_USB_COMPOSITION_ENDPOINT_TYPE_HSUSB, NULL); + qmi_message_gas_dms_set_usb_composition_input_set_composition_persistence (input, TRUE, NULL); + qmi_message_gas_dms_set_usb_composition_input_set_immediate_setting (input, FALSE, NULL); + qmi_message_gas_dms_set_usb_composition_input_set_reboot_after_setting (input, TRUE, NULL); + g_debug ("Asynchronously switching the USB composition..."); + qmi_client_gas_dms_set_usb_composition (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_usb_composition_ready, + NULL); + qmi_message_gas_dms_set_usb_composition_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_GAS_DMS_GET_USB_COMPOSITION + if (get_usb_composition_flag) { + g_debug ("Asynchronously getting the USB composition..."); + qmi_client_gas_dms_get_usb_composition (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_usb_composition_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_GAS_DMS_GET_FIRMWARE_LIST + if (get_firmware_list_flag || get_active_firmware_flag) { + QmiMessageGasDmsGetFirmwareListInput *input; + + input = qmi_message_gas_dms_get_firmware_list_input_new (); + if (get_firmware_list_flag) { + g_debug ("Asynchronously getting full firmware list..."); + qmi_message_gas_dms_get_firmware_list_input_set_mode (input, QMI_GAS_FIRMWARE_LISTING_MODE_ALL_FIRMWARE, NULL); + } else if (get_active_firmware_flag) { + g_debug ("Asynchronously getting active firmware list..."); + qmi_message_gas_dms_get_firmware_list_input_set_mode (input, QMI_GAS_FIRMWARE_LISTING_MODE_ACTIVE_FIRMWARE, NULL); + } else + g_assert_not_reached (); + + qmi_client_gas_dms_get_firmware_list (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_firmware_list_ready, + NULL); + qmi_message_gas_dms_get_firmware_list_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_GAS_DMS_SET_ACTIVE_FIRMWARE + if (set_active_firmware_int >= 0) { + QmiMessageGasDmsSetActiveFirmwareInput *input; + + input = qmi_message_gas_dms_set_active_firmware_input_new (); + qmi_message_gas_dms_set_active_firmware_input_set_slot_index (input, set_active_firmware_int, NULL); + g_debug ("Asynchronously setting the active firmware index..."); + qmi_client_gas_dms_set_active_firmware (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_active_firmware_ready, + NULL); + qmi_message_gas_dms_set_active_firmware_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_GAS_DMS_GET_ETHERNET_PDU_MAC_ADDRESS + if (get_ethernet_mac_address_flag) { + g_debug ("Asynchronously getting ethernet mac adress..."); + qmi_client_gas_dms_get_ethernet_pdu_mac_address (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_ethernet_pdu_mac_address_ready, + NULL); + return; + } +#endif + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_GAS */ diff --git a/pkgs/qmi-nmea/qmicli-gms.c b/pkgs/qmi-nmea/qmicli-gms.c new file mode 100644 index 0000000..9567c60 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-gms.c @@ -0,0 +1,316 @@ +/* -*- 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) 2020 Vladimir Podshivalov + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_GMS + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientGms *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gboolean get_value_flag; +static gchar *set_value_str; +static gboolean noop_flag; + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_GMS_TEST_GET_VALUE + { "gms-test-get-value", 0, 0, G_OPTION_ARG_NONE, &get_value_flag, + "Gets test value", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_GMS_TEST_SET_VALUE + { "gms-test-set-value", 0, 0, G_OPTION_ARG_STRING, &set_value_str, + "Sets test value", + "[mandatory-value][,[optional-value]]" + }, +#endif + { "gms-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a GMS client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_gms_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("gms", + "GMS options:", + "Show General Modem Service options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_gms_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (get_value_flag + + !!set_value_str + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many GMS actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->cancellable) + g_object_unref (context->cancellable); + if (context->device) + g_object_unref (context->device); + if (context->client) + g_object_unref (context->client); + g_slice_free (Context, context); +} + +static void +operation_shutdown (gboolean operation_status) +{ + /* Cleanup context and finish async operation */ + context_free (ctx); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_GMS_TEST_GET_VALUE + +static void +get_value_ready (QmiClientGms *client, + GAsyncResult *res) +{ + QmiMessageGmsTestGetValueOutput *output; + GError *error = NULL; + guint8 test_mandatory_value; + guint8 test_optional_value; + + output = qmi_client_gms_test_get_value_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_gms_test_get_value_output_get_result (output, &error)) { + g_printerr ("error: couldn't get stored test value: %s\n", error->message); + g_error_free (error); + qmi_message_gms_test_get_value_output_unref (output); + operation_shutdown (FALSE); + return; + } + + if (qmi_message_gms_test_get_value_output_get_test_mandatory_value (output, &test_mandatory_value, NULL)) { + g_print ("Test mandatory value: %u\n", test_mandatory_value); + } + + if (qmi_message_gms_test_get_value_output_get_test_optional_value (output, &test_optional_value, NULL)) { + g_print ("Test optional value: %u\n", test_optional_value); + } + + qmi_message_gms_test_get_value_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_GMS_TEST_GET_VALUE */ + +#if defined HAVE_QMI_MESSAGE_GMS_TEST_SET_VALUE + +static QmiMessageGmsTestSetValueInput * +set_value_input_create (const gchar *str) +{ + QmiMessageGmsTestSetValueInput *input = NULL; + const gchar *mand_value_str = NULL; + const gchar *opt_value_str = NULL; + guint mand_value_int = 0; + guint opt_value_int = 0; + gchar **parts = NULL; + + if (strchr (str, ',')) { + /* Both Mandatory Test Value and Optional Test Value were given */ + parts = g_strsplit_set (str, ",", -1); + if (g_strv_length (parts) != 2) { + g_printerr ("error: failed to parse test value: '%s'\n", str); + goto out; + } + mand_value_str = parts[0]; + opt_value_str = parts[1]; + } else { + /* Only Mandatory Test Value was given */ + mand_value_str = str; + } + + g_assert (mand_value_str); + if (!qmicli_read_uint_from_string (mand_value_str, &mand_value_int) || (mand_value_int > G_MAXUINT8)) { + g_printerr ("error: failed to parse test mandatory value as 8bit value: '%s'\n", mand_value_str); + goto out; + } + + if (opt_value_str && (!qmicli_read_uint_from_string (opt_value_str, &opt_value_int) || (opt_value_int > G_MAXUINT8))) { + g_printerr ("error: failed to parse test optional value as 8bit value: '%s'\n", opt_value_str); + goto out; + } + + input = qmi_message_gms_test_set_value_input_new (); + + qmi_message_gms_test_set_value_input_set_test_mandatory_value (input, (guint8) mand_value_int, NULL); + if (opt_value_str) { + qmi_message_gms_test_set_value_input_set_test_optional_value (input, (guint8) opt_value_int, NULL); + } + +out: + g_strfreev (parts); + return input; +} + +static void +set_value_ready (QmiClientGms *client, + GAsyncResult *res) +{ + QmiMessageGmsTestSetValueOutput *output; + GError *error = NULL; + + output = qmi_client_gms_test_set_value_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_gms_test_set_value_output_get_result (output, &error)) { + g_printerr ("error: couldn't set test value: %s\n", error->message); + g_error_free (error); + qmi_message_gms_test_set_value_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("Successfully set test value.\n"); + + qmi_message_gms_test_set_value_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_GMS_TEST_SET_VALUE */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_gms_run (QmiDevice *device, + QmiClientGms *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_GMS_TEST_GET_VALUE + if (get_value_flag) { + g_debug ("Asynchronously getting test value..."); + qmi_client_gms_test_get_value (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_value_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_GMS_TEST_SET_VALUE + if (set_value_str) { + QmiMessageGmsTestSetValueInput *input; + g_debug ("Asynchronously setting test value..."); + + input = set_value_input_create (set_value_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_gms_test_set_value (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_value_ready, + NULL); + + qmi_message_gms_test_set_value_input_unref (input); + return; + } +#endif + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_GMS */ diff --git a/pkgs/qmi-nmea/qmicli-helpers.c b/pkgs/qmi-nmea/qmicli-helpers.c new file mode 100644 index 0000000..e44fec2 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-helpers.c @@ -0,0 +1,985 @@ +/* -*- 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) 2015 Velocloud Inc. + * Copyright (C) 2012-2019 Aleksander Morgado + */ + +#include +#include +#include +#include + +#include "qmicli-helpers.h" + +#define QMICLI_ENUM_LIST_ITEM(TYPE,TYPE_UNDERSCORE,DESCR) \ + gboolean \ + qmicli_read_## TYPE_UNDERSCORE ##_from_string (const gchar *str, \ + TYPE *out) \ + { \ + GType type; \ + GEnumClass *enum_class; \ + GEnumValue *enum_value; \ + \ + type = qmi_## TYPE_UNDERSCORE ##_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 = (TYPE)enum_value->value; \ + else \ + g_printerr ("error: invalid " DESCR " value given: '%s'\n", str); \ + \ + g_type_class_unref (enum_class); \ + return !!enum_value; \ + } +QMICLI_ENUM_LIST +#undef QMICLI_ENUM_LIST_ITEM + +#define QMICLI_FLAGS_LIST_ITEM(TYPE,TYPE_UNDERSCORE,DESCR) \ + gboolean \ + qmicli_read_## TYPE_UNDERSCORE ##_from_string (const gchar *str, \ + TYPE *out) \ + { \ + GType type; \ + GFlagsClass *flags_class; \ + GFlagsValue *flags_value; \ + gchar **items, **iter; \ + gboolean success = TRUE; \ + \ + type = qmi_## TYPE_UNDERSCORE ##_get_type (); \ + flags_class = G_FLAGS_CLASS (g_type_class_ref (type)); \ + \ + *out = 0; \ + items = g_strsplit_set (str, "|", 0); \ + for (iter = items; iter && *iter && success; iter++) { \ + g_strstrip (*iter); \ + if (!*iter[0]) \ + continue; \ + \ + flags_value = g_flags_get_value_by_nick (flags_class, *iter); \ + if (flags_value) { \ + *out |= (TYPE)flags_value->value; \ + } else { \ + g_printerr ("error: unknown " DESCR " value given: '%s'\n", *iter); \ + success = FALSE; \ + } \ + } \ + \ + g_strfreev (items); \ + g_type_class_unref (flags_class); \ + return success; \ + } +QMICLI_FLAGS_LIST +#undef QMICLI_FLAGS_LIST_ITEM + + +/* For 64-bit flags we don't have GFlagsClass as they're aren't registered in the + * type system. Instead, we create a temporary array with all known flag names, and + * use it to match the input values given */ +#define QMICLI_FLAGS64_LIST_ITEM(TYPE,TYPE_UNDERSCORE,DESCR) \ + gboolean \ + qmicli_read_## TYPE_UNDERSCORE ##_from_string (const gchar *str, \ + TYPE *out) \ + { \ + gchar *flag_names[64]; \ + gchar **items, **iter; \ + guint i; \ + gboolean success = TRUE; \ + \ + for (i = 0; i < G_N_ELEMENTS (flag_names); i++) \ + flag_names[i] = qmi_ ## TYPE_UNDERSCORE ## _build_string_from_mask (((guint64)1) << i); \ + \ + *out = 0; \ + items = g_strsplit_set (str, "|", 0); \ + for (iter = items; iter && *iter && success; iter++) { \ + g_strstrip (*iter); \ + if (!*iter[0]) \ + continue; \ + \ + for (i = 0; i < G_N_ELEMENTS (flag_names); i++) { \ + if (g_strcmp0 (*iter, flag_names[i]) == 0) { \ + *out |= (TYPE)(((guint64)1) << i); \ + break; \ + } \ + } \ + if (i == G_N_ELEMENTS (flag_names)) { \ + g_printerr ("error: unknown " DESCR " value given: '%s'\n", *iter); \ + success = FALSE; \ + } \ + } \ + \ + for (i = 0; i < G_N_ELEMENTS (flag_names); i++) \ + g_free (flag_names[i]); \ + g_strfreev (items); \ + return success; \ + } +QMICLI_FLAGS64_LIST +#undef QMICLI_FLAGS64_LIST_ITEM + +gchar * +qmicli_get_raw_data_printable (const GArray *data, + gsize max_line_length, + const gchar *line_prefix) +{ + gsize i; + gsize j; + gsize k; + gsize new_str_length; + gchar *new_str; + gsize prefix_len; + guint n_lines; + gboolean is_new_line; + + g_return_val_if_fail (max_line_length >= 3, NULL); + + if (!data) + return g_strdup (""); + + /* Get new string length. If input string has N bytes, we need: + * - 1 byte for last NUL char + * - 2N bytes for hexadecimal char representation of each byte... + * - N-1 bytes for the separator ':' + * So... a total of (1+2N+N-1) = 3N bytes are needed... */ + new_str_length = 3 * data->len; + + /* Effective max line length needs to be multiple of 3, we don't want to + * split in half a given byte representation */ + while (max_line_length % 3 != 0) + max_line_length--; + + /* Prefix len includes the newline character plus the length of the input + * prefix */ + prefix_len = strlen (line_prefix) + 1; + /* We don't consider the last NUL byte when counting lines to generate */ + n_lines = (new_str_length - 1) / max_line_length; + if ((new_str_length - 1) % max_line_length != 0) + n_lines++; + + /* Build new str length expected when we prefix the string and we limit the + * line length */ + new_str_length += (n_lines * prefix_len); + + /* Allocate memory for new array and initialize contents to NUL */ + new_str = g_malloc0 (new_str_length); + + /* Print hexadecimal representation of each byte... */ + is_new_line = TRUE; + for (i = 0, j = 0, k = 0; i < data->len; i++) { + if (is_new_line) { + strcpy (&new_str[j], line_prefix); + j += strlen (line_prefix); + is_new_line = FALSE; + } + + /* Print character in output string... */ + snprintf (&new_str[j], 3, "%02X", g_array_index (data, guint8, i)); + j+=2; + k+=2; + + if (i != (data->len - 1) ) { + new_str[j] = ':'; + j++; + k++; + } + + if (k % max_line_length == 0 || + i == (data->len -1)) { + new_str[j] = '\n'; + j++; + is_new_line = TRUE; + } + } + + /* Set output string */ + return new_str; +} + +gchar * +qmicli_get_firmware_image_unique_id_printable (const GArray *unique_id) +{ + gchar *unique_id_str; + guint i; + guint n_ascii = 0; + gboolean end = FALSE; + +#define UNIQUE_ID_LEN 16 + + g_warn_if_fail (unique_id->len <= UNIQUE_ID_LEN); + unique_id_str = g_malloc0 (UNIQUE_ID_LEN + 1); + memcpy (unique_id_str, unique_id->data, UNIQUE_ID_LEN); + + /* We want an ASCII string that, if finished before the 16 bytes, + * is suffixed with NUL bytes. */ + for (i = 0; i < UNIQUE_ID_LEN; i++) { + /* If a byte isn't ASCII, stop */ + if (unique_id_str[i] & 0x80) + break; + /* If string isn't finished yet... */ + if (!end) { + /* String finished now */ + if (unique_id_str[i] == '\0') + end = TRUE; + else + n_ascii++; + } else { + /* String finished but we then got + * another ASCII byte? not possible */ + if (unique_id_str[i] != '\0') + break; + } + } + + if (i == UNIQUE_ID_LEN && n_ascii > 0) + return unique_id_str; + +#undef UNIQUE_ID_LEN + + g_free (unique_id_str); + + /* Get a raw hex string otherwise */ + unique_id_str = qmicli_get_raw_data_printable (unique_id, 80, ""); + unique_id_str[strlen (unique_id_str) - 1] = '\0'; /* remove EOL */ + + return unique_id_str; +} + +gboolean +qmicli_read_dms_uim_pin_id_from_string (const gchar *str, + QmiDmsUimPinId *out) +{ + if (!str || str[0] == '\0') { + g_printerr ("error: expected 'PIN' or 'PIN2', got: none\n"); + return FALSE; + } + + if (g_str_equal (str, "PIN")) { + *out = QMI_DMS_UIM_PIN_ID_PIN; + return TRUE; + } + + if (g_str_equal (str, "PIN2")) { + *out = QMI_DMS_UIM_PIN_ID_PIN2; + return TRUE; + } + + g_printerr ("error: expected 'PIN' or 'PIN2', got: '%s'\n", str); + return FALSE; +} + +gboolean +qmicli_read_uim_pin_id_from_string (const gchar *str, + QmiUimPinId *out) +{ + if (!str || str[0] == '\0') { + g_printerr ("error: expected 'PIN1', 'PIN2' or 'UPIN', got: none\n"); + return FALSE; + } + + if (g_str_equal (str, "PIN1")) { + *out = QMI_UIM_PIN_ID_PIN1; + return TRUE; + } + + if (g_str_equal (str, "PIN2")) { + *out = QMI_UIM_PIN_ID_PIN2; + return TRUE; + } + if (g_str_equal (str, "UPIN")) { + *out = QMI_UIM_PIN_ID_UPIN; + return TRUE; + } + + g_printerr ("error: expected 'PIN1', 'PIN2' or 'UPIN', got: '%s'\n", str); + return FALSE; +} + +gboolean +qmicli_read_ssp_rat_options_from_string (const gchar *str, + QmiNasRatModePreference *out_mode_preference, + GArray **out_acquisition_order) +{ + GType rat_mode_preference_type; + GFlagsClass *rat_mode_preference_flags_class; + GFlagsValue *rat_mode_preference_flags_value; + gboolean mode_preference_set = FALSE; + GType radio_interface_type; + GEnumClass *radio_interface_enum_class; + GEnumValue *radio_interface_enum_value; + gboolean acquisition_order_set = FALSE; + gboolean success = TRUE; + char **items, **iter; + + rat_mode_preference_type = qmi_nas_rat_mode_preference_get_type (); + rat_mode_preference_flags_class = G_FLAGS_CLASS (g_type_class_ref (rat_mode_preference_type)); + radio_interface_type = qmi_nas_radio_interface_get_type (); + radio_interface_enum_class = G_ENUM_CLASS (g_type_class_ref (radio_interface_type)); + + *out_mode_preference = 0; + *out_acquisition_order = g_array_new (FALSE, FALSE, sizeof (QmiNasRadioInterface)); + + items = g_strsplit_set (str, "|", 0); + for (iter = items; iter && *iter && success; iter++) { + if (!*iter[0]) + continue; + + /* Note: we can use the same nick names both for mode preference flags + * and acquistion order enums, which is very fortunate */ + + rat_mode_preference_flags_value = g_flags_get_value_by_nick (rat_mode_preference_flags_class, *iter); + if (rat_mode_preference_flags_value) { + *out_mode_preference |= (QmiNasRatModePreference)rat_mode_preference_flags_value->value; + mode_preference_set = TRUE; + } else { + g_printerr ("error: invalid rat mode pref value given: '%s'\n", *iter); + success = FALSE; + } + + radio_interface_enum_value = g_enum_get_value_by_nick (radio_interface_enum_class, *iter); + if (radio_interface_enum_value) { + QmiNasRadioInterface value; + + value = (QmiNasRadioInterface)(radio_interface_enum_value->value); + g_array_append_val (*out_acquisition_order, value); + acquisition_order_set = TRUE; + } else { + g_printerr ("error: invalid radio interface value given: '%s'\n", *iter); + success = FALSE; + } + } + + if (!mode_preference_set) + g_printerr ("error: invalid rat mode pref input given: '%s'\n", str); + if (!acquisition_order_set) + g_printerr ("error: invalid rat mode pref input given: '%s'\n", str); + + if (items) + g_strfreev (items); + g_type_class_unref (rat_mode_preference_flags_class); + g_type_class_unref (radio_interface_enum_class); + return success && (mode_preference_set || acquisition_order_set);; +} + +static gboolean +parse_3gpp_mcc_mnc (const gchar *str, + guint16 *out_mcc, + guint16 *out_mnc, + gboolean *out_pcs_digit) +{ + guint len; + guint i; + gchar aux[4]; + guint16 tmp; + + len = strlen (str); + if (len != 5 && len != 6) + return FALSE; + for (i = 0; i < len; i++) { + if (!g_ascii_isdigit (str[i])) + return FALSE; + } + + memcpy (&aux[0], str, 3); + aux[3] = '\0'; + tmp = atoi (aux); + if (tmp == 0) + return FALSE; + *out_mcc = tmp; + + if (len == 5) { + memcpy (&aux[0], &str[3], 2); + aux[2] = '\0'; + } else + memcpy (&aux[0], &str[3], 3); + *out_mnc = atoi (aux); + if (out_pcs_digit) + *out_pcs_digit = len == 6; + + return TRUE; +} + +gboolean +qmicli_read_ssp_net_options_from_string (const gchar *str, + QmiNasNetworkSelectionPreference *out, + guint16 *out_mcc, + guint16 *out_mnc) +{ + GType type; + GEnumClass *enum_class; + GEnumValue *enum_value; + gchar *copy, *equals; + guint16 mcc = 0, mnc = 0; + + copy = g_strdup (str); + equals = strchr (copy, '='); + if (equals) { + /* Parse MCC/MNC */ + *equals++ = '\0'; + if (!parse_3gpp_mcc_mnc (equals, &mcc, &mnc, NULL)) { + g_free (copy); + g_printerr ("error: invalid net selection MCC/MNC: '%s'\n", equals); + return FALSE; + } + } + + type = qmi_nas_network_selection_preference_get_type (); + enum_class = G_ENUM_CLASS (g_type_class_ref (type)); + enum_value = g_enum_get_value_by_nick (enum_class, copy); + if (enum_value) { + *out = (QmiNasNetworkSelectionPreference)enum_value->value; + *out_mcc = mcc; + *out_mnc = mnc; + } else + g_printerr ("error: invalid net selection preference value given: '%s'\n", copy); + + g_free (copy); + g_type_class_unref (enum_class); + return !!enum_value; +} + +gboolean +qmicli_read_parse_3gpp_mcc_mnc (const gchar *str, + guint16 *out_mcc, + guint16 *out_mnc, + gboolean *out_pcs_digit) +{ + g_autofree gchar *copy = NULL; + guint16 mcc = 0, mnc = 0; + gboolean pcs_digit = FALSE; + + copy = g_strdup (str); + if (!parse_3gpp_mcc_mnc (copy, &mcc, &mnc, &pcs_digit)) { + g_printerr ("error: invalid net selection MCC/MNC: '%s'\n", str); + return FALSE; + } + + *out_mcc = mcc; + *out_mnc = mnc; + if (out_pcs_digit) + *out_pcs_digit = pcs_digit; + + return TRUE; +} + +gboolean +qmicli_read_enable_disable_from_string (const gchar *str, + gboolean *out) +{ + if (!str || str[0] == '\0') { + g_printerr ("error: expected 'disable' or 'enable', got: none\n"); + return FALSE; + } + + if (g_str_equal (str, "disable")) { + *out = FALSE; + return TRUE; + } + + if (g_str_equal (str, "enable")) { + *out = TRUE; + return TRUE; + } + + g_printerr ("error: expected 'disable' or 'enable', got: '%s'\n", str); + return FALSE; +} + +gboolean +qmicli_read_yes_no_from_string (const gchar *str, + gboolean *out) +{ + if (!str || str[0] == '\0') { + g_printerr ("error: expected 'true', 'false', 'yes' or 'no', got: none\n"); + return FALSE; + } + + if ((g_strcasecmp (str, "yes") == 0) || (g_strcasecmp (str, "true") == 0)) { + *out = TRUE; + return TRUE; + } + + if ((g_strcasecmp (str, "no") == 0) || (g_strcasecmp (str, "false") == 0)) { + *out = FALSE; + return TRUE; + } + + g_printerr ("error: expected 'true', 'false', 'yes' or 'no', got: %s\n", str); + return FALSE; +} + +gboolean +qmicli_read_non_empty_string (const gchar *str, + const gchar *description, + gchar **out) +{ + if (!str || str[0] == '\0') { + g_printerr ("error: empty %s given\n", description); + return FALSE; + } + + *out = (gchar *)str; + return TRUE; +} + +gboolean +qmicli_read_raw_data_from_string (const gchar *str, + GArray **out) +{ + GArray *array; + gsize i; + gsize str_len; + + array = g_array_new (FALSE, FALSE, sizeof (guint8)); + + str_len = str ? strlen (str) : 0; + + for (i = 0; i < str_len; i += 2) { + gint high, low; + guint8 value; + + /* For easy processing, we just ignore the ':' chars, if any available */ + if (str[i] == ':') + i++; + + high = g_ascii_xdigit_value (str[i]); + if (high < 0 || high > 0xF) { + g_printerr ("error: invalid hex char found: '%c'\n", str[i]); + g_clear_pointer (&array, g_array_unref); + return FALSE; + } + + if (!((i + 1) < str_len)) { + g_printerr ("unterminated byte found: '%c?'\n", str[i]); + g_clear_pointer (&array, g_array_unref); + return FALSE; + } + + low = g_ascii_xdigit_value (str[i + 1]); + if (low < 0 || low > 0xF) { + g_printerr ("invalid hex char found: '%c'\n", str[i + 1]); + g_clear_pointer (&array, g_array_unref); + return FALSE; + } + + value = (high << 4) | low; + g_array_append_val (array, value); + } + + *out = array; + return TRUE; +} + +gboolean +qmicli_read_firmware_id_from_string (const gchar *str, + QmiDmsFirmwareImageType *out_type, + guint *out_index) +{ + const gchar *index_str; + + if (g_str_has_prefix (str, "modem")) { + *out_type = QMI_DMS_FIRMWARE_IMAGE_TYPE_MODEM; + index_str = &str[5]; + } else if (g_str_has_prefix (str, "pri")) { + *out_type = QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI; + index_str = &str[3]; + } else { + g_printerr ("error: invalid firmware image type value given: '%s'\n", str); + return FALSE; + } + + return qmicli_read_uint_from_string (index_str, out_index); +} + +gboolean +qmicli_read_binary_array_from_string (const gchar *str, + GArray **out) +{ + gsize i, j, len; + + g_return_val_if_fail (out != NULL, FALSE); + g_return_val_if_fail (str, FALSE); + + /* Ignore ':' digits in the binary string input */ + for (len = 0, i = 0; str[i]; i++) { + if (str[i] == ':') + continue; + len++; + } + + /* Length must be a multiple of 2 */ + if (len & 1) + return FALSE; + + *out = g_array_sized_new (FALSE, TRUE, sizeof (guint8), len >> 1); + g_array_set_size (*out, len >> 1); + + i = 0; + j = 0; + while (str[i]) { + gint a, b; + + while (str[i] == ':') + i++; + a = g_ascii_xdigit_value (str[i++]); + while (str[i] == ':') + i++; + b = g_ascii_xdigit_value (str[i++]); + if (a < 0 || b < 0) { + g_array_unref (*out); + return FALSE; + } + + g_array_index (*out, guint8, j++) = (a << 4) | b; + } + + return TRUE; +} + +gboolean +qmicli_validate_device_open_flags (QmiDeviceOpenFlags mask) +{ + if (!mask) { + g_printerr ("error: invalid device open flags given\n"); + return FALSE; + } + + if ((mask & QMI_DEVICE_OPEN_FLAGS_NET_802_3) && + (mask & QMI_DEVICE_OPEN_FLAGS_NET_RAW_IP)) { + g_printerr ("error: cannot give both 802.3 and raw-IP options\n"); + return FALSE; + } + + if ((mask & QMI_DEVICE_OPEN_FLAGS_NET_QOS_HEADER) && + (mask & QMI_DEVICE_OPEN_FLAGS_NET_NO_QOS_HEADER)) { + g_printerr ("error: cannot request both QoS and no-QoS headers\n"); + return FALSE; + } + + if ((mask & (QMI_DEVICE_OPEN_FLAGS_NET_802_3 | QMI_DEVICE_OPEN_FLAGS_NET_RAW_IP)) && + !(mask & (QMI_DEVICE_OPEN_FLAGS_NET_QOS_HEADER | QMI_DEVICE_OPEN_FLAGS_NET_NO_QOS_HEADER))) { + g_printerr ("error: missing QoS or no-QoS header request\n"); + return FALSE; + } + + if ((mask & (QMI_DEVICE_OPEN_FLAGS_NET_QOS_HEADER | QMI_DEVICE_OPEN_FLAGS_NET_NO_QOS_HEADER)) && + !(mask & (QMI_DEVICE_OPEN_FLAGS_NET_802_3 | QMI_DEVICE_OPEN_FLAGS_NET_RAW_IP))) { + g_printerr ("error: missing link protocol (802.3 or raw IP)\n"); + return FALSE; + } + + return TRUE; +} + +gboolean +qmicli_read_authentication_from_string (const gchar *str, + QmiWdsAuthentication *out) +{ + if (g_ascii_strcasecmp (str, "PAP") == 0) + *out = QMI_WDS_AUTHENTICATION_PAP; + else if (g_ascii_strcasecmp (str, "CHAP") == 0) + *out = QMI_WDS_AUTHENTICATION_CHAP; + else if (g_ascii_strcasecmp (str, "BOTH") == 0) + *out = (QMI_WDS_AUTHENTICATION_PAP | QMI_WDS_AUTHENTICATION_CHAP); + else if (!str[0] || g_ascii_strcasecmp (str, "NONE") == 0) + *out = QMI_WDS_AUTHENTICATION_NONE; + else + return FALSE; + + return TRUE; +} + +gboolean +qmicli_read_pdp_type_from_string (const gchar *str, + QmiWdsPdpType *out) +{ + if (g_ascii_strcasecmp (str, "IP") == 0 || g_ascii_strcasecmp (str, "IPV4") == 0) + *out = QMI_WDS_PDP_TYPE_IPV4; + else if (g_ascii_strcasecmp (str, "PPP") == 0) + *out = QMI_WDS_PDP_TYPE_PPP; + else if (g_ascii_strcasecmp (str, "IPV6") == 0) + *out = (QMI_WDS_PDP_TYPE_IPV6); + else if (g_ascii_strcasecmp (str, "IPV4V6") == 0) + *out = QMI_WDS_PDP_TYPE_IPV4_OR_IPV6; + else + return FALSE; + + return TRUE; +} + +gboolean +qmicli_read_uint_from_string (const gchar *str, + guint *out) +{ + gulong num; + + if (!str || !str[0]) + return FALSE; + + for (num = 0; str[num]; num++) { + if (!g_ascii_isdigit (str[num])) + return FALSE; + } + + errno = 0; + num = strtoul (str, NULL, 10); + if (!errno && num <= G_MAXUINT) { + *out = (guint)num; + return TRUE; + } + return FALSE; +} + +gchar * +qmicli_get_supported_messages_list (const guint8 *data, + gsize len) +{ + GString *str = NULL; + + if (len > 0 && data) { + guint bytearray_i; + + for (bytearray_i = 0; bytearray_i < len; bytearray_i++) { + guint bit_i; + + for (bit_i = 0; bit_i < 8; bit_i++) { + if (data[bytearray_i] & (1 << bit_i)) { + if (!str) + str = g_string_new (""); + g_string_append_printf (str, "\t0x%04X\n", (guint16) (bit_i + (8 * bytearray_i))); + } + } + } + } + + return (str ? g_string_free (str, FALSE) : g_strdup ("\tnone\n")); +} + +/******************************************************************************/ + +typedef struct { + guint16 min; + guint16 max; + const gchar *name; +} EarfcnRange; + +/* http://niviuk.free.fr/lte_band.php */ +static const EarfcnRange earfcn_ranges[] = { + { 0, 599, "E-UTRA band 1: 2100" }, + { 600, 1199, "E-UTRA band 2: 1900 PCS" }, + { 1200, 1949, "E-UTRA band 3: 1800+" }, + { 1950, 2399, "E-UTRA band 4: AWS-1" }, + { 2400, 2649, "E-UTRA band 5: 850" }, + { 2650, 2749, "E-UTRA band 6: UMTS only" }, + { 2750, 3449, "E-UTRA band 7: 2600" }, + { 3450, 3799, "E-UTRA band 8: 900" }, + { 3800, 4149, "E-UTRA band 9: 1800" }, + { 4150, 4749, "E-UTRA band 10: AWS-1+" }, + { 4750, 4999, "E-UTRA band 11: 1500 Lower" }, + { 5000, 5179, "E-UTRA band 12: 700 a" }, + { 5180, 5279, "E-UTRA band 13: 700 c" }, + { 5280, 5379, "E-UTRA band 14: 700 PS" }, + { 5730, 5849, "E-UTRA band 17: 700 b" }, + { 5850, 5999, "E-UTRA band 18: 800 Lower" }, + { 6000, 6149, "E-UTRA band 19: 800 Upper" }, + { 6150, 6449, "E-UTRA band 20: 800 DD" }, + { 6450, 6599, "E-UTRA band 21: 1500 Upper" }, + { 6600, 7399, "E-UTRA band 22: 3500" }, + { 7500, 7699, "E-UTRA band 23: 2000 S-band" }, + { 7700, 8039, "E-UTRA band 24: 1600 L-band" }, + { 8040, 8689, "E-UTRA band 25: 1900+" }, + { 8690, 9039, "E-UTRA band 26: 850+" }, + { 9040, 9209, "E-UTRA band 27: 800 SMR" }, + { 9210, 9659, "E-UTRA band 28: 700 APT" }, + { 9660, 9769, "E-UTRA band 29: 700 d" }, + { 9770, 9869, "E-UTRA band 30: 2300 WCS" }, + { 9870, 9919, "E-UTRA band 31: 450" }, + { 9920, 10359, "E-UTRA band 32: 1500 L-band" }, + { 36000, 36199, "E-UTRA band 33: TD 1900" }, + { 36200, 36349, "E-UTRA band 34: TD 2000" }, + { 36350, 36949, "E-UTRA band 35: TD PCS Lower" }, + { 36950, 37549, "E-UTRA band 36: TD PCS Upper" }, + { 37550, 37749, "E-UTRA band 37: TD PCS Center" }, + { 37750, 38249, "E-UTRA band 38: TD 2600" }, + { 38250, 38649, "E-UTRA band 39: TD 1900+" }, + { 38650, 39649, "E-UTRA band 40: TD 2300" }, + { 39650, 41589, "E-UTRA band 41: TD 2500" }, + { 41590, 43589, "E-UTRA band 42: TD 3500" }, + { 43590, 45589, "E-UTRA band 43: TD 3700" }, + { 45590, 46589, "E-UTRA band 44: TD 700" }, +}; + +const char * +qmicli_earfcn_to_eutra_band_string (guint16 earfcn) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (earfcn_ranges); i++) { + if (earfcn <= earfcn_ranges[i].max && earfcn >= earfcn_ranges[i].min) + return earfcn_ranges[i].name; + } + return "unknown"; +} + +/* Expecting input as: + * key1=string,key2=true,key3=false... + * Strings may also be passed enclosed between double or single quotes, like: + * key1="this is a string", key2='and so is this' + * + * Based on libmbim's mbimcli_parse_key_value_string(). + */ +gboolean +qmicli_parse_key_value_string (const gchar *str, + GError **error, + QmiParseKeyValueForeachFn callback, + gpointer user_data) +{ + GError *inner_error = NULL; + gchar *dupstr, *p, *key, *key_end, *value, *value_end, quote; + + g_return_val_if_fail (callback != NULL, FALSE); + g_return_val_if_fail (str != NULL, FALSE); + + /* Allow empty strings, we'll just return with success */ + while (g_ascii_isspace (*str)) + str++; + if (!str[0]) + return TRUE; + + dupstr = g_strdup (str); + p = dupstr; + + while (TRUE) { + gboolean keep_iteration = FALSE; + + /* Skip leading spaces */ + while (g_ascii_isspace (*p)) + p++; + + /* Key start */ + key = p; + if (!g_ascii_isalnum (*key)) { + inner_error = g_error_new (QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "Key must start with alpha/num, starts with '%c'", + *key); + break; + } + + /* Key end */ + while (g_ascii_isalnum (*p) || (*p == '-') || (*p == '_')) + p++; + key_end = p; + if (key_end == key) { + inner_error = g_error_new (QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "Couldn't find a proper key"); + break; + } + + /* Skip whitespaces, if any */ + while (g_ascii_isspace (*p)) + p++; + + /* Equal sign must be here */ + if (*p != '=') { + inner_error = g_error_new (QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "Couldn't find equal sign separator"); + break; + } + /* Skip the equal */ + p++; + + /* Skip whitespaces, if any */ + while (g_ascii_isspace (*p)) + p++; + + /* Do we have a quote-enclosed string? */ + if (*p == '\"' || *p == '\'') { + quote = *p; + /* Skip the quote */ + p++; + /* Value start */ + value = p; + /* Find the closing quote */ + p = strchr (p, quote); + if (!p) { + inner_error = g_error_new (QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "Unmatched quotes in string value"); + break; + } + + /* Value end */ + value_end = p; + /* Skip the quote */ + p++; + } else { + /* Value start */ + value = p; + + /* Value end */ + while ((*p != ',') && (*p != '\0') && !g_ascii_isspace (*p)) + p++; + value_end = p; + } + + /* Note that we allow value == value_end here */ + + /* Skip whitespaces, if any */ + while (g_ascii_isspace (*p)) + p++; + + /* If a comma is found, we should keep the iteration */ + if (*p == ',') { + /* skip the comma */ + p++; + keep_iteration = TRUE; + } + + /* Got key and value, prepare them and run the callback */ + *value_end = '\0'; + *key_end = '\0'; + if (!callback (key, value, &inner_error, user_data)) { + /* We were told to abort */ + break; + } + g_assert (!inner_error); + + if (keep_iteration) + continue; + + /* Check if no more key/value pairs expected */ + if (*p == '\0') + break; + + inner_error = g_error_new (QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "Unexpected content (%s) after value", + p); + break; + } + + g_free (dupstr); + + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + + return TRUE; +} diff --git a/pkgs/qmi-nmea/qmicli-helpers.h b/pkgs/qmi-nmea/qmicli-helpers.h new file mode 100644 index 0000000..8c31031 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-helpers.h @@ -0,0 +1,147 @@ +/* -*- 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) 2015 Velocloud Inc. + * Copyright (C) 2012-2017 Aleksander Morgado + */ + +#include + +#include + +#ifndef __QMICLI_HELPERS_H__ +#define __QMICLI_HELPERS_H__ + +/* Common helpers to read enums from strings */ +#define QMICLI_ENUM_LIST \ + QMICLI_ENUM_LIST_ITEM (QmiDmsOperatingMode, dms_operating_mode, "operating mode") \ + QMICLI_ENUM_LIST_ITEM (QmiDmsUimFacility, dms_uim_facility, "facility") \ + QMICLI_ENUM_LIST_ITEM (QmiPdcConfigurationType, pdc_configuration_type, "configuration type") \ + QMICLI_ENUM_LIST_ITEM (QmiNasRadioInterface, nas_radio_interface, "radio interface") \ + QMICLI_ENUM_LIST_ITEM (QmiDeviceExpectedDataFormat, device_expected_data_format, "device expected data format") \ + QMICLI_ENUM_LIST_ITEM (QmiWdaLinkLayerProtocol, wda_link_layer_protocol, "link layer protocol") \ + QMICLI_ENUM_LIST_ITEM (QmiWdaDataAggregationProtocol, wda_data_aggregation_protocol, "data aggregation protocol") \ + QMICLI_ENUM_LIST_ITEM (QmiDataEndpointType, data_endpoint_type, "data endpoint type") \ + QMICLI_ENUM_LIST_ITEM (QmiWdsAutoconnectSetting, wds_autoconnect_setting, "autoconnect setting") \ + QMICLI_ENUM_LIST_ITEM (QmiWdsAutoconnectSettingRoaming, wds_autoconnect_setting_roaming, "autoconnect setting roaming") \ + QMICLI_ENUM_LIST_ITEM (QmiDmsBootImageDownloadMode, dms_boot_image_download_mode, "boot image download mode") \ + QMICLI_ENUM_LIST_ITEM (QmiDmsHpDeviceMode, dms_hp_device_mode, "hp device mode") \ + QMICLI_ENUM_LIST_ITEM (QmiDmsSwiUsbComposition, dms_swi_usb_composition, "swi usb composition") \ + QMICLI_ENUM_LIST_ITEM (QmiDmsFoxconnDeviceMode, dms_foxconn_device_mode, "foxconn device mode") \ + QMICLI_ENUM_LIST_ITEM (QmiDmsFoxconnFirmwareVersionType, dms_foxconn_firmware_version_type, "foxconn firmware version type") \ + QMICLI_ENUM_LIST_ITEM (QmiUimSessionType, uim_session_type, "session type") \ + QMICLI_ENUM_LIST_ITEM (QmiDsdApnType, dsd_apn_type, "apn type") \ + QMICLI_ENUM_LIST_ITEM (QmiDmsMacType, dms_mac_type, "mac address type") \ + QMICLI_ENUM_LIST_ITEM (QmiSarRfState, sar_rf_state, "sar rf state") \ + QMICLI_ENUM_LIST_ITEM (QmiSioPort, sio_port, "sio port") \ + QMICLI_ENUM_LIST_ITEM (QmiLocOperationMode, loc_operation_mode, "operation mode") \ + QMICLI_ENUM_LIST_ITEM (QmiLocLockType, loc_lock_type, "lock type") \ + QMICLI_ENUM_LIST_ITEM (QmiUimCardApplicationPersonalizationFeature, uim_card_application_personalization_feature, "personalization feature" ) \ + QMICLI_ENUM_LIST_ITEM (QmiUimDepersonalizationOperation, uim_depersonalization_operation, "depersonalization operation" ) \ + QMICLI_ENUM_LIST_ITEM (QmiWmsMessageType, wms_message_type, "message type" ) \ + QMICLI_ENUM_LIST_ITEM (QmiWmsMessageClass, wms_message_class, "message class" ) \ + QMICLI_ENUM_LIST_ITEM (QmiWmsStorageType, wms_storage_type, "storage type" ) \ + QMICLI_ENUM_LIST_ITEM (QmiWmsReceiptAction, wms_receipt_action, "receipt action" ) \ + QMICLI_ENUM_LIST_ITEM (QmiFoxFirmwareVersionType, fox_firmware_version_type, "fox firmware version type") + +#define QMICLI_ENUM_LIST_ITEM(TYPE,TYPE_UNDERSCORE,DESCR) \ + gboolean qmicli_read_## TYPE_UNDERSCORE ##_from_string (const gchar *str, TYPE *out); +QMICLI_ENUM_LIST +#undef QMICLI_ENUM_LIST_ITEM + +/* Common helpers to read flags from strings */ +#define QMICLI_FLAGS_LIST \ + QMICLI_FLAGS_LIST_ITEM (QmiDeviceOpenFlags, device_open_flags, "device open flags") \ + QMICLI_FLAGS_LIST_ITEM (QmiDeviceAddLinkFlags, device_add_link_flags, "device add link flags") \ + QMICLI_FLAGS_LIST_ITEM (QmiLocNmeaType, loc_nmea_type, "NMEA type") \ + QMICLI_FLAGS_LIST_ITEM (QmiNasPlmnAccessTechnologyIdentifier, nas_plmn_access_technology_identifier, "PLMN access technology") + +#define QMICLI_FLAGS_LIST_ITEM(TYPE,TYPE_UNDERSCORE,DESCR) \ + gboolean qmicli_read_## TYPE_UNDERSCORE ##_from_string (const gchar *str, TYPE *out); +QMICLI_FLAGS_LIST +#undef QMICLI_FLAGS_LIST_ITEM + +/* Common helpers to read 64bit flags from strings */ +#define QMICLI_FLAGS64_LIST \ + QMICLI_FLAGS64_LIST_ITEM (QmiDsdApnTypePreference, dsd_apn_type_preference, "apn type preference") \ + QMICLI_FLAGS64_LIST_ITEM (QmiWdsApnTypeMask, wds_apn_type_mask, "apn type mask") + +#define QMICLI_FLAGS64_LIST_ITEM(TYPE,TYPE_UNDERSCORE,DESCR) \ + gboolean qmicli_read_## TYPE_UNDERSCORE ##_from_string (const gchar *str, TYPE *out); +QMICLI_FLAGS64_LIST +#undef QMICLI_FLAGS64_LIST_ITEM + +gchar *qmicli_get_raw_data_printable (const GArray *data, + gsize max_line_length, + const gchar *new_line_prefix); + +gchar *qmicli_get_firmware_image_unique_id_printable (const GArray *unique_id); + +gboolean qmicli_read_dms_uim_pin_id_from_string (const gchar *str, + QmiDmsUimPinId *out); +gboolean qmicli_read_uim_pin_id_from_string (const gchar *str, + QmiUimPinId *out); +gboolean qmicli_read_ssp_rat_options_from_string (const gchar *str, + QmiNasRatModePreference *out_mode_preference, + GArray **out_acquisition_order); +gboolean qmicli_read_ssp_net_options_from_string (const gchar *str, + QmiNasNetworkSelectionPreference *out_network_preference, + guint16 *out_network_mcc, + guint16 *out_network_mnc); +gboolean qmicli_read_parse_3gpp_mcc_mnc (const gchar *str, + guint16 *out_mcc, + guint16 *out_mnc, + gboolean *out_pcs_digit); +gboolean qmicli_read_enable_disable_from_string (const gchar *str, + gboolean *out); +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_authentication_from_string (const gchar *str, + QmiWdsAuthentication *out); +gboolean qmicli_read_pdp_type_from_string (const gchar *str, + QmiWdsPdpType *out); +gboolean qmicli_read_non_empty_string (const gchar *str, + const gchar *description, + gchar **out); +gboolean qmicli_read_uint_from_string (const gchar *str, + guint *out); +gboolean qmicli_read_raw_data_from_string (const gchar *str, + GArray **out); +gboolean qmicli_read_yes_no_from_string (const gchar *str, + gboolean *out); + +gchar *qmicli_get_supported_messages_list (const guint8 *data, + gsize len); + +const char *qmicli_earfcn_to_eutra_band_string (guint16 earfcn); + +gboolean qmicli_validate_device_open_flags (QmiDeviceOpenFlags mask); + +typedef gboolean (*QmiParseKeyValueForeachFn) (const gchar *key, + const gchar *value, + GError **error, + gpointer user_data); + +gboolean qmicli_parse_key_value_string (const gchar *str, + GError **error, + QmiParseKeyValueForeachFn callback, + gpointer user_data); + +#endif /* __QMICLI_H__ */ diff --git a/pkgs/qmi-nmea/qmicli-ims.c b/pkgs/qmi-nmea/qmicli-ims.c new file mode 100644 index 0000000..b690597 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-ims.c @@ -0,0 +1,283 @@ +/* -*- 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) 2023 Dylan Van Assche + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_IMS + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientIms *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gint bind_flag = -1; +static gboolean get_services_enabled_flag; +static gboolean noop_flag; + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_IMS_BIND + { "ims-bind", 0, 0, G_OPTION_ARG_INT, &bind_flag, + "Bind to IMS Settings (use with --client-no-release-cid)", + "[binding]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_IMS_GET_IMS_SERVICES_ENABLED_SETTING + { "ims-get-ims-services-enabled-setting", 0, 0, G_OPTION_ARG_NONE, &get_services_enabled_flag, + "Get IMS Services Enabled Setting", + NULL + }, +#endif + { "ims-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a IMS client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL } +}; + +GOptionGroup * +qmicli_ims_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("ims", + "IMS options:", + "Show IP Multimedia Subsystem Settings Service options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_ims_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = ((bind_flag >= 0) + + get_services_enabled_flag + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many IMS actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->cancellable) + g_object_unref (context->cancellable); + if (context->device) + g_object_unref (context->device); + if (context->client) + g_object_unref (context->client); + g_slice_free (Context, context); +} + +static void +operation_shutdown (gboolean operation_status) +{ + /* Cleanup context and finish async operation */ + context_free (ctx); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_IMS_BIND + +static void +bind_ready (QmiClientIms *client, + GAsyncResult *res) +{ + QmiMessageImsBindOutput *output; + g_autoptr(GError) error = NULL; + + output = qmi_client_ims_bind_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_ims_bind_output_get_result (output, &error)) { + g_printerr ("error: couldn't bind to IMS Settings: %s\n", error->message); + qmi_message_ims_bind_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] IMS Settings bind successful\n", qmi_device_get_path_display (ctx->device)); + + qmi_message_ims_bind_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_IMS_BIND */ + +#if defined HAVE_QMI_MESSAGE_IMS_GET_IMS_SERVICES_ENABLED_SETTING + +static void +get_services_enabled_ready (QmiClientIms *client, + GAsyncResult *res) +{ + QmiMessageImsGetImsServicesEnabledSettingOutput *output; + gboolean service_voice_enabled; + gboolean service_vt_enabled; + gboolean service_voice_wifi_enabled; + gboolean service_ims_registration_enabled; + gboolean service_ut_enabled; + gboolean service_sms_enabled; + gboolean service_ussd_enabled; + GError *error = NULL; + + output = qmi_client_ims_get_ims_services_enabled_setting_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_ims_get_ims_services_enabled_setting_output_get_result (output, &error)) { + g_printerr ("error: couldn't get IMS services enabled setting: %s\n", error->message); + g_error_free (error); + qmi_message_ims_get_ims_services_enabled_setting_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] IMS services:\n", qmi_device_get_path_display (ctx->device)); + + if (qmi_message_ims_get_ims_services_enabled_setting_output_get_ims_voice_service_enabled (output, &service_voice_enabled, NULL)) + g_print ("\t Voice service enabled: %s\n", service_voice_enabled? "yes" : "no"); + + if (qmi_message_ims_get_ims_services_enabled_setting_output_get_ims_video_telephony_service_enabled (output, &service_vt_enabled, NULL)) + g_print ("\tVideo Telephony service enabled: %s\n", service_vt_enabled? "yes" : "no"); + + if (qmi_message_ims_get_ims_services_enabled_setting_output_get_ims_voice_wifi_service_enabled (output, &service_voice_wifi_enabled, NULL)) + g_print ("\t Voice WiFi service enabled: %s\n", service_voice_wifi_enabled? "yes" : "no"); + + if (qmi_message_ims_get_ims_services_enabled_setting_output_get_ims_registration_service_enabled (output, &service_ims_registration_enabled, NULL)) + g_print ("\t IMS registration enabled: %s\n", service_ims_registration_enabled? "yes" : "no"); + + if (qmi_message_ims_get_ims_services_enabled_setting_output_get_ims_ut_service_enabled (output, &service_ut_enabled, NULL)) + g_print ("\t UE to TAS service enabled: %s\n", service_ut_enabled? "yes" : "no"); + + if (qmi_message_ims_get_ims_services_enabled_setting_output_get_ims_sms_service_enabled (output, &service_sms_enabled, NULL)) + g_print ("\t SMS service enabled: %s\n", service_sms_enabled? "yes" : "no"); + + if (qmi_message_ims_get_ims_services_enabled_setting_output_get_ims_ussd_service_enabled (output, &service_ussd_enabled, NULL)) + g_print ("\t USSD service enabled: %s\n", service_ut_enabled? "yes" : "no"); + + qmi_message_ims_get_ims_services_enabled_setting_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_IMS_GET_IMS_SERVICES_ENABLED_SETTING */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_ims_run (QmiDevice *device, + QmiClientIms *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_IMS_BIND + if (bind_flag >= 0) { + QmiMessageImsBindInput *input; + + input = qmi_message_ims_bind_input_new (); + qmi_message_ims_bind_input_set_binding (input, bind_flag, NULL); + g_debug ("Asynchronously binding to IMS settings service..."); + qmi_client_ims_bind (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)bind_ready, + NULL); + qmi_message_ims_bind_input_unref (input); + return; + } +#endif /* HAVE_QMI_MESSAGE_IMS_BIND */ +#if defined HAVE_QMI_MESSAGE_IMS_GET_IMS_SERVICES_ENABLED_SETTING + if (get_services_enabled_flag) { + g_debug ("Asynchronously getting services enabled setting..."); + + qmi_client_ims_get_ims_services_enabled_setting (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_services_enabled_ready, + NULL); + return; + } +#endif /* HAVE_QMI_MESSAGE_IMS_GET_IMS_SERVICES_ENABLED_SETTING */ + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_IMS */ + diff --git a/pkgs/qmi-nmea/qmicli-imsa.c b/pkgs/qmi-nmea/qmicli-imsa.c new file mode 100644 index 0000000..864420d --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-imsa.c @@ -0,0 +1,367 @@ +/* -*- 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) 2023 Dylan Van Assche + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_IMSA + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientImsa *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gint bind_flag = -1; +static gboolean get_ims_registration_status_flag; +static gboolean get_ims_services_status_flag; +static gboolean noop_flag; + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_IMSA_BIND + { "imsa-bind", 0, 0, G_OPTION_ARG_INT, &bind_flag, + "Bind to IMSA (use with --client-no-release-cid)", + "[binding]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_IMSA_GET_IMS_REGISTRATION_STATUS + { "imsa-get-ims-registration-status", 0, 0, G_OPTION_ARG_NONE, &get_ims_registration_status_flag, + "Get IMS registration status", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_IMSA_GET_IMS_SERVICES_STATUS + { "imsa-get-ims-services-status", 0, 0, G_OPTION_ARG_NONE, &get_ims_services_status_flag, + "Get IMS services status", + NULL + }, +#endif + { "imsa-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a IMSA client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL } +}; + +GOptionGroup * +qmicli_imsa_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("imsa", + "IMSA options:", + "Show IP Multimedia Subsystem Application Service options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_imsa_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = ((bind_flag >= 0) + + get_ims_registration_status_flag + + get_ims_services_status_flag + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many IMSA actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->cancellable) + g_object_unref (context->cancellable); + if (context->device) + g_object_unref (context->device); + if (context->client) + g_object_unref (context->client); + g_slice_free (Context, context); +} + +static void +operation_shutdown (gboolean operation_status) +{ + /* Cleanup context and finish async operation */ + context_free (ctx); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_IMSA_BIND + +static void +bind_ready (QmiClientImsa *client, + GAsyncResult *res) +{ + QmiMessageImsaBindOutput *output; + g_autoptr(GError) error = NULL; + + output = qmi_client_imsa_bind_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_imsa_bind_output_get_result (output, &error)) { + g_printerr ("error: couldn't bind to IMSA: %s\n", error->message); + qmi_message_imsa_bind_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] IMSA bind successful\n", qmi_device_get_path_display (ctx->device)); + + qmi_message_imsa_bind_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_IMSA_BIND */ + +#if defined HAVE_QMI_MESSAGE_IMSA_GET_IMS_REGISTRATION_STATUS + +static void +get_ims_registration_status_ready (QmiClientImsa *client, + GAsyncResult *res) +{ + QmiMessageImsaGetImsRegistrationStatusOutput *output; + QmiImsaImsRegistrationStatus registration_status; + QmiImsaRegistrationTechnology registration_technology; + GError *error = NULL; + + output = qmi_client_imsa_get_ims_registration_status_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_imsa_get_ims_registration_status_output_get_result (output, &error)) { + g_printerr ("error: couldn't get IMS registration status: %s\n", error->message); + g_error_free (error); + qmi_message_imsa_get_ims_registration_status_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] IMS registration:\n", qmi_device_get_path_display (ctx->device)); + + if (qmi_message_imsa_get_ims_registration_status_output_get_ims_registration_status (output, ®istration_status, NULL)) + g_print ("\t Status: '%s'\n", qmi_imsa_ims_registration_status_get_string (registration_status)); + + if (qmi_message_imsa_get_ims_registration_status_output_get_ims_registration_technology (output, ®istration_technology, NULL)) + g_print ("\tTechnology: '%s'\n", qmi_imsa_registration_technology_get_string (registration_technology)); + + qmi_message_imsa_get_ims_registration_status_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_IMSA_GET_IMS_REGISTRATION_STATUS */ + +#if defined HAVE_QMI_MESSAGE_IMSA_GET_IMS_SERVICES_STATUS + +static void +get_ims_services_status_ready (QmiClientImsa *client, + GAsyncResult *res) +{ + QmiMessageImsaGetImsServicesStatusOutput *output; + QmiImsaServiceStatus service_sms_status; + QmiImsaRegistrationTechnology service_sms_technology; + QmiImsaServiceStatus service_voice_status; + QmiImsaRegistrationTechnology service_voice_technology; + QmiImsaServiceStatus service_vt_status; + QmiImsaRegistrationTechnology service_vt_technology; + QmiImsaServiceStatus service_ut_status; + QmiImsaRegistrationTechnology service_ut_technology; + QmiImsaServiceStatus service_vs_status; + QmiImsaRegistrationTechnology service_vs_technology; + GError *error = NULL; + + output = qmi_client_imsa_get_ims_services_status_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_imsa_get_ims_services_status_output_get_result (output, &error)) { + g_printerr ("error: couldn't get IMS services status: %s\n", error->message); + g_error_free (error); + qmi_message_imsa_get_ims_services_status_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] IMS services:\n", qmi_device_get_path_display (ctx->device)); + + g_print ("\tSMS service\n"); + + if (qmi_message_imsa_get_ims_services_status_output_get_ims_sms_service_status (output, &service_sms_status, NULL)) + g_print ("\t\t Status: '%s'\n", qmi_imsa_service_status_get_string (service_sms_status)); + + if (qmi_message_imsa_get_ims_services_status_output_get_ims_sms_service_registration_technology (output, &service_sms_technology, NULL)) + g_print ("\t\tTechnology: '%s'\n", qmi_imsa_registration_technology_get_string (service_sms_technology)); + + g_print ("\tVoice service\n"); + + if (qmi_message_imsa_get_ims_services_status_output_get_ims_voice_service_status (output, &service_voice_status, NULL)) + g_print ("\t\t Status: '%s'\n", qmi_imsa_service_status_get_string (service_voice_status)); + + if (qmi_message_imsa_get_ims_services_status_output_get_ims_voice_service_registration_technology (output, &service_voice_technology, NULL)) + g_print ("\t\tTechnology: '%s'\n", qmi_imsa_registration_technology_get_string (service_voice_technology)); + + g_print ("\tVideo Telephony service\n"); + + if (qmi_message_imsa_get_ims_services_status_output_get_ims_video_telephony_service_status (output, &service_vt_status, NULL)) + g_print ("\t\t Status: '%s'\n", qmi_imsa_service_status_get_string (service_vt_status)); + + if (qmi_message_imsa_get_ims_services_status_output_get_ims_video_telephony_service_registration_technology (output, &service_vt_technology, NULL)) + g_print ("\t\tTechnology: '%s'\n", qmi_imsa_registration_technology_get_string (service_vt_technology)); + + g_print ("\tUE to TAS service\n"); + + if (qmi_message_imsa_get_ims_services_status_output_get_ims_ue_to_tas_service_status (output, &service_ut_status, NULL)) + g_print ("\t\t Status: '%s'\n", qmi_imsa_service_status_get_string (service_ut_status)); + + if (qmi_message_imsa_get_ims_services_status_output_get_ims_ue_to_tas_service_registration_technology (output, &service_ut_technology, NULL)) + g_print ("\t\tTechnology: '%s'\n", qmi_imsa_registration_technology_get_string (service_ut_technology)); + + g_print ("\tVideo Share service\n"); + + if (qmi_message_imsa_get_ims_services_status_output_get_ims_video_share_service_status (output, &service_vs_status, NULL)) + g_print ("\t\t Status: '%s'\n", qmi_imsa_service_status_get_string (service_vs_status)); + + if (qmi_message_imsa_get_ims_services_status_output_get_ims_video_share_service_registration_technology (output, &service_vs_technology, NULL)) + g_print ("\t\tTechnology: '%s'\n", qmi_imsa_registration_technology_get_string (service_vs_technology)); + + qmi_message_imsa_get_ims_services_status_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_IMSA_GET_IMS_SERVICES_STATUS */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_imsa_run (QmiDevice *device, + QmiClientImsa *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_IMSA_BIND + if (bind_flag >= 0) { + QmiMessageImsaBindInput *input; + + input = qmi_message_imsa_bind_input_new (); + qmi_message_imsa_bind_input_set_binding (input, bind_flag, NULL); + g_debug ("Asynchronously binding to IMSA service..."); + qmi_client_imsa_bind (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)bind_ready, + NULL); + qmi_message_imsa_bind_input_unref (input); + return; + } +#endif /* HAVE_QMI_MESSAGE_IMSA_BIND */ +#if defined HAVE_QMI_MESSAGE_IMSA_GET_IMS_REGISTRATION_STATUS + if (get_ims_registration_status_flag) { + g_debug ("Asynchronously getting IMS registration status..."); + + qmi_client_imsa_get_ims_registration_status (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_ims_registration_status_ready, + NULL); + return; + } +#endif /* HAVE_QMI_MESSAGE_IMSA_GET_IMS_REGISTRATION_STATUS */ +#if defined HAVE_QMI_MESSAGE_IMSA_GET_IMS_SERVICES_STATUS + if (get_ims_services_status_flag) { + g_debug ("Asynchronously getting IMS services status..."); + + qmi_client_imsa_get_ims_services_status (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_ims_services_status_ready, + NULL); + return; + } +#endif /* HAVE_QMI_MESSAGE_IMSA_GET_IMS_SERVICES_STATUS */ + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_IMSA */ + diff --git a/pkgs/qmi-nmea/qmicli-imsp.c b/pkgs/qmi-nmea/qmicli-imsp.c new file mode 100644 index 0000000..52dedfd --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-imsp.c @@ -0,0 +1,205 @@ +/* -*- 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) 2023 Dylan Van Assche + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_IMSP + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientImsp *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gboolean get_enabler_state_flag; +static gboolean noop_flag; + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_IMSP_GET_ENABLER_STATE + { "imsp-get-enabler-state", 0, 0, G_OPTION_ARG_NONE, &get_enabler_state_flag, + "Get IMSP enabler state", + NULL + }, +#endif + { "imsp-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a IMSP client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL } +}; + +GOptionGroup * +qmicli_imsp_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("imsp", + "IMSP options:", + "Show IP Multimedia Subsystem Presence Service options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_imsp_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (get_enabler_state_flag + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many IMSP actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->cancellable) + g_object_unref (context->cancellable); + if (context->device) + g_object_unref (context->device); + if (context->client) + g_object_unref (context->client); + g_slice_free (Context, context); +} + +static void +operation_shutdown (gboolean operation_status) +{ + /* Cleanup context and finish async operation */ + context_free (ctx); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_IMSP_GET_ENABLER_STATE + +static void +get_enabler_state_ready (QmiClientImsp *client, + GAsyncResult *res) +{ + QmiMessageImspGetEnablerStateOutput *output; + QmiImspEnablerState enabler_state; + GError *error = NULL; + + output = qmi_client_imsp_get_enabler_state_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_imsp_get_enabler_state_output_get_result (output, &error)) { + g_printerr ("error: couldn't get enabler state: %s\n", error->message); + g_error_free (error); + qmi_message_imsp_get_enabler_state_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] IMSP enabler state retrieved:\n", qmi_device_get_path_display (ctx->device)); + + if (qmi_message_imsp_get_enabler_state_output_get_enabler_state (output, &enabler_state, NULL)) { + g_print ("\tRegistration status: '%s'\n", + qmi_imsp_enabler_state_get_string (enabler_state)); + } + + qmi_message_imsp_get_enabler_state_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_IMSP_GET_ENABLER_STATE */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_imsp_run (QmiDevice *device, + QmiClientImsp *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_IMSP_GET_ENABLER_STATE + if (get_enabler_state_flag) { + g_debug ("Asynchronously getting enabler state..."); + + qmi_client_imsp_get_enabler_state (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_enabler_state_ready, + NULL); + return; + } +#endif /* HAVE_QMI_MESSAGE_IMSP_GET_ENABLER_STATE */ + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_IMSP */ + diff --git a/pkgs/qmi-nmea/qmicli-link-management.c b/pkgs/qmi-nmea/qmicli-link-management.c new file mode 100644 index 0000000..eb3b1a1 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-link-management.c @@ -0,0 +1,386 @@ +/* -*- 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) 2021 Aleksander Morgado + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +/* Options */ +static gchar *link_list_str; +static gchar *link_add_str; +static gchar *link_delete_str; +static gchar *link_delete_all_str; + +static GOptionEntry entries[] = { + { "link-list", 0, 0, G_OPTION_ARG_STRING, &link_list_str, + "List links created from a given interface", + "[IFACE]" + }, + { "link-add", 0, 0, G_OPTION_ARG_STRING, &link_add_str, + "Create new network interface link", + "[iface=IFACE,prefix=PREFIX[,mux-id=N][,flags=FLAGS]]" + }, + { "link-delete", 0, 0, G_OPTION_ARG_STRING, &link_delete_str, + "Delete a given network interface link", + "[link-iface=IFACE][,[mux-id=N]]" + }, + { "link-delete-all", 0, 0, G_OPTION_ARG_STRING, &link_delete_all_str, + "Delete all network interface links from the given interface", + "[IFACE]" + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_link_management_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("link-management", + "Link management options:", + "Show link management specific options", + NULL, NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_link_management_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (!!link_list_str + + !!link_add_str + + !!link_delete_str + + !!link_delete_all_str); + + if (n_actions > 1) { + g_printerr ("error: too many link management actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +/******************************************************************************/ + +static void +link_delete_all_ready (QmiDevice *dev, + GAsyncResult *res) +{ + g_autoptr(GError) error = NULL; + + if (!qmi_device_delete_all_links_finish (dev, res, &error)) + g_printerr ("error: couldn't delete all links: %s\n", error->message); + else + g_print ("[%s] all links successfully deleted\n", + qmi_device_get_path_display (dev)); + + qmicli_async_operation_done (!error, FALSE); +} + +static void +device_link_delete_all (QmiDevice *dev, + GCancellable *cancellable, + const gchar *iface) +{ + qmi_device_delete_all_links (dev, + iface, + cancellable, + (GAsyncReadyCallback)link_delete_all_ready, + NULL); +} + +typedef struct { + guint mux_id; + gchar *link_iface; +} DeleteLinkProperties; + +static void +link_delete_ready (QmiDevice *dev, + GAsyncResult *res) +{ + g_autoptr(GError) error = NULL; + + if (!qmi_device_delete_link_finish (dev, res, &error)) + g_printerr ("error: couldn't delete link: %s\n", + error->message); + else + g_print ("[%s] link successfully deleted\n", + qmi_device_get_path_display (dev)); + + qmicli_async_operation_done (!error, FALSE); +} + +static gboolean +del_link_properties_handle (const gchar *key, + const gchar *value, + GError **error, + DeleteLinkProperties *props) +{ + if (g_ascii_strcasecmp (key, "mux-id") == 0 && props->mux_id == QMI_DEVICE_MUX_ID_UNBOUND) { + if (!qmicli_read_uint_from_string (value, &props->mux_id)) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "invalid mux-id given: '%s'", value); + return FALSE; + } + return TRUE; + } + + if (g_ascii_strcasecmp (key, "link-iface") == 0 && !props->link_iface) { + props->link_iface = g_strdup (value); + return TRUE; + } + + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "unrecognized or duplicate option '%s'", key); + return FALSE; +} + +static void +device_link_delete (QmiDevice *dev, + GCancellable *cancellable, + const gchar *del_settings) +{ + g_autoptr(GError) error = NULL; + DeleteLinkProperties props = { + .mux_id = QMI_DEVICE_MUX_ID_UNBOUND, + .link_iface = NULL, + }; + + if (!qmicli_parse_key_value_string (del_settings, + &error, + (QmiParseKeyValueForeachFn)del_link_properties_handle, + &props)) { + g_printerr ("error: couldn't parse input add link settings: %s\n", + error->message); + qmicli_async_operation_done (FALSE, FALSE); + return; + } + + if (!props.link_iface) { + g_printerr ("error: missing mandatory 'link-iface' setting\n"); + qmicli_async_operation_done (FALSE, FALSE); + return; + } + + if ((props.mux_id != QMI_DEVICE_MUX_ID_UNBOUND) && + (props.mux_id < QMI_DEVICE_MUX_ID_MIN || props.mux_id > QMI_DEVICE_MUX_ID_MAX)) { + g_printerr ("error: mux id %u out of range [%u,%u]\n", + props.mux_id, QMI_DEVICE_MUX_ID_MIN, QMI_DEVICE_MUX_ID_MAX); + qmicli_async_operation_done (FALSE, FALSE); + return; + } + + qmi_device_delete_link (dev, + props.link_iface, + props.mux_id, + cancellable, + (GAsyncReadyCallback)link_delete_ready, + NULL); + + g_free (props.link_iface); +} + +typedef struct { + guint mux_id; + gchar *iface; + gchar *prefix; + QmiDeviceAddLinkFlags flags; +} AddLinkProperties; + +static void +link_add_ready (QmiDevice *dev, + GAsyncResult *res) +{ + g_autoptr(GError) error = NULL; + g_autofree gchar *link_iface = NULL; + guint mux_id; + + link_iface = qmi_device_add_link_with_flags_finish (dev, res, &mux_id, &error); + if (!link_iface) + g_printerr ("error: couldn't add link: %s\n", + error->message); + else + g_print ("[%s] link successfully added:\n" + " iface name: %s\n" + " mux-id: %u\n", + qmi_device_get_path_display (dev), + link_iface, + mux_id); + + qmicli_async_operation_done (!error, FALSE); +} + +static gboolean +add_link_properties_handle (const gchar *key, + const gchar *value, + GError **error, + AddLinkProperties *props) +{ + if (g_ascii_strcasecmp (key, "mux-id") == 0 && props->mux_id == QMI_DEVICE_MUX_ID_AUTOMATIC) { + if (!qmicli_read_uint_from_string (value, &props->mux_id)) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "invalid mux-id given: '%s'", value); + return FALSE; + } + return TRUE; + } + + if (g_ascii_strcasecmp (key, "iface") == 0 && !props->iface) { + props->iface = g_strdup (value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "prefix") == 0 && !props->prefix) { + props->prefix = g_strdup (value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "flags") == 0 && !props->flags) { + if (!qmicli_read_device_add_link_flags_from_string (value, &props->flags)) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "invalid flags given: '%s'", value); + return FALSE; + } + return TRUE; + } + + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "unrecognized or duplicate option '%s'", key); + return FALSE; +} + +static void +device_link_add (QmiDevice *dev, + GCancellable *cancellable, + const gchar *add_settings) +{ + g_autoptr(GError) error = NULL; + AddLinkProperties props = { + .mux_id = QMI_DEVICE_MUX_ID_AUTOMATIC, + .iface = NULL, + .prefix = NULL, + .flags = QMI_DEVICE_ADD_LINK_FLAGS_NONE, + }; + + if (!qmicli_parse_key_value_string (add_settings, + &error, + (QmiParseKeyValueForeachFn)add_link_properties_handle, + &props)) { + g_printerr ("error: couldn't parse input add link settings: %s\n", + error->message); + qmicli_async_operation_done (FALSE, FALSE); + return; + } + + if (!props.iface) { + g_printerr ("error: missing mandatory 'iface' setting\n"); + qmicli_async_operation_done (FALSE, FALSE); + return; + } + + if (!props.prefix) + props.prefix = g_strdup_printf ("%s.", props.iface); + + if ((props.mux_id != QMI_DEVICE_MUX_ID_AUTOMATIC) && + (props.mux_id < QMI_DEVICE_MUX_ID_MIN || props.mux_id > QMI_DEVICE_MUX_ID_MAX)) { + g_printerr ("error: mux id %u out of range [%u,%u]\n", + props.mux_id, QMI_DEVICE_MUX_ID_MIN, QMI_DEVICE_MUX_ID_MAX); + qmicli_async_operation_done (FALSE, FALSE); + return; + } + + qmi_device_add_link_with_flags (dev, + props.mux_id, + props.iface, + props.prefix, + props.flags, + cancellable, + (GAsyncReadyCallback)link_add_ready, + NULL); + + g_free (props.iface); + g_free (props.prefix); +} + +static void +device_link_list (QmiDevice *dev, + GCancellable *cancellable, + const gchar *iface) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) links = NULL; + + if (!qmi_device_list_links (dev, iface, &links, &error)) + g_printerr ("error: couldn't list links: %s\n", error->message); + else { + guint i; + guint n_links; + + n_links = (links ? links->len : 0); + + g_print ("[%s] found %u links%s\n", + qmi_device_get_path_display (dev), + n_links, + n_links > 0 ? ":" : ""); + for (i = 0; i < n_links; i++) + g_print (" [%u] %s\n", i, (const gchar *) g_ptr_array_index (links, i)); + } + + qmicli_async_operation_done (!error, FALSE); +} + +/******************************************************************************/ +/* Common */ + +void +qmicli_link_management_run (QmiDevice *dev, + GCancellable *cancellable) +{ + if (link_list_str) + device_link_list (dev, cancellable, link_list_str); + else if (link_add_str) + device_link_add (dev, cancellable, link_add_str); + else if (link_delete_str) + device_link_delete (dev, cancellable, link_delete_str); + else if (link_delete_all_str) + device_link_delete_all (dev, cancellable, link_delete_all_str); + else + g_warn_if_reached (); +} diff --git a/pkgs/qmi-nmea/qmicli-loc.c b/pkgs/qmi-nmea/qmicli-loc.c new file mode 100644 index 0000000..946af2e --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-loc.c @@ -0,0 +1,1593 @@ +/* -*- 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) 2018 Thomas Weißschuh + * Copyright (C) 2018 Aleksander Morgado + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_LOC + +#undef VALIDATE_MASK_NONE +#define VALIDATE_MASK_NONE(str) (str ? str : "none") + +/* Context */ + +typedef enum { + MONITORING_STEP_FIRST, + MONITORING_STEP_REGISTER_EVENTS, + MONITORING_STEP_SETUP_TIMEOUT, + MONITORING_STEP_ONGOING, +} MonitoringStep; + +typedef struct { + QmiDevice *device; + QmiClientLoc *client; + GCancellable *cancellable; + guint timeout_id; + MonitoringStep monitoring_step; + guint position_report_indication_id; + guint nmea_indication_id; + guint gnss_sv_info_indication_id; + guint delete_assistance_data_indication_id; + guint get_nmea_types_indication_id; + guint set_nmea_types_indication_id; + guint get_operation_mode_indication_id; + guint set_operation_mode_indication_id; + guint get_engine_lock_indication_id; + guint set_engine_lock_indication_id; +} Context; +static Context *ctx; + +/* Options */ +static gint session_id; +static gboolean start_flag; +static gboolean stop_flag; +static gboolean get_position_report_flag; +static gboolean get_gnss_sv_info_flag; +static gint timeout; +static gboolean follow_position_report_flag; +static gboolean follow_gnss_sv_info_flag; +static gboolean follow_nmea_flag; +static gboolean delete_assistance_data_flag; +static gboolean get_nmea_types_flag; +static gchar *set_nmea_types_str; +static gboolean get_operation_mode_flag; +static gchar *set_operation_mode_str; +static gboolean get_engine_lock_flag; +static gchar *set_engine_lock_str; +static gboolean noop_flag; + +#define DEFAULT_LOC_TIMEOUT_SECS 30 + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_LOC_START || defined HAVE_QMI_MESSAGE_LOC_STOP + { + "loc-session-id", 0, 0, G_OPTION_ARG_INT, &session_id, + "Session ID for the LOC session", + "[ID]", + }, +#endif +#if defined HAVE_QMI_MESSAGE_LOC_START + { + "loc-start", 0, 0, G_OPTION_ARG_NONE, &start_flag, + "Start location gathering", + NULL, + }, +#endif +#if defined HAVE_QMI_MESSAGE_LOC_STOP + { + "loc-stop", 0, 0, G_OPTION_ARG_NONE, &stop_flag, + "Stop location gathering", + NULL, + }, +#endif +#if defined HAVE_QMI_INDICATION_LOC_POSITION_REPORT && defined HAVE_QMI_MESSAGE_LOC_REGISTER_EVENTS + { + "loc-get-position-report", 0, 0, G_OPTION_ARG_NONE, &get_position_report_flag, + "Get position reported by the location module", + NULL, + }, +#endif +#if defined HAVE_QMI_INDICATION_LOC_GNSS_SV_INFO && defined HAVE_QMI_MESSAGE_LOC_REGISTER_EVENTS + { + "loc-get-gnss-sv-info", 0, 0, G_OPTION_ARG_NONE, &get_gnss_sv_info_flag, + "Show GNSS space vehicle info", + NULL, + }, +#endif +#if (defined HAVE_QMI_INDICATION_LOC_POSITION_REPORT || defined HAVE_QMI_INDICATION_LOC_GNSS_SV_INFO) && \ + defined HAVE_QMI_MESSAGE_LOC_REGISTER_EVENTS + { + "loc-timeout", 0, 0, G_OPTION_ARG_INT, &timeout, + "Maximum time to wait for information in `--loc-get-position-report' and `--loc-get-gnss-sv-info' (default 30s)", + "[SECS]", + }, +#endif +#if defined HAVE_QMI_INDICATION_LOC_POSITION_REPORT && defined HAVE_QMI_MESSAGE_LOC_REGISTER_EVENTS + { + "loc-follow-position-report", 0, 0, G_OPTION_ARG_NONE, &follow_position_report_flag, + "Follow all position updates reported by the location module indefinitely", + NULL, + }, +#endif +#if defined HAVE_QMI_INDICATION_LOC_GNSS_SV_INFO && defined HAVE_QMI_MESSAGE_LOC_REGISTER_EVENTS + { + "loc-follow-gnss-sv-info", 0, 0, G_OPTION_ARG_NONE, &follow_gnss_sv_info_flag, + "Follow all GNSS space vehicle info updates reported by the location module indefinitely", + NULL, + }, +#endif +#if defined HAVE_QMI_INDICATION_LOC_NMEA && defined HAVE_QMI_MESSAGE_LOC_REGISTER_EVENTS + { + "loc-follow-nmea", 0, 0, G_OPTION_ARG_NONE, &follow_nmea_flag, + "Follow all NMEA trace updates reported by the location module indefinitely", + NULL, + }, +#endif +#if defined HAVE_QMI_MESSAGE_LOC_DELETE_ASSISTANCE_DATA + { + "loc-delete-assistance-data", 0, 0, G_OPTION_ARG_NONE, &delete_assistance_data_flag, + "Delete positioning assistance data", + NULL, + }, +#endif +#if defined HAVE_QMI_MESSAGE_LOC_GET_NMEA_TYPES + { "loc-get-nmea-types", 0, 0, G_OPTION_ARG_NONE, &get_nmea_types_flag, + "Get list of enabled NMEA traces", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_LOC_SET_NMEA_TYPES + { "loc-set-nmea-types", 0, 0, G_OPTION_ARG_STRING, &set_nmea_types_str, + "Set list of enabled NMEA traces", + "[type1|type2|type3...]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_LOC_GET_OPERATION_MODE + { "loc-get-operation-mode", 0, 0, G_OPTION_ARG_NONE, &get_operation_mode_flag, + "Get operation mode", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_LOC_SET_OPERATION_MODE + { "loc-set-operation-mode", 0, 0, G_OPTION_ARG_STRING, &set_operation_mode_str, + "Set operation mode", + "[default|msb|msa|standalone|cellid|wwan]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_LOC_GET_ENGINE_LOCK + { "loc-get-engine-lock", 0, 0, G_OPTION_ARG_NONE, &get_engine_lock_flag, + "Get engine lock status", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_LOC_SET_ENGINE_LOCK + { "loc-set-engine-lock", 0, 0, G_OPTION_ARG_STRING, &set_engine_lock_str, + "Set engine lock status", + "[none|mi|mt|all]" + }, +#endif + { "loc-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a LOC client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_loc_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("loc", + "LOC options:", + "Show location options", NULL, NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_loc_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + gboolean follow_action; + + if (checked) + return !!n_actions; + + /* Let's define the following actions: + * - Start location engine + * - Stop location engine + * - Show current position (oneshot). + * - Show current satellite info (oneshot). + * - Follow updates indefinitely, including either position, satellite info or NMEA traces. + * - Other single-request operations. + */ + follow_action = !!(follow_position_report_flag + follow_gnss_sv_info_flag + follow_nmea_flag); + n_actions = (start_flag + + stop_flag + + get_position_report_flag + + get_gnss_sv_info_flag + + follow_action + + delete_assistance_data_flag + + get_nmea_types_flag + + !!set_nmea_types_str + + get_operation_mode_flag + + !!set_operation_mode_str + + get_engine_lock_flag + + !!set_engine_lock_str + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many LOC actions requested\n"); + exit (EXIT_FAILURE); + } + + if (session_id < 0 || session_id > G_MAXUINT8) { + g_printerr ("error: invalid session ID: %d [0,%u]\n", session_id, G_MAXUINT8); + exit (EXIT_FAILURE); + } + + if (timeout < 0) { + g_printerr ("error: invalid timeout: %d", timeout); + exit (EXIT_FAILURE); + } + + if (timeout > 0 && !(get_position_report_flag || get_gnss_sv_info_flag)) { + g_printerr ("error: `--loc-timeout' is only applicable with `--loc-get-position-report' or `--loc-get-gnss-sv-info'\n"); + exit (EXIT_FAILURE); + } + + /* Actions that require receiving QMI indication messages must specify that + * indications are expected. */ + if (get_position_report_flag || + get_gnss_sv_info_flag || + follow_action || + delete_assistance_data_flag || + get_nmea_types_flag || + set_nmea_types_str || + get_operation_mode_flag || + set_operation_mode_str || + get_engine_lock_flag || + set_engine_lock_str) + qmicli_expect_indications (); + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->timeout_id) + g_source_remove (context->timeout_id); + + if (context->position_report_indication_id) + g_signal_handler_disconnect (context->client, context->position_report_indication_id); + + if (context->gnss_sv_info_indication_id) + g_signal_handler_disconnect (context->client, context->gnss_sv_info_indication_id); + + if (context->nmea_indication_id) + g_signal_handler_disconnect (context->client, context->nmea_indication_id); + + if (context->delete_assistance_data_indication_id) + g_signal_handler_disconnect (context->client, context->delete_assistance_data_indication_id); + + if (context->get_nmea_types_indication_id) + g_signal_handler_disconnect (context->client, context->get_nmea_types_indication_id); + + if (context->set_nmea_types_indication_id) + g_signal_handler_disconnect (context->client, context->set_nmea_types_indication_id); + + if (context->get_operation_mode_indication_id) + g_signal_handler_disconnect (context->client, context->get_operation_mode_indication_id); + + if (context->set_operation_mode_indication_id) + g_signal_handler_disconnect (context->client, context->set_operation_mode_indication_id); + + if (context->get_engine_lock_indication_id) + g_signal_handler_disconnect (context->client, context->get_engine_lock_indication_id); + + if (context->set_engine_lock_indication_id) + g_signal_handler_disconnect (context->client, context->set_engine_lock_indication_id); + + g_clear_object (&context->cancellable); + g_clear_object (&context->client); + g_clear_object (&context->device); + g_slice_free (Context, context); +} + +static void +operation_shutdown (gboolean operation_status) +{ + context_free (ctx); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if (defined HAVE_QMI_INDICATION_LOC_POSITION_REPORT || \ + defined HAVE_QMI_INDICATION_LOC_GNSS_SV_INFO || \ + defined HAVE_QMI_INDICATION_LOC_NMEA) && \ + defined HAVE_QMI_MESSAGE_LOC_REGISTER_EVENTS + +static void monitoring_step_run (void); + +static gboolean +monitoring_timed_out (void) +{ + ctx->timeout_id = 0; + g_printerr ("error: operation failed: timeout\n"); + operation_shutdown (FALSE); + return G_SOURCE_REMOVE; +} + +static void +monitoring_cancelled (GCancellable *cancellable) +{ + /* For GET operations, this is a failure */ + if (get_position_report_flag || get_gnss_sv_info_flag) { + g_printerr ("error: operation failed: cancelled\n"); + operation_shutdown (FALSE); + return; + } + + /* For FOLLOW operations, silently exit */ + if (follow_position_report_flag || follow_gnss_sv_info_flag || follow_nmea_flag) { + operation_shutdown (TRUE); + return; + } + + g_assert_not_reached (); +} + +#endif /* HAVE_QMI_INDICATION_LOC_POSITION_REPORT + * HAVE_QMI_INDICATION_LOC_GNSS_SV_INFO + * HAVE_QMI_INDICATION_LOC_NMEA */ + +#if defined HAVE_QMI_INDICATION_LOC_NMEA && defined HAVE_QMI_MESSAGE_LOC_REGISTER_EVENTS + +static void +nmea_received (QmiClientLoc *client, + QmiIndicationLocNmeaOutput *output) +{ + const gchar *nmea = NULL; + + qmi_indication_loc_nmea_output_get_nmea_string (output, &nmea, NULL); + if (nmea) + /* Note: NMEA traces already have an EOL */ + g_print ("%s", nmea); +} + +#endif /* HAVE_QMI_INDICATION_LOC_NMEA */ + +#if defined HAVE_QMI_INDICATION_LOC_GNSS_SV_INFO && defined HAVE_QMI_MESSAGE_LOC_REGISTER_EVENTS + +static void +gnss_sv_info_received (QmiClientLoc *client, + QmiIndicationLocGnssSvInfoOutput *output) +{ + GArray *satellite_infos = NULL; + guint i, num_satellite_infos; + gboolean altitude_assumed; + + if (qmi_indication_loc_gnss_sv_info_output_get_altitude_assumed (output, &altitude_assumed, NULL)) + g_print ("[gnss sv info] Altitude assumed: %s\n", altitude_assumed ? "yes" : "no"); + else + g_print ("[gnss sv info] Altitude assumed: n/a\n"); + + qmi_indication_loc_gnss_sv_info_output_get_list (output, &satellite_infos, NULL); + + num_satellite_infos = satellite_infos ? satellite_infos->len : 0; + g_print ("[gnss sv info] %d satellites detected:\n", num_satellite_infos); + for (i = 0; i < num_satellite_infos; i++) { + QmiIndicationLocGnssSvInfoOutputListElement *element; + + element = &g_array_index (satellite_infos, QmiIndicationLocGnssSvInfoOutputListElement, i); + g_print (" [satellite #%u]\n", i); + g_print (" system: %s\n", (element->valid_information & QMI_LOC_SATELLITE_VALID_INFORMATION_SYSTEM) ? qmi_loc_system_get_string (element->system) : "n/a"); + if (element->valid_information & QMI_LOC_SATELLITE_VALID_INFORMATION_GNSS_SATELLITE_ID) + g_print (" satellite id: %u\n", element->gnss_satellite_id); + else + g_print (" satellite id: n/a\n"); + g_print (" health status: %s\n", (element->valid_information & QMI_LOC_SATELLITE_VALID_INFORMATION_HEALTH_STATUS) ? qmi_loc_health_status_get_string (element->health_status) : "n/a"); + g_print (" satellite status: %s\n", (element->valid_information & QMI_LOC_SATELLITE_VALID_INFORMATION_PROCESS_STATUS) ? qmi_loc_satellite_status_get_string (element->satellite_status) : "n/a"); + g_print (" navigation data: %s\n", (element->valid_information & QMI_LOC_SATELLITE_VALID_INFORMATION_SATELLITE_INFO_MASK) ? qmi_loc_navigation_data_get_string (element->navigation_data) : "n/a"); + + if (element->valid_information & QMI_LOC_SATELLITE_VALID_INFORMATION_ELEVATION) + g_print (" elevation: %lf\n", (gdouble)element->elevation_degrees); + else + g_print (" elevation: n/a\n"); + + if (element->valid_information & QMI_LOC_SATELLITE_VALID_INFORMATION_AZIMUTH) + g_print (" azimuth: %lf\n", (gdouble)element->azimuth_degrees); + else + g_print (" azimuth: n/a\n"); + + if (element->valid_information & QMI_LOC_SATELLITE_VALID_INFORMATION_SIGNAL_TO_NOISE_RATIO) + g_print (" SNR: %lf\n", (gdouble)element->signal_to_noise_ratio_bhz); + else + g_print (" SNR: n/a\n"); + } + + /* Terminate GET request */ + if (get_gnss_sv_info_flag) + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_INDICATION_LOC_GNSS_SV_INFO */ + +#if defined HAVE_QMI_INDICATION_LOC_POSITION_REPORT && defined HAVE_QMI_MESSAGE_LOC_REGISTER_EVENTS + +static void +position_report_received (QmiClientLoc *client, + QmiIndicationLocPositionReportOutput *output) +{ + QmiLocSessionStatus status; + + qmi_indication_loc_position_report_output_get_session_status (output, &status, NULL); + g_print ("[position report] status: %s\n", qmi_loc_session_status_get_string (status)); + + if (status == QMI_LOC_SESSION_STATUS_SUCCESS || status == QMI_LOC_SESSION_STATUS_IN_PROGRESS) { + gdouble auxd; + gfloat auxf; + guint8 aux8; + guint32 aux32; + guint64 aux64; + QmiLocReliability reliability; + QmiLocTechnologyUsed technology; + QmiLocTimeSource time_source; + QmiLocSensorDataUsage sensor_data_usage; + gfloat pdop; + gfloat hdop; + gfloat vdop; + guint16 gps_weeks; + guint32 gps_time_of_week_milliseconds; + gboolean auxb; + GArray *array; + + if (qmi_indication_loc_position_report_output_get_latitude (output, &auxd, NULL)) + g_print (" latitude: %lf degrees\n", auxd); + else + g_print (" latitude: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_longitude (output, &auxd, NULL)) + g_print (" longitude: %lf degrees\n", auxd); + else + g_print (" longitude: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_horizontal_uncertainty_circular (output, &auxf, NULL)) + g_print (" circular horizontal position uncertainty: %lf meters\n", (gdouble)auxf); + else + g_print (" circular horizontal position uncertainty: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_horizontal_uncertainty_elliptical_minor (output, &auxf, NULL)) + g_print (" horizontal elliptical uncertainty (semi-minor axis): %lf meters\n", (gdouble)auxf); + else + g_print (" horizontal elliptical uncertainty (semi-minor axis): n/a\n"); + + if (qmi_indication_loc_position_report_output_get_horizontal_uncertainty_elliptical_major (output, &auxf, NULL)) + g_print (" horizontal elliptical uncertainty (semi-major axis): %lf meters\n", (gdouble)auxf); + else + g_print (" horizontal elliptical uncertainty (semi-major axis): n/a\n"); + + if (qmi_indication_loc_position_report_output_get_horizontal_uncertainty_elliptical_azimuth (output, &auxf, NULL)) + g_print (" horizontal elliptical uncertainty azimuth: %lf meters\n", (gdouble)auxf); + else + g_print (" horizontal elliptical uncertainty azimuth: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_horizontal_confidence (output, &aux8, NULL)) + g_print (" horizontal confidence: %u%%\n", aux8); + else + g_print (" horizontal confidence: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_horizontal_reliability (output, &reliability, NULL)) + g_print (" horizontal reliability: %s\n", qmi_loc_reliability_get_string (reliability)); + else + g_print (" horizontal reliability: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_horizontal_speed (output, &auxf, NULL)) + g_print (" horizontal speed: %lf m/s\n", (gdouble)auxf); + else + g_print (" horizontal speed: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_speed_uncertainty (output, &auxf, NULL)) + g_print (" speed uncertainty: %lf m/s\n", (gdouble)auxf); + else + g_print (" speed uncertainty: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_altitude_from_ellipsoid (output, &auxf, NULL)) + g_print (" altitude w.r.t. ellipsoid: %lf meters\n", (gdouble)auxf); + else + g_print (" altitude w.r.t. ellipsoid: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_altitude_from_sealevel (output, &auxf, NULL)) + g_print (" altitude w.r.t. mean sea level: %lf meters\n", (gdouble)auxf); + else + g_print (" altitude w.r.t. mean sea level: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_vertical_uncertainty (output, &auxf, NULL)) + g_print (" vertical uncertainty: %lf meters\n", (gdouble)auxf); + else + g_print (" vertical uncertainty: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_vertical_confidence (output, &aux8, NULL)) + g_print (" vertical confidence: %u%%\n", aux8); + else + g_print (" vertical confidence: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_vertical_reliability (output, &reliability, NULL)) + g_print (" vertical reliability: %s\n", qmi_loc_reliability_get_string (reliability)); + else + g_print (" vertical reliability: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_vertical_speed (output, &auxf, NULL)) + g_print (" vertical speed: %lf m/s\n", (gdouble)auxf); + else + g_print (" vertical speed: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_heading (output, &auxf, NULL)) + g_print (" heading: %lf degrees\n", (gdouble)auxf); + else + g_print (" heading: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_heading_uncertainty (output, &auxf, NULL)) + g_print (" heading uncertainty: %lf meters\n", (gdouble)auxf); + else + g_print (" heading uncertainty: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_magnetic_deviation (output, &auxf, NULL)) + g_print (" magnetic deviation: %lf degrees\n", (gdouble)auxf); + else + g_print (" magnetic deviation: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_technology_used (output, &technology, NULL)) { + g_autofree gchar *technology_str = NULL; + + technology_str = qmi_loc_technology_used_build_string_from_mask (technology); + g_print (" technology: %s\n", VALIDATE_MASK_NONE (technology_str)); + } else + g_print (" technology: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_dop (output, &pdop, &hdop, &vdop, NULL)) { + g_print (" position DOP: %lf\n", (gdouble)pdop); + g_print (" horizontal DOP: %lf\n", (gdouble)hdop); + g_print (" vertical DOP: %lf\n", (gdouble)vdop); + } else { + g_print (" position DOP: n/a\n"); + g_print (" horizontal DOP: n/a\n"); + g_print (" vertical DOP: n/a\n"); + } + + if (qmi_indication_loc_position_report_output_get_utc_timestamp (output, &aux64, NULL)) + g_print (" UTC timestamp: %" G_GUINT64_FORMAT " ms\n", aux64); + else + g_print (" UTC timestamp: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_leap_seconds (output, &aux8, NULL)) + g_print (" Leap seconds: %u\n", aux8); + else + g_print (" Leap seconds: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_gps_date_time (output, &gps_weeks, &gps_time_of_week_milliseconds, NULL)) + g_print (" GPS time: %u weeks and %ums\n", gps_weeks, gps_time_of_week_milliseconds); + else + g_print (" GPS time: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_time_uncertainty (output, &auxf, NULL)) + g_print (" time uncertainty: %lf ms\n", (gdouble)auxf); + else + g_print (" time uncertainty: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_time_source (output, &time_source, NULL)) + g_print (" time source: %s\n", qmi_loc_time_source_get_string (time_source)); + else + g_print (" time source: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_sensor_data_usage (output, &sensor_data_usage, NULL)) { + g_autofree gchar *sensor_data_usage_str = NULL; + + sensor_data_usage_str = qmi_loc_sensor_data_usage_build_string_from_mask (sensor_data_usage); + g_print (" sensor data usage: %s\n", VALIDATE_MASK_NONE (sensor_data_usage_str)); + } else + g_print (" sensor data usage: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_session_fix_count (output, &aux32, NULL)) + g_print (" Fix count: %u\n", aux32); + else + g_print (" Fix count: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_satellites_used (output, &array, NULL)) { + guint i; + + g_print (" Satellites used: "); + for (i = 0; i < array->len; i++) { + guint16 sv_id; + + /* + * - For GPS: 1 to 32 + * - For SBAS: 33 to 64 + * - For GLONASS: 65 to 96 + * - For QZSS: 193 to 197 + * - For BDS: 201 to 237 + */ + sv_id = g_array_index (array, guint16, i); + g_print ("%u%s", sv_id, i == array->len - 1 ? "" : ","); + } + g_print ("\n"); + } else + g_print (" Satellites used: n/a\n"); + + if (qmi_indication_loc_position_report_output_get_altitude_assumed (output, &auxb, NULL)) + g_print (" Altitude assumed: %s\n", auxb ? "yes" : "no"); + else + g_print (" Altitude assumed: n/a\n"); + + /* Terminate GET request */ + if (get_position_report_flag) + operation_shutdown (TRUE); + + return; + } + + /* Otherwise, treat as error */ + g_printerr ("[position report] error: %s\n", qmi_loc_session_status_get_string (status)); + if (get_position_report_flag) + operation_shutdown (FALSE); +} + +#endif /* HAVE_QMI_INDICATION_LOC_POSITION_REPORT */ + +#if (defined HAVE_QMI_INDICATION_LOC_POSITION_REPORT || \ + defined HAVE_QMI_INDICATION_LOC_GNSS_SV_INFO || \ + defined HAVE_QMI_INDICATION_LOC_NMEA) && \ + defined HAVE_QMI_MESSAGE_LOC_REGISTER_EVENTS + +static void +monitoring_step_ongoing (void) +{ +#if defined HAVE_QMI_INDICATION_LOC_POSITION_REPORT + if (get_position_report_flag || follow_position_report_flag) + ctx->position_report_indication_id = g_signal_connect (ctx->client, + "position-report", + G_CALLBACK (position_report_received), + NULL); +#endif + +#if defined HAVE_QMI_INDICATION_LOC_GNSS_SV_INFO + if (get_gnss_sv_info_flag || follow_gnss_sv_info_flag) + ctx->gnss_sv_info_indication_id = g_signal_connect (ctx->client, + "gnss-sv-info", + G_CALLBACK (gnss_sv_info_received), + NULL); +#endif + +#if defined HAVE_QMI_INDICATION_LOC_NMEA + if (follow_nmea_flag) + ctx->nmea_indication_id = g_signal_connect (ctx->client, + "nmea", + G_CALLBACK (nmea_received), + NULL); +#endif + + g_assert (ctx->position_report_indication_id || + ctx->gnss_sv_info_indication_id || + ctx->nmea_indication_id); +} + +static void +monitoring_step_setup_timeout (void) +{ + /* User can use Ctrl+C to cancel the monitoring at any time */ + g_cancellable_connect (ctx->cancellable, + G_CALLBACK (monitoring_cancelled), + NULL, + NULL); + + /* For non-follow requests, we also setup a timeout */ + if (get_position_report_flag || get_gnss_sv_info_flag) + ctx->timeout_id = g_timeout_add_seconds (timeout > 0 ? timeout : DEFAULT_LOC_TIMEOUT_SECS, + (GSourceFunc) monitoring_timed_out, + NULL); + + /* Go on */ + ctx->monitoring_step++; + monitoring_step_run (); +} + +static void +register_events_ready (QmiClientLoc *client, + GAsyncResult *res) +{ + QmiMessageLocRegisterEventsOutput *output; + GError *error = NULL; + + output = qmi_client_loc_register_events_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_loc_register_events_output_get_result (output, &error)) { + g_printerr ("error: could not register location tracking events: %s\n", error->message); + qmi_message_loc_register_events_output_unref (output); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + g_debug ("Registered location tracking events..."); + + /* Go on */ + ctx->monitoring_step++; + monitoring_step_run (); + + qmi_message_loc_register_events_output_unref (output); +} + +static void +monitoring_step_register_events (void) +{ + QmiMessageLocRegisterEventsInput *re_input; + QmiLocEventRegistrationFlag indication_mask = 0; + + /* Configure events to enable */ + + if (get_position_report_flag || follow_position_report_flag) + indication_mask |= QMI_LOC_EVENT_REGISTRATION_FLAG_POSITION_REPORT; + + if (get_gnss_sv_info_flag || follow_gnss_sv_info_flag) + indication_mask |= QMI_LOC_EVENT_REGISTRATION_FLAG_GNSS_SATELLITE_INFO; + + if (follow_nmea_flag) + indication_mask |= QMI_LOC_EVENT_REGISTRATION_FLAG_NMEA; + + g_assert (indication_mask); + + re_input = qmi_message_loc_register_events_input_new (); + qmi_message_loc_register_events_input_set_event_registration_mask ( + re_input, indication_mask, NULL); + qmi_client_loc_register_events (ctx->client, + re_input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) register_events_ready, + NULL); + qmi_message_loc_register_events_input_unref (re_input); +} + +static void +monitoring_step_run (void) +{ + switch (ctx->monitoring_step) { + case MONITORING_STEP_FIRST: + ctx->monitoring_step++; + /* fall through */ + + case MONITORING_STEP_REGISTER_EVENTS: + monitoring_step_register_events (); + return; + + case MONITORING_STEP_SETUP_TIMEOUT: + monitoring_step_setup_timeout (); + return; + + case MONITORING_STEP_ONGOING: + monitoring_step_ongoing (); + return; + + default: + g_assert_not_reached(); + } +} + +#endif /* HAVE_QMI_INDICATION_LOC_POSITION_REPORT + * HAVE_QMI_INDICATION_LOC_GNSS_SV_INFO + * HAVE_QMI_INDICATION_LOC_NMEA */ + +#if defined HAVE_QMI_MESSAGE_LOC_DELETE_ASSISTANCE_DATA + +static gboolean +delete_assistance_data_timed_out (void) +{ + ctx->timeout_id = 0; + g_printerr ("error: operation failed: timeout\n"); + operation_shutdown (FALSE); + return G_SOURCE_REMOVE; +} + +static void +delete_assistance_data_received (QmiClientLoc *client, + QmiIndicationLocDeleteAssistanceDataOutput *output) +{ + QmiLocIndicationStatus status; + GError *error = NULL; + + if (!qmi_indication_loc_delete_assistance_data_output_get_indication_status (output, &status, &error)) { + g_printerr ("error: couldn't delete assistance data: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + g_print ("Successfully deleted assistance data\n"); + operation_shutdown (TRUE); +} + +static void +delete_assistance_data_ready (QmiClientLoc *client, + GAsyncResult *res) +{ + QmiMessageLocDeleteAssistanceDataOutput *output; + GError *error = NULL; + + output = qmi_client_loc_delete_assistance_data_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_loc_delete_assistance_data_output_get_result (output, &error)) { + g_printerr ("error: could not delete assistance data: %s\n", error->message); + qmi_message_loc_delete_assistance_data_output_unref (output); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + /* Wait for response asynchronously */ + ctx->timeout_id = g_timeout_add_seconds (timeout > 0 ? timeout : DEFAULT_LOC_TIMEOUT_SECS, + (GSourceFunc) delete_assistance_data_timed_out, + NULL); + + ctx->delete_assistance_data_indication_id = g_signal_connect (ctx->client, + "delete-assistance-data", + G_CALLBACK (delete_assistance_data_received), + NULL); + + qmi_message_loc_delete_assistance_data_output_unref (output); +} + +#endif /* HAVE_QMI_MESSAGE_LOC_DELETE_ASSISTANCE_DATA */ + +#if defined HAVE_QMI_MESSAGE_LOC_GET_NMEA_TYPES + +static gboolean +get_nmea_types_timed_out (void) +{ + ctx->timeout_id = 0; + g_printerr ("error: operation failed: timeout\n"); + operation_shutdown (FALSE); + return G_SOURCE_REMOVE; +} + +static void +get_nmea_types_received (QmiClientLoc *client, + QmiIndicationLocGetNmeaTypesOutput *output) +{ + QmiLocIndicationStatus status; + QmiLocNmeaType nmea_types_mask; + g_autoptr(GError) error = NULL; + g_autofree gchar *nmea_types_str = NULL; + + if (!qmi_indication_loc_get_nmea_types_output_get_indication_status (output, &status, &error)) { + g_printerr ("error: couldn't get NMEA types: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_indication_loc_get_nmea_types_output_get_nmea_types (output, &nmea_types_mask, NULL)) { + g_printerr ("error: couldn't get NMEA types: missing\n"); + operation_shutdown (FALSE); + return; + } + + nmea_types_str = qmi_loc_nmea_type_build_string_from_mask (nmea_types_mask); + g_print ("Successfully retrieved NMEA types: %s\n", VALIDATE_MASK_NONE (nmea_types_str)); + operation_shutdown (TRUE); +} + +static void +get_nmea_types_ready (QmiClientLoc *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageLocGetNmeaTypesOutput) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_loc_get_nmea_types_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_loc_get_nmea_types_output_get_result (output, &error)) { + g_printerr ("error: could not get NMEA types: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + /* Wait for response asynchronously */ + ctx->timeout_id = g_timeout_add_seconds (timeout > 0 ? timeout : DEFAULT_LOC_TIMEOUT_SECS, + (GSourceFunc) get_nmea_types_timed_out, + NULL); + + ctx->get_nmea_types_indication_id = g_signal_connect (ctx->client, + "get-nmea-types", + G_CALLBACK (get_nmea_types_received), + NULL); +} + +#endif /* HAVE_QMI_MESSAGE_LOC_GET_NMEA_TYPES */ + +#if defined HAVE_QMI_MESSAGE_LOC_SET_NMEA_TYPES + +static gboolean +set_nmea_types_timed_out (void) +{ + ctx->timeout_id = 0; + g_printerr ("error: operation failed: timeout\n"); + operation_shutdown (FALSE); + return G_SOURCE_REMOVE; +} + +static void +set_nmea_types_received (QmiClientLoc *client, + QmiIndicationLocSetNmeaTypesOutput *output) +{ + QmiLocIndicationStatus status; + g_autoptr(GError) error = NULL; + + if (!qmi_indication_loc_set_nmea_types_output_get_indication_status (output, &status, &error)) { + g_printerr ("error: couldn't set NMEA types: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("Successfully set NMEA types\n"); + operation_shutdown (TRUE); +} + +static void +set_nmea_types_ready (QmiClientLoc *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageLocSetNmeaTypesOutput) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_loc_set_nmea_types_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_loc_set_nmea_types_output_get_result (output, &error)) { + g_printerr ("error: could not set NMEA types: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + /* Wait for response asynchronously */ + ctx->timeout_id = g_timeout_add_seconds (timeout > 0 ? timeout : DEFAULT_LOC_TIMEOUT_SECS, + (GSourceFunc) set_nmea_types_timed_out, + NULL); + + ctx->set_nmea_types_indication_id = g_signal_connect (ctx->client, + "set-nmea-types", + G_CALLBACK (set_nmea_types_received), + NULL); +} + +static QmiMessageLocSetNmeaTypesInput * +set_nmea_types_input_create (const gchar *str) +{ + g_autoptr(QmiMessageLocSetNmeaTypesInput) input = NULL; + g_autoptr(GError) error = NULL; + QmiLocNmeaType nmea_type_mask; + + if (!qmicli_read_loc_nmea_type_from_string (str, &nmea_type_mask)) { + g_printerr ("error: couldn't parse input string as NMEA types: '%s'\n", str); + return NULL; + } + + input = qmi_message_loc_set_nmea_types_input_new (); + if (!qmi_message_loc_set_nmea_types_input_set_nmea_types (input, nmea_type_mask, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", error->message); + return NULL; + } + + return g_steal_pointer (&input); +} + +#endif /* HAVE_QMI_MESSAGE_LOC_SET_NMEA_TYPES */ + +#if defined HAVE_QMI_MESSAGE_LOC_GET_OPERATION_MODE + +static gboolean +get_operation_mode_timed_out (void) +{ + ctx->timeout_id = 0; + g_printerr ("error: operation failed: timeout\n"); + operation_shutdown (FALSE); + return G_SOURCE_REMOVE; +} + +static void +get_operation_mode_received (QmiClientLoc *client, + QmiIndicationLocGetOperationModeOutput *output) +{ + QmiLocIndicationStatus status; + QmiLocOperationMode operation_mode; + g_autoptr(GError) error = NULL; + + if (!qmi_indication_loc_get_operation_mode_output_get_indication_status (output, &status, &error)) { + g_printerr ("error: couldn't get operation mode %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_indication_loc_get_operation_mode_output_get_operation_mode (output, &operation_mode, NULL)) { + g_printerr ("error: couldn't get operation mode: missing\n"); + operation_shutdown (FALSE); + return; + } + + g_print ("Successfully retrieved operation mode: %s\n", qmi_loc_operation_mode_get_string (operation_mode)); + operation_shutdown (TRUE); +} + +static void +get_operation_mode_ready (QmiClientLoc *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageLocGetOperationModeOutput) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_loc_get_operation_mode_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_loc_get_operation_mode_output_get_result (output, &error)) { + g_printerr ("error: could not get operation mode: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + /* Wait for response asynchronously */ + ctx->timeout_id = g_timeout_add_seconds (timeout > 0 ? timeout : DEFAULT_LOC_TIMEOUT_SECS, + (GSourceFunc) get_operation_mode_timed_out, + NULL); + + ctx->get_operation_mode_indication_id = g_signal_connect (ctx->client, + "get-operation-mode", + G_CALLBACK (get_operation_mode_received), + NULL); +} +#endif /* HAVE_QMI_MESSAGE_LOC_GET_OPERATION_MODE */ + +#if defined HAVE_QMI_MESSAGE_LOC_SET_OPERATION_MODE + +static gboolean +set_operation_mode_timed_out (void) +{ + ctx->timeout_id = 0; + g_printerr ("error: operation failed: timeout\n"); + operation_shutdown (FALSE); + return G_SOURCE_REMOVE; +} + +static void +set_operation_mode_received (QmiClientLoc *client, + QmiIndicationLocSetOperationModeOutput *output) +{ + QmiLocIndicationStatus status; + g_autoptr(GError) error = NULL; + + if (!qmi_indication_loc_set_operation_mode_output_get_indication_status (output, &status, &error)) { + g_printerr ("error: couldn't set operation mode: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("Successfully set operation mode\n"); + operation_shutdown (TRUE); +} + +static void +set_operation_mode_ready (QmiClientLoc *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageLocSetOperationModeOutput) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_loc_set_operation_mode_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_loc_set_operation_mode_output_get_result (output, &error)) { + g_printerr ("error: could not set operation mode: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + /* Wait for response asynchronously */ + ctx->timeout_id = g_timeout_add_seconds (timeout > 0 ? timeout : DEFAULT_LOC_TIMEOUT_SECS, + (GSourceFunc) set_operation_mode_timed_out, + NULL); + + ctx->set_operation_mode_indication_id = g_signal_connect (ctx->client, + "set-operation-mode", + G_CALLBACK (set_operation_mode_received), + NULL); +} + +static QmiMessageLocSetOperationModeInput * +set_operation_mode_input_create (const gchar *str) +{ + g_autoptr(QmiMessageLocSetOperationModeInput) input = NULL; + g_autoptr(GError) error = NULL; + QmiLocOperationMode operation_mode; + + if (!qmicli_read_loc_operation_mode_from_string (str, &operation_mode)) + return NULL; + + input = qmi_message_loc_set_operation_mode_input_new (); + if (!qmi_message_loc_set_operation_mode_input_set_operation_mode (input, operation_mode, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", error->message); + return NULL; + } + + return g_steal_pointer (&input); +} + +#endif /* HAVE_QMI_MESSAGE_LOC_SET_OPERATION_MODE */ + +#if defined HAVE_QMI_MESSAGE_LOC_GET_ENGINE_LOCK + +static gboolean +get_engine_lock_timed_out (void) +{ + ctx->timeout_id = 0; + g_printerr ("error: operation failed: timeout\n"); + operation_shutdown (FALSE); + return G_SOURCE_REMOVE; +} + +static void +get_engine_lock_received (QmiClientLoc *client, + QmiIndicationLocGetEngineLockOutput *output) +{ + QmiLocIndicationStatus status; + QmiLocLockType type; + g_autoptr(GError) error = NULL; + + if (!qmi_indication_loc_get_engine_lock_output_get_indication_status (output, &status, &error)) { + g_printerr ("error: couldn't get engine lock %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_indication_loc_get_engine_lock_output_get_lock_type (output, &type, NULL)) { + g_printerr ("error: couldn't get engine lock: missing\n"); + operation_shutdown (FALSE); + return; + } + + g_print ("Successfully retrieved engine lock: %s\n", qmi_loc_lock_type_get_string (type)); + operation_shutdown (TRUE); +} + +static void +get_engine_lock_ready (QmiClientLoc *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageLocGetEngineLockOutput) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_loc_get_engine_lock_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_loc_get_engine_lock_output_get_result (output, &error)) { + g_printerr ("error: could not get engine lock: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + /* Wait for response asynchronously */ + ctx->timeout_id = g_timeout_add_seconds (timeout > 0 ? timeout : DEFAULT_LOC_TIMEOUT_SECS, + (GSourceFunc) get_engine_lock_timed_out, + NULL); + + ctx->get_engine_lock_indication_id = g_signal_connect (ctx->client, + "get-engine-lock", + G_CALLBACK (get_engine_lock_received), + NULL); +} +#endif /* HAVE_QMI_MESSAGE_LOC_GET_ENGINE_LOCK */ + +#if defined HAVE_QMI_MESSAGE_LOC_SET_ENGINE_LOCK + +static gboolean +set_engine_lock_timed_out (void) +{ + ctx->timeout_id = 0; + g_printerr ("error: operation failed: timeout\n"); + operation_shutdown (FALSE); + return G_SOURCE_REMOVE; +} + +static void +set_engine_lock_received (QmiClientLoc *client, + QmiIndicationLocSetEngineLockOutput *output) +{ + QmiLocIndicationStatus status; + g_autoptr(GError) error = NULL; + + if (!qmi_indication_loc_set_engine_lock_output_get_indication_status (output, &status, &error)) { + g_printerr ("error: couldn't set engine lock: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("Successfully set engine lock\n"); + operation_shutdown (TRUE); +} + +static void +set_engine_lock_ready (QmiClientLoc *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageLocSetEngineLockOutput) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_loc_set_engine_lock_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_loc_set_engine_lock_output_get_result (output, &error)) { + g_printerr ("error: could not set engine lock: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + /* Wait for response asynchronously */ + ctx->timeout_id = g_timeout_add_seconds (timeout > 0 ? timeout : DEFAULT_LOC_TIMEOUT_SECS, + (GSourceFunc) set_engine_lock_timed_out, + NULL); + + ctx->set_engine_lock_indication_id = g_signal_connect (ctx->client, + "set-engine-lock", + G_CALLBACK (set_engine_lock_received), + NULL); +} + +static QmiMessageLocSetEngineLockInput * +set_engine_lock_input_create (const gchar *str) +{ + g_autoptr(QmiMessageLocSetEngineLockInput) input = NULL; + g_autoptr(GError) error = NULL; + QmiLocLockType type; + + if (!qmicli_read_loc_lock_type_from_string (str, &type)) + return NULL; + + input = qmi_message_loc_set_engine_lock_input_new (); + if (!qmi_message_loc_set_engine_lock_input_set_lock_type (input, type, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", error->message); + return NULL; + } + + return g_steal_pointer (&input); +} + +#endif /* HAVE_QMI_MESSAGE_LOC_SET_ENGINE_LOCK */ + +#if defined HAVE_QMI_MESSAGE_LOC_STOP + +static void +stop_ready (QmiClientLoc *client, + GAsyncResult *res) +{ + QmiMessageLocStopOutput *output; + GError *error = NULL; + + output = qmi_client_loc_stop_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_loc_stop_output_get_result (output, &error)) { + g_printerr ("error: could not stop location tracking: %s\n", error->message); + qmi_message_loc_stop_output_unref (output); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully stopped location tracking (session id %u)\n", + qmi_device_get_path_display (ctx->device), session_id); + + qmi_message_loc_stop_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_LOC_STOP */ + +#if defined HAVE_QMI_MESSAGE_LOC_START + +static void +start_ready (QmiClientLoc *client, + GAsyncResult *res) +{ + QmiMessageLocStartOutput *output; + GError *error = NULL; + + output = qmi_client_loc_start_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_loc_start_output_get_result (output, &error)) { + g_printerr ("error: could not start location tracking: %s\n", error->message); + qmi_message_loc_start_output_unref (output); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully started location tracking (session id %u)\n", + qmi_device_get_path_display (ctx->device), session_id); + + qmi_message_loc_start_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_LOC_START */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return G_SOURCE_REMOVE; +} + +void +qmicli_loc_run (QmiDevice *device, + QmiClientLoc *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new0 (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_LOC_START + if (start_flag) { + QmiMessageLocStartInput *input; + + input = qmi_message_loc_start_input_new (); + qmi_message_loc_start_input_set_session_id (input, (guint8) session_id, NULL); + qmi_message_loc_start_input_set_intermediate_report_state (input, QMI_LOC_INTERMEDIATE_REPORT_STATE_ENABLE, NULL); + qmi_message_loc_start_input_set_minimum_interval_between_position_reports (input, 1000, NULL); + qmi_message_loc_start_input_set_fix_recurrence_type (input, QMI_LOC_FIX_RECURRENCE_TYPE_REQUEST_PERIODIC_FIXES, NULL); + qmi_client_loc_start (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) start_ready, + NULL); + qmi_message_loc_start_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_LOC_STOP + if (stop_flag) { + QmiMessageLocStopInput *input; + + input = qmi_message_loc_stop_input_new (); + qmi_message_loc_stop_input_set_session_id (input, (guint8) session_id, NULL); + qmi_client_loc_stop (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) stop_ready, + NULL); + qmi_message_loc_stop_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_LOC_DELETE_ASSISTANCE_DATA + if (delete_assistance_data_flag) { + QmiMessageLocDeleteAssistanceDataInput *input; + + input = qmi_message_loc_delete_assistance_data_input_new (); + qmi_message_loc_delete_assistance_data_input_set_delete_all (input, TRUE, NULL); + qmi_client_loc_delete_assistance_data (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) delete_assistance_data_ready, + NULL); + qmi_message_loc_delete_assistance_data_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_LOC_GET_NMEA_TYPES + if (get_nmea_types_flag) { + qmi_client_loc_get_nmea_types (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback) get_nmea_types_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_LOC_SET_NMEA_TYPES + if (set_nmea_types_str) { + g_autoptr(QmiMessageLocSetNmeaTypesInput) input = NULL; + + g_debug ("Asynchronously setting APN type..."); + input = set_nmea_types_input_create (set_nmea_types_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_loc_set_nmea_types (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_nmea_types_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_LOC_GET_OPERATION_MODE + if (get_operation_mode_flag) { + qmi_client_loc_get_operation_mode (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback) get_operation_mode_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_LOC_SET_OPERATION_MODE + if (set_operation_mode_str) { + g_autoptr(QmiMessageLocSetOperationModeInput) input = NULL; + + g_debug ("Asynchronously setting operation mode..."); + input = set_operation_mode_input_create (set_operation_mode_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_loc_set_operation_mode (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_operation_mode_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_LOC_GET_ENGINE_LOCK + if (get_engine_lock_flag) { + qmi_client_loc_get_engine_lock (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback) get_engine_lock_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_LOC_SET_ENGINE_LOCK + if (set_engine_lock_str) { + g_autoptr(QmiMessageLocSetEngineLockInput) input = NULL; + + g_debug ("Asynchronously setting engine lock..."); + input = set_engine_lock_input_create (set_engine_lock_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_loc_set_engine_lock (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_engine_lock_ready, + NULL); + return; + } +#endif + +#if (defined HAVE_QMI_INDICATION_LOC_POSITION_REPORT || \ + defined HAVE_QMI_INDICATION_LOC_GNSS_SV_INFO || \ + defined HAVE_QMI_INDICATION_LOC_NMEA) && \ + defined HAVE_QMI_MESSAGE_LOC_REGISTER_EVENTS + if (get_position_report_flag || get_gnss_sv_info_flag || follow_position_report_flag || follow_gnss_sv_info_flag || follow_nmea_flag) { + /* All the remaining actions require monitoring */ + ctx->monitoring_step = MONITORING_STEP_FIRST; + monitoring_step_run (); + return; + } +#endif + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_LOC */ diff --git a/pkgs/qmi-nmea/qmicli-nas.c b/pkgs/qmi-nmea/qmicli-nas.c new file mode 100644 index 0000000..7f69bfb --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-nas.c @@ -0,0 +1,4790 @@ +/* -*- 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) 2012-2017 Aleksander Morgado + * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_NAS + +#undef VALIDATE_MASK_NONE +#define VALIDATE_MASK_NONE(str) (str ? str : "none") + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientNas *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gboolean get_signal_strength_flag; +static gboolean get_signal_info_flag; +static gchar *get_tx_rx_info_str; +static gboolean get_home_network_flag; +static gboolean get_serving_system_flag; +static gboolean get_system_info_flag; +static gboolean get_technology_preference_flag; +static gboolean get_preferred_networks_flag; +static gchar *set_preferred_networks_str; +static gboolean get_system_selection_preference_flag; +static gchar *set_system_selection_preference_str; +static gchar *get_plmn_name_str; +static gboolean network_scan_flag; +static gboolean get_cell_location_info_flag; +static gboolean force_network_search_flag; +static gboolean get_operator_name_flag; +static gboolean get_lte_cphy_ca_info_flag; +static gboolean get_rf_band_info_flag; +static gboolean get_drx_flag; +static gboolean get_supported_messages_flag; +static gboolean swi_get_status_flag; +static gboolean reset_flag; +static gboolean noop_flag; + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_NAS_GET_SIGNAL_STRENGTH + { "nas-get-signal-strength", 0, 0, G_OPTION_ARG_NONE, &get_signal_strength_flag, + "Get signal strength", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_GET_SIGNAL_INFO + { "nas-get-signal-info", 0, 0, G_OPTION_ARG_NONE, &get_signal_info_flag, + "Get signal info", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_GET_TX_RX_INFO + { "nas-get-tx-rx-info", 0, 0, G_OPTION_ARG_STRING, &get_tx_rx_info_str, + "Get TX/RX info", + "[(Radio Interface)]", + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_GET_HOME_NETWORK + { "nas-get-home-network", 0, 0, G_OPTION_ARG_NONE, &get_home_network_flag, + "Get home network", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_GET_SERVING_SYSTEM + { "nas-get-serving-system", 0, 0, G_OPTION_ARG_NONE, &get_serving_system_flag, + "Get serving system", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_GET_SYSTEM_INFO + { "nas-get-system-info", 0, 0, G_OPTION_ARG_NONE, &get_system_info_flag, + "Get system info", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_GET_TECHNOLOGY_PREFERENCE + { "nas-get-technology-preference", 0, 0, G_OPTION_ARG_NONE, &get_technology_preference_flag, + "Get technology preference", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_GET_PREFERRED_NETWORKS + { "nas-get-preferred-networks", 0, 0, G_OPTION_ARG_NONE, &get_preferred_networks_flag, + "Get preferred networks", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_SET_PREFERRED_NETWORKS + { "nas-set-preferred-networks", 0, 0, G_OPTION_ARG_STRING, &set_preferred_networks_str, + "Set preferred networks list", + "[[MCCMNC,access_tech],...]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_GET_SYSTEM_SELECTION_PREFERENCE + { "nas-get-system-selection-preference", 0, 0, G_OPTION_ARG_NONE, &get_system_selection_preference_flag, + "Get system selection preference", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_SET_SYSTEM_SELECTION_PREFERENCE + { "nas-set-system-selection-preference", 0, 0, G_OPTION_ARG_STRING, &set_system_selection_preference_str, + "Set system selection preference", + "[cdma-1x|cdma-1xevdo|gsm|umts|lte|td-scdma][,[automatic|manual=MCCMNC]]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_NETWORK_SCAN + { "nas-network-scan", 0, 0, G_OPTION_ARG_NONE, &network_scan_flag, + "Scan networks", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_GET_CELL_LOCATION_INFO + { "nas-get-cell-location-info", 0, 0, G_OPTION_ARG_NONE, &get_cell_location_info_flag, + "Get Cell Location Info", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_FORCE_NETWORK_SEARCH + { "nas-force-network-search", 0, 0, G_OPTION_ARG_NONE, &force_network_search_flag, + "Force network search", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_GET_OPERATOR_NAME + { "nas-get-operator-name", 0, 0, G_OPTION_ARG_NONE, &get_operator_name_flag, + "Get operator name data", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_GET_PLMN_NAME + { "nas-get-plmn-name", 0, 0, G_OPTION_ARG_STRING, &get_plmn_name_str, + "Get plmn name data", + "[mccmnc]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_GET_LTE_CPHY_CA_INFO + { "nas-get-lte-cphy-ca-info", 0, 0, G_OPTION_ARG_NONE, &get_lte_cphy_ca_info_flag, + "Get LTE Cphy CA Info", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_GET_RF_BAND_INFORMATION + { "nas-get-rf-band-info", 0, 0, G_OPTION_ARG_NONE, &get_rf_band_info_flag, + "Get RF Band Info", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_GET_DRX + { "nas-get-drx", 0, 0, G_OPTION_ARG_NONE, &get_drx_flag, + "Get DRX", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_GET_SUPPORTED_MESSAGES + { "nas-get-supported-messages", 0, 0, G_OPTION_ARG_NONE, &get_supported_messages_flag, + "Get supported messages", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_SWI_GET_STATUS + { "nas-swi-get-status", 0, 0, G_OPTION_ARG_NONE, &swi_get_status_flag, + "Get status ((Sierra Wireless specific)", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_NAS_RESET + { "nas-reset", 0, 0, G_OPTION_ARG_NONE, &reset_flag, + "Reset the service state", + NULL + }, +#endif + { "nas-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a NAS client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_nas_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("nas", + "NAS options:", + "Show Network Access Service options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_nas_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (get_signal_strength_flag + + get_signal_info_flag + + !!get_tx_rx_info_str + + get_home_network_flag + + get_serving_system_flag + + get_system_info_flag + + get_technology_preference_flag + + get_preferred_networks_flag + + !!set_preferred_networks_str + + get_system_selection_preference_flag + + !!set_system_selection_preference_str + + !!get_plmn_name_str + + network_scan_flag + + get_cell_location_info_flag + + force_network_search_flag + + get_operator_name_flag + + get_lte_cphy_ca_info_flag + + get_rf_band_info_flag + + get_drx_flag + + get_supported_messages_flag + + swi_get_status_flag + + reset_flag + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many NAS actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->cancellable) + g_object_unref (context->cancellable); + if (context->device) + g_object_unref (context->device); + if (context->client) + g_object_unref (context->client); + g_slice_free (Context, context); +} + +static void +operation_shutdown (gboolean operation_status) +{ + /* Cleanup context and finish async operation */ + context_free (ctx); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_NAS_GET_SIGNAL_INFO || \ + defined HAVE_QMI_MESSAGE_NAS_GET_SIGNAL_STRENGTH + +static gboolean +get_db_from_sinr_level (QmiNasEvdoSinrLevel level, + gdouble *out) +{ + g_assert (out != NULL); + + switch (level) { + case QMI_NAS_EVDO_SINR_LEVEL_0: + *out = -9.0; + return TRUE; + case QMI_NAS_EVDO_SINR_LEVEL_1: + *out = -6.0; + return TRUE; + case QMI_NAS_EVDO_SINR_LEVEL_2: + *out = -4.5; + return TRUE; + case QMI_NAS_EVDO_SINR_LEVEL_3: + *out = -3.0; + return TRUE; + case QMI_NAS_EVDO_SINR_LEVEL_4: + *out = -2.0; + return TRUE; + case QMI_NAS_EVDO_SINR_LEVEL_5: + *out = 1.0; + return TRUE; + case QMI_NAS_EVDO_SINR_LEVEL_6: + *out = 3.0; + return TRUE; + case QMI_NAS_EVDO_SINR_LEVEL_7: + *out = 6.0; + return TRUE; + case QMI_NAS_EVDO_SINR_LEVEL_8: + *out = 9.0; + return TRUE; + default: + g_warning ("Invalid SINR level '%u'", level); + return FALSE; + } +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_SIGNAL_INFO + * HAVE_QMI_MESSAGE_NAS_GET_SIGNAL_STRENGTH */ + +#if defined HAVE_QMI_MESSAGE_NAS_GET_SIGNAL_INFO + +static void +get_signal_info_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasGetSignalInfoOutput *output; + GError *error = NULL; + gint8 rssi; + gint16 ecio; + QmiNasEvdoSinrLevel sinr_level; + gint32 io; + gint8 rsrq; + gint16 rsrp; + gint16 snr; + gint8 rscp; + gint16 wcdma_rscp; + gint16 rsrq_5g; + gint32 rssi_tdma; + gint32 rscp_tdma; + gint32 ecio_tdma; + gint32 sinr_tdma; + + output = qmi_client_nas_get_signal_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_nas_get_signal_info_output_get_result (output, &error)) { + g_printerr ("error: couldn't get signal info: %s\n", error->message); + g_error_free (error); + qmi_message_nas_get_signal_info_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got signal info\n", + qmi_device_get_path_display (ctx->device)); + + /* CDMA... */ + if (qmi_message_nas_get_signal_info_output_get_cdma_signal_strength (output, + &rssi, + &ecio, + NULL)) { + g_print ("CDMA:\n" + "\tRSSI: '%d dBm'\n" + "\tECIO: '%.1lf dBm'\n", + rssi, + (-0.5)*((gdouble)ecio)); + } + + /* HDR... */ + if (qmi_message_nas_get_signal_info_output_get_hdr_signal_strength (output, + &rssi, + &ecio, + &sinr_level, + &io, + NULL)) { + gdouble db_sinr = 0.0; + + g_print ("HDR:\n" + "\tRSSI: '%d dBm'\n" + "\tECIO: '%.1lf dBm'\n" + "\tIO: '%d dBm'\n", + rssi, + (-0.5)*((gdouble)ecio), + io); + + if (get_db_from_sinr_level (sinr_level, &db_sinr)) + g_print ("\tSINR (%u): '%.1lf dB'\n", sinr_level, db_sinr); + else + g_print ("\tSINR (%u): N/A'\n", sinr_level); + } + + /* GSM */ + if (qmi_message_nas_get_signal_info_output_get_gsm_signal_strength (output, + &rssi, + NULL)) { + g_print ("GSM:\n" + "\tRSSI: '%d dBm'\n", + rssi); + } + + /* WCDMA... */ + if (qmi_message_nas_get_signal_info_output_get_wcdma_signal_strength (output, + &rssi, + &ecio, + NULL)) { + g_print ("WCDMA:\n" + "\tRSSI: '%d dBm'\n" + "\tECIO: '%.1lf dBm'\n", + rssi, + (-0.5)*((gdouble)ecio)); + } + + /* WCDMA Signal Code Power... */ + if (qmi_message_nas_get_signal_info_output_get_wcdma_rscp (output, + &wcdma_rscp, + NULL)) { + g_print ("\tRSCP: '%d dBm'\n", + (-1)*wcdma_rscp); + } + + /* LTE... */ + if (qmi_message_nas_get_signal_info_output_get_lte_signal_strength (output, + &rssi, + &rsrq, + &rsrp, + &snr, + NULL)) { + g_print ("LTE:\n" + "\tRSSI: '%d dBm'\n" + "\tRSRQ: '%d dB'\n" + "\tRSRP: '%d dBm'\n" + "\tSNR: '%.1lf dB'\n", + rssi, + rsrq, + rsrp, + (0.1) * ((gdouble)snr)); + } + + /* TDMA */ + if (qmi_message_nas_get_signal_info_output_get_tdma_signal_strength (output, + &rscp, + NULL)) { + g_print ("TDMA:\n" + "\tRSCP: '%d dBm'\n", + rscp); + } + + /* TDMA extended */ + if (qmi_message_nas_get_signal_info_output_get_tdma_signal_strength_extended (output, + &rssi_tdma, + &rscp_tdma, + &ecio_tdma, + &sinr_tdma, + NULL)) { + g_print ("\tRSSI: '%d dB'\n" + "\tRSCP: '%d dBm'\n" + "\tECIO: '%d dBm'\n" + "\tSINR: '%d dB'\n", + rssi_tdma, + rscp_tdma, + ecio_tdma, + sinr_tdma); + } + + /* 5G, values of -32768 in EN-DC mode indicate the modem is not connected... */ + if (qmi_message_nas_get_signal_info_output_get_5g_signal_strength (output, + &rsrp, + &snr, + NULL)) { + g_print ("5G:\n"); + if (rsrp == (gint16)(0x8000)) + g_print ("\tRSRP: 'n/a'\n"); + else + g_print ("\tRSRP: '%d dBm'\n", rsrp); + if (snr == (gint16)(0x8000)) + g_print ("\tSNR: 'n/a'\n"); + else + g_print ("\tSNR: '%.1lf dB'\n", (0.1) * ((gdouble)snr)); + } + + /* 5G extended... */ + if (qmi_message_nas_get_signal_info_output_get_5g_signal_strength_extended (output, + &rsrq_5g, + NULL)) { + if (rsrq_5g == (gint16)(0x8000)) + g_print ("\tRSRQ: 'n/a'\n"); + else + g_print ("\tRSRQ: '%d dB'\n", + rsrq_5g); + } + + qmi_message_nas_get_signal_info_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_SIGNAL_INFO */ + +#if defined HAVE_QMI_MESSAGE_NAS_GET_SIGNAL_STRENGTH + +static QmiMessageNasGetSignalStrengthInput * +get_signal_strength_input_create (void) +{ + GError *error = NULL; + QmiMessageNasGetSignalStrengthInput *input; + QmiNasSignalStrengthRequest mask; + + mask = (QMI_NAS_SIGNAL_STRENGTH_REQUEST_RSSI | + QMI_NAS_SIGNAL_STRENGTH_REQUEST_ECIO | + QMI_NAS_SIGNAL_STRENGTH_REQUEST_IO | + QMI_NAS_SIGNAL_STRENGTH_REQUEST_SINR | + QMI_NAS_SIGNAL_STRENGTH_REQUEST_RSRQ | + QMI_NAS_SIGNAL_STRENGTH_REQUEST_LTE_SNR | + QMI_NAS_SIGNAL_STRENGTH_REQUEST_LTE_RSRP); + + input = qmi_message_nas_get_signal_strength_input_new (); + if (!qmi_message_nas_get_signal_strength_input_set_request_mask ( + input, + mask, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_nas_get_signal_strength_input_unref (input); + input = NULL; + } + + return input; +} + +static void +get_signal_strength_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasGetSignalStrengthOutput *output; + GError *error = NULL; + GArray *array; + QmiNasRadioInterface radio_interface; + gint8 strength; + gint32 io; + QmiNasEvdoSinrLevel sinr_level; + gint8 rsrq; + gint16 rsrp; + gint16 snr; + + output = qmi_client_nas_get_signal_strength_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_nas_get_signal_strength_output_get_result (output, &error)) { + g_printerr ("error: couldn't get signal strength: %s\n", error->message); + g_error_free (error); + qmi_message_nas_get_signal_strength_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_nas_get_signal_strength_output_get_signal_strength (output, + &strength, + &radio_interface, + NULL); + + g_print ("[%s] Successfully got signal strength\n" + "Current:\n" + "\tNetwork '%s': '%d dBm'\n", + qmi_device_get_path_display (ctx->device), + qmi_nas_radio_interface_get_string (radio_interface), + strength); + + /* Other signal strengths in other networks... */ + if (qmi_message_nas_get_signal_strength_output_get_strength_list (output, &array, NULL)) { + guint i; + + g_print ("Other:\n"); + for (i = 0; i < array->len; i++) { + QmiMessageNasGetSignalStrengthOutputStrengthListElement *element; + + element = &g_array_index (array, QmiMessageNasGetSignalStrengthOutputStrengthListElement, i); + g_print ("\tNetwork '%s': '%d dBm'\n", + qmi_nas_radio_interface_get_string (element->radio_interface), + element->strength); + } + } + + /* RSSI... */ + if (qmi_message_nas_get_signal_strength_output_get_rssi_list (output, &array, NULL)) { + guint i; + + g_print ("RSSI:\n"); + for (i = 0; i < array->len; i++) { + QmiMessageNasGetSignalStrengthOutputRssiListElement *element; + + element = &g_array_index (array, QmiMessageNasGetSignalStrengthOutputRssiListElement, i); + g_print ("\tNetwork '%s': '%d dBm'\n", + qmi_nas_radio_interface_get_string (element->radio_interface), + (-1) * element->rssi); + } + } + + /* ECIO... */ + if (qmi_message_nas_get_signal_strength_output_get_ecio_list (output, &array, NULL)) { + guint i; + + g_print ("ECIO:\n"); + for (i = 0; i < array->len; i++) { + QmiMessageNasGetSignalStrengthOutputEcioListElement *element; + + element = &g_array_index (array, QmiMessageNasGetSignalStrengthOutputEcioListElement, i); + g_print ("\tNetwork '%s': '%.1lf dBm'\n", + qmi_nas_radio_interface_get_string (element->radio_interface), + (-0.5) * ((gdouble)element->ecio)); + } + } + + /* IO... */ + if (qmi_message_nas_get_signal_strength_output_get_io (output, &io, NULL)) { + g_print ("IO: '%d dBm'\n", io); + } + + /* SINR level */ + if (qmi_message_nas_get_signal_strength_output_get_sinr (output, &sinr_level, NULL)) { + gdouble db_sinr = 0.0; + + if (get_db_from_sinr_level (sinr_level, &db_sinr)) + g_print ("SINR (%u): '%.1lf dB'\n", sinr_level, db_sinr); + else + g_print ("SINR (%u): N/A'\n", sinr_level); + } + + /* RSRQ */ + if (qmi_message_nas_get_signal_strength_output_get_rsrq (output, &rsrq, &radio_interface, NULL)) { + g_print ("RSRQ:\n" + "\tNetwork '%s': '%d dB'\n", + qmi_nas_radio_interface_get_string (radio_interface), + rsrq); + } + + /* LTE SNR */ + if (qmi_message_nas_get_signal_strength_output_get_lte_snr (output, &snr, NULL)) { + g_print ("SNR:\n" + "\tNetwork '%s': '%.1lf dB'\n", + qmi_nas_radio_interface_get_string (QMI_NAS_RADIO_INTERFACE_LTE), + (0.1) * ((gdouble)snr)); + } + + /* LTE RSRP */ + if (qmi_message_nas_get_signal_strength_output_get_lte_rsrp (output, &rsrp, NULL)) { + g_print ("RSRP:\n" + "\tNetwork '%s': '%d dBm'\n", + qmi_nas_radio_interface_get_string (QMI_NAS_RADIO_INTERFACE_LTE), + rsrp); + } + + /* Just skip others for now */ + + qmi_message_nas_get_signal_strength_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_SIGNAL_STRENGTH */ + +#if defined HAVE_QMI_MESSAGE_NAS_GET_TX_RX_INFO + +static void +get_tx_rx_info_ready (QmiClientNas *client, + GAsyncResult *res, + gpointer user_data) +{ + QmiNasRadioInterface interface; + QmiMessageNasGetTxRxInfoOutput *output; + GError *error = NULL; + gboolean is_radio_tuned; + gboolean is_in_traffic; + gint32 power; + gint32 ecio; + gint32 rscp; + gint32 rsrp; + guint32 phase; + + interface = GPOINTER_TO_UINT (user_data); + + output = qmi_client_nas_get_tx_rx_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_nas_get_tx_rx_info_output_get_result (output, &error)) { + g_printerr ("error: couldn't get TX/RX info: %s\n", error->message); + g_error_free (error); + qmi_message_nas_get_tx_rx_info_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got TX/RX info\n", + qmi_device_get_path_display (ctx->device)); + + /* RX Channel 0 */ + if (qmi_message_nas_get_tx_rx_info_output_get_rx_chain_0_info ( + output, + &is_radio_tuned, + &power, + &ecio, + &rscp, + &rsrp, + &phase, + NULL)) { + g_print ("RX Chain 0:\n" + "\tRadio tuned: '%s'\n" + "\tPower: '%.1lf dBm'\n", + is_radio_tuned ? "yes" : "no", + (0.1) * ((gdouble)power)); + if (interface == QMI_NAS_RADIO_INTERFACE_CDMA_1X || + interface == QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO || + interface == QMI_NAS_RADIO_INTERFACE_GSM || + interface == QMI_NAS_RADIO_INTERFACE_UMTS || + interface == QMI_NAS_RADIO_INTERFACE_LTE || + interface == QMI_NAS_RADIO_INTERFACE_5GNR) + g_print ("\tECIO: '%.1lf dB'\n", (0.1) * ((gdouble)ecio)); + + if (interface == QMI_NAS_RADIO_INTERFACE_UMTS) + g_print ("\tRSCP: '%.1lf dBm'\n", (0.1) * ((gdouble)rscp)); + + if (interface == QMI_NAS_RADIO_INTERFACE_LTE || + interface == QMI_NAS_RADIO_INTERFACE_5GNR) + g_print ("\tRSRP: '%.1lf dBm'\n", (0.1) * ((gdouble)rsrp)); + + if (interface == QMI_NAS_RADIO_INTERFACE_LTE || + interface == QMI_NAS_RADIO_INTERFACE_5GNR) { + if (phase == 0xFFFFFFFF) + g_print ("\tPhase: 'unknown'\n"); + else + g_print ("\tPhase: '%.2lf degrees'\n", (0.01) * ((gdouble)phase)); + } + } + + /* RX Channel 1 */ + if (qmi_message_nas_get_tx_rx_info_output_get_rx_chain_1_info ( + output, + &is_radio_tuned, + &power, + &ecio, + &rscp, + &rsrp, + &phase, + NULL)) { + g_print ("RX Chain 1:\n" + "\tRadio tuned: '%s'\n" + "\tPower: '%.1lf dBm'\n", + is_radio_tuned ? "yes" : "no", + (0.1) * ((gdouble)power)); + if (interface == QMI_NAS_RADIO_INTERFACE_CDMA_1X || + interface == QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO || + interface == QMI_NAS_RADIO_INTERFACE_GSM || + interface == QMI_NAS_RADIO_INTERFACE_UMTS || + interface == QMI_NAS_RADIO_INTERFACE_LTE || + interface == QMI_NAS_RADIO_INTERFACE_5GNR) + g_print ("\tECIO: '%.1lf dB'\n", (0.1) * ((gdouble)ecio)); + + if (interface == QMI_NAS_RADIO_INTERFACE_UMTS) + g_print ("\tRSCP: '%.1lf dBm'\n", (0.1) * ((gdouble)rscp)); + + if (interface == QMI_NAS_RADIO_INTERFACE_LTE || + interface == QMI_NAS_RADIO_INTERFACE_5GNR) + g_print ("\tRSRP: '%.1lf dBm'\n", (0.1) * ((gdouble)rsrp)); + + if (interface == QMI_NAS_RADIO_INTERFACE_LTE || + interface == QMI_NAS_RADIO_INTERFACE_5GNR) { + if (phase == 0xFFFFFFFF) + g_print ("\tPhase: 'unknown'\n"); + else + g_print ("\tPhase: '%.2lf degrees'\n", (0.01) * ((gdouble)phase)); + } + } + + /* RX Channel 2 */ + if (qmi_message_nas_get_tx_rx_info_output_get_rx_chain_2_info ( + output, + &is_radio_tuned, + &power, + &ecio, + &rscp, + &rsrp, + &phase, + NULL)) { + g_print ("RX Chain 2:\n" + "\tRadio tuned: '%s'\n" + "\tPower: '%.1lf dBm'\n", + is_radio_tuned ? "yes" : "no", + (0.1) * ((gdouble)power)); + if (interface == QMI_NAS_RADIO_INTERFACE_CDMA_1X || + interface == QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO || + interface == QMI_NAS_RADIO_INTERFACE_GSM || + interface == QMI_NAS_RADIO_INTERFACE_UMTS || + interface == QMI_NAS_RADIO_INTERFACE_LTE || + interface == QMI_NAS_RADIO_INTERFACE_5GNR) + g_print ("\tECIO: '%.1lf dB'\n", (0.1) * ((gdouble)ecio)); + + if (interface == QMI_NAS_RADIO_INTERFACE_UMTS) + g_print ("\tRSCP: '%.1lf dBm'\n", (0.1) * ((gdouble)rscp)); + + if (interface == QMI_NAS_RADIO_INTERFACE_LTE || + interface == QMI_NAS_RADIO_INTERFACE_5GNR) + g_print ("\tRSRP: '%.1lf dBm'\n", (0.1) * ((gdouble)rsrp)); + + if (interface == QMI_NAS_RADIO_INTERFACE_LTE || + interface == QMI_NAS_RADIO_INTERFACE_5GNR) { + if (phase == 0xFFFFFFFF) + g_print ("\tPhase: 'unknown'\n"); + else + g_print ("\tPhase: '%.2lf degrees'\n", (0.01) * ((gdouble)phase)); + } + } + + /* RX Channel 3 */ + if (qmi_message_nas_get_tx_rx_info_output_get_rx_chain_3_info ( + output, + &is_radio_tuned, + &power, + &ecio, + &rscp, + &rsrp, + &phase, + NULL)) { + g_print ("RX Chain 3:\n" + "\tRadio tuned: '%s'\n" + "\tPower: '%.1lf dBm'\n", + is_radio_tuned ? "yes" : "no", + (0.1) * ((gdouble)power)); + if (interface == QMI_NAS_RADIO_INTERFACE_CDMA_1X || + interface == QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO || + interface == QMI_NAS_RADIO_INTERFACE_GSM || + interface == QMI_NAS_RADIO_INTERFACE_UMTS || + interface == QMI_NAS_RADIO_INTERFACE_LTE || + interface == QMI_NAS_RADIO_INTERFACE_5GNR) + g_print ("\tECIO: '%.1lf dB'\n", (0.1) * ((gdouble)ecio)); + + if (interface == QMI_NAS_RADIO_INTERFACE_UMTS) + g_print ("\tRSCP: '%.1lf dBm'\n", (0.1) * ((gdouble)rscp)); + + if (interface == QMI_NAS_RADIO_INTERFACE_LTE || + interface == QMI_NAS_RADIO_INTERFACE_5GNR) + g_print ("\tRSRP: '%.1lf dBm'\n", (0.1) * ((gdouble)rsrp)); + + if (interface == QMI_NAS_RADIO_INTERFACE_LTE || + interface == QMI_NAS_RADIO_INTERFACE_5GNR) { + if (phase == 0xFFFFFFFF) + g_print ("\tPhase: 'unknown'\n"); + else + g_print ("\tPhase: '%.2lf degrees'\n", (0.01) * ((gdouble)phase)); + } + } + + /* TX Channel */ + if (qmi_message_nas_get_tx_rx_info_output_get_tx_info ( + output, + &is_in_traffic, + &power, + NULL)) { + g_print ("TX:\n"); + if (is_in_traffic) + g_print ("\tIn traffic: 'yes'\n" + "\tPower: '%.1lf dBm'\n", + (0.1) * ((gdouble)power)); + else + g_print ("\tIn traffic: 'no'\n"); + } + + qmi_message_nas_get_tx_rx_info_output_unref (output); + operation_shutdown (TRUE); +} + +static QmiMessageNasGetTxRxInfoInput * +get_tx_rx_info_input_create (const gchar *str, + QmiNasRadioInterface *interface) +{ + QmiMessageNasGetTxRxInfoInput *input = NULL; + + g_assert (interface != NULL); + + if (qmicli_read_nas_radio_interface_from_string (str, interface)) { + GError *error = NULL; + + input = qmi_message_nas_get_tx_rx_info_input_new (); + if (!qmi_message_nas_get_tx_rx_info_input_set_radio_interface ( + input, + *interface, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_nas_get_tx_rx_info_input_unref (input); + input = NULL; + } + } + + return input; +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_TX_RX_INFO */ + +#if defined HAVE_QMI_MESSAGE_NAS_GET_HOME_NETWORK + +static void +get_home_network_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasGetHomeNetworkOutput *output; + GError *error = NULL; + + output = qmi_client_nas_get_home_network_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_nas_get_home_network_output_get_result (output, &error)) { + g_printerr ("error: couldn't get home network: %s\n", error->message); + g_error_free (error); + qmi_message_nas_get_home_network_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got home network:\n", + qmi_device_get_path_display (ctx->device)); + + { + guint16 mcc; + guint16 mnc; + const gchar *description; + + qmi_message_nas_get_home_network_output_get_home_network ( + output, + &mcc, + &mnc, + &description, + NULL); + + g_print ("\tHome network:\n" + "\t\tMCC: '%" G_GUINT16_FORMAT"'\n" + "\t\tMNC: '%" G_GUINT16_FORMAT"'\n" + "\t\tDescription: '%s'\n", + mcc, + mnc, + description); + } + + { + QmiNasNetworkNameSource network_name_source; + if (qmi_message_nas_get_home_network_output_get_network_name_source ( + output, + &network_name_source, + NULL)) { + g_print ("\tNetwork name source: %s\n", + qmi_nas_network_name_source_get_string (network_name_source)); + } + } + + { + guint16 sid; + guint16 nid; + + if (qmi_message_nas_get_home_network_output_get_home_system_id ( + output, + &sid, + &nid, + NULL)) { + g_print ("\t\tSID: '%" G_GUINT16_FORMAT"'\n" + "\t\tNID: '%" G_GUINT16_FORMAT"'\n", + sid, + nid); + } + } + + { + guint16 mcc; + guint16 mnc; + QmiNasNetworkDescriptionEncoding description_encoding; + GArray *description_array; + + if (qmi_message_nas_get_home_network_output_get_home_network_3gpp2_ext ( + output, + &mcc, + &mnc, + NULL, /* display_description */ + &description_encoding, + &description_array, + NULL)) { + g_autofree gchar *description = NULL; + + description = qmi_nas_read_string_from_network_description_encoded_array (description_encoding, description_array); + g_print ("\t3GPP2 Home network (extended):\n" + "\t\tMCC: '%" G_GUINT16_FORMAT"'\n" + "\t\tMNC: '%" G_GUINT16_FORMAT"'\n" + "\t\tDescription: '%s'\n", + mcc, + mnc, + description ?: ""); + } + } + + qmi_message_nas_get_home_network_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_HOME_NETWORK */ + +#if defined HAVE_QMI_MESSAGE_NAS_GET_PREFERRED_NETWORKS + +static void +get_preferred_networks_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasGetPreferredNetworksOutput *output; + GError *error = NULL; + GArray *preferred_networks_array; + GArray *pcs_digit_array; + + output = qmi_client_nas_get_preferred_networks_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_nas_get_preferred_networks_output_get_result (output, &error)) { + g_printerr ("error: couldn't get preferred networks: %s\n", error->message); + g_error_free (error); + qmi_message_nas_get_preferred_networks_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got preferred networks:\n", + qmi_device_get_path_display (ctx->device)); + + if (qmi_message_nas_get_preferred_networks_output_get_preferred_networks (output, &preferred_networks_array, NULL)) { + guint i; + + g_print ("Preferred PLMN list:\n"); + if (preferred_networks_array->len == 0) + g_print ("\t\n"); + for (i = 0; i < preferred_networks_array->len; i++) { + QmiMessageNasGetPreferredNetworksOutputPreferredNetworksElement *element; + g_autofree gchar *access_tech_string = NULL; + + element = &g_array_index (preferred_networks_array, QmiMessageNasGetPreferredNetworksOutputPreferredNetworksElement, i); + access_tech_string = qmi_nas_plmn_access_technology_identifier_build_string_from_mask (element->radio_access_technology); + g_print ("[%u]:\n" + "\tMCC: '%" G_GUINT16_FORMAT "'\n" + "\tMNC: '%" G_GUINT16_FORMAT "'\n" + "\tAccess Technology: '%s'\n", + i, + element->mcc, + element->mnc, + VALIDATE_MASK_NONE (access_tech_string)); + } + } + + if (qmi_message_nas_get_preferred_networks_output_get_mnc_pcs_digit_include_status (output, &pcs_digit_array, NULL)) { + guint i; + + g_print ("PCS digit status:\n"); + if (pcs_digit_array->len == 0) + g_print ("\t\n"); + for (i = 0; i < pcs_digit_array->len; i++) { + QmiMessageNasGetPreferredNetworksOutputMncPcsDigitIncludeStatusElement *element; + + element = &g_array_index (pcs_digit_array, QmiMessageNasGetPreferredNetworksOutputMncPcsDigitIncludeStatusElement, i); + g_print ("[%u]:\n" + "\tMCC: '%" G_GUINT16_FORMAT "'\n" + "\tMNC: '%" G_GUINT16_FORMAT "'\n" + "\tMCC with PCS digit: '%s'\n", + i, + element->mcc, + element->mnc, + element->includes_pcs_digit ? "yes" : "no"); + } + } + + qmi_message_nas_get_preferred_networks_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_PREFERRED_NETWORKS */ + +#if defined HAVE_QMI_MESSAGE_NAS_SET_PREFERRED_NETWORKS + +static QmiMessageNasSetPreferredNetworksInput * +set_preferred_networks_input_create (const gchar *str) +{ + QmiMessageNasSetPreferredNetworksInput *input = NULL; + GError *error = NULL; + gchar **parts = NULL; + gint i; + gint num_parts; + const gchar *part; + guint16 mcc = 0; + guint16 mnc = 0; + gboolean pcs_digit = FALSE; + QmiNasPlmnAccessTechnologyIdentifier access_tech = QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_UNSPECIFIED; + GArray *preferred_nets_array; + GArray *pcs_digit_array; + QmiMessageNasSetPreferredNetworksInputPreferredNetworksElement preferred_nets_element; + QmiMessageNasSetPreferredNetworksInputMncPcsDigitIncludeStatusElement pcs_digit_element; + + preferred_nets_array = g_array_new (FALSE, FALSE, sizeof (QmiMessageNasSetPreferredNetworksInputPreferredNetworksElement)); + pcs_digit_array = g_array_new (FALSE, FALSE, sizeof (QmiMessageNasSetPreferredNetworksInputMncPcsDigitIncludeStatusElement)); + + parts = g_strsplit (str, ",", -1); + num_parts = g_strv_length (parts); + for (i = 0; i < num_parts; i += 2) { + part = parts[i]; + /* Parse MCCMNC, if it's found, also read the access technology in numeric format */ + if (qmicli_read_parse_3gpp_mcc_mnc (part, &mcc, &mnc, &pcs_digit)) { + access_tech = QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_UNSPECIFIED; + if (i + 1 < num_parts) { + const gchar *access_tech_str = parts[i + 1]; + + if (!qmicli_read_nas_plmn_access_technology_identifier_from_string (access_tech_str, &access_tech)) + goto out; + + memset (&preferred_nets_element, 0, sizeof (preferred_nets_element)); + preferred_nets_element.mcc = mcc; + preferred_nets_element.mnc = mnc; + preferred_nets_element.radio_access_technology = access_tech; + g_array_append_val (preferred_nets_array, preferred_nets_element); + memset (&pcs_digit_element, 0, sizeof (pcs_digit_element)); + pcs_digit_element.mcc = mcc; + pcs_digit_element.mnc = mnc; + pcs_digit_element.includes_pcs_digit = pcs_digit; + g_array_append_val (pcs_digit_array, pcs_digit_element); + } else { + g_printerr ("error: access technology missing for MCCMNC: '%s'\n", part); + goto out; + } + } else + goto out; + } + + input = qmi_message_nas_set_preferred_networks_input_new (); + + if (!qmi_message_nas_set_preferred_networks_input_set_preferred_networks (input, preferred_nets_array, &error)) + goto out; + + if (!qmi_message_nas_set_preferred_networks_input_set_mnc_pcs_digit_include_status (input, pcs_digit_array, &error)) + goto out; + + /* Always set the clear previous flag, leaving any previously configured networks is not desired */ + if (!qmi_message_nas_set_preferred_networks_input_set_clear_previous_preferred_networks (input, TRUE, &error)) + goto out; + +out: + g_strfreev (parts); + + if (preferred_nets_array) + g_array_unref (preferred_nets_array); + if (pcs_digit_array) + g_array_unref (pcs_digit_array); + + if (error) { + g_printerr ("error: couldn't create preferred networks input data bundle: '%s'\n", error->message); + g_error_free (error); + qmi_message_nas_set_preferred_networks_input_unref (input); + return NULL; + } + + return input; +} + +static void +set_preferred_networks_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasSetPreferredNetworksOutput *output = NULL; + GError *error = NULL; + + output = qmi_client_nas_set_preferred_networks_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_nas_set_preferred_networks_output_get_result (output, &error)) { + g_printerr ("error: couldn't set preferred networks: %s\n", error->message); + g_error_free (error); + qmi_message_nas_set_preferred_networks_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Preferred networks set successfully.\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_nas_set_preferred_networks_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_SET_PREFERRED_NETWORKS */ + +#if defined HAVE_QMI_MESSAGE_NAS_GET_SERVING_SYSTEM + +static void +get_serving_system_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasGetServingSystemOutput *output; + GError *error = NULL; + + output = qmi_client_nas_get_serving_system_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_nas_get_serving_system_output_get_result (output, &error)) { + g_printerr ("error: couldn't get serving system: %s\n", error->message); + g_error_free (error); + qmi_message_nas_get_serving_system_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got serving system:\n", + qmi_device_get_path_display (ctx->device)); + + { + QmiNasRegistrationState registration_state; + QmiNasAttachState cs_attach_state; + QmiNasAttachState ps_attach_state; + QmiNasNetworkType selected_network; + GArray *radio_interfaces; + guint i; + + qmi_message_nas_get_serving_system_output_get_serving_system ( + output, + ®istration_state, + &cs_attach_state, + &ps_attach_state, + &selected_network, + &radio_interfaces, + NULL); + + g_print ("\tRegistration state: '%s'\n" + "\tCS: '%s'\n" + "\tPS: '%s'\n" + "\tSelected network: '%s'\n" + "\tRadio interfaces: '%u'\n", + qmi_nas_registration_state_get_string (registration_state), + qmi_nas_attach_state_get_string (cs_attach_state), + qmi_nas_attach_state_get_string (ps_attach_state), + qmi_nas_network_type_get_string (selected_network), + radio_interfaces->len); + + for (i = 0; i < radio_interfaces->len; i++) { + QmiNasRadioInterface iface; + + iface = g_array_index (radio_interfaces, QmiNasRadioInterface, i); + g_print ("\t\t[%u]: '%s'\n", i, qmi_nas_radio_interface_get_string (iface)); + } + } + + { + QmiNasRoamingIndicatorStatus roaming; + + if (qmi_message_nas_get_serving_system_output_get_roaming_indicator ( + output, + &roaming, + NULL)) { + g_print ("\tRoaming status: '%s'\n", + qmi_nas_roaming_indicator_status_get_string (roaming)); + } + } + + { + GArray *data_service_capability; + + if (qmi_message_nas_get_serving_system_output_get_data_service_capability ( + output, + &data_service_capability, + NULL)) { + guint i; + + g_print ("\tData service capabilities: '%u'\n", + data_service_capability->len); + + for (i = 0; i < data_service_capability->len; i++) { + QmiNasDataCapability cap; + + cap = g_array_index (data_service_capability, QmiNasDataCapability, i); + g_print ("\t\t[%u]: '%s'\n", i, qmi_nas_data_capability_get_string (cap)); + } + } + } + + { + guint16 current_plmn_mcc; + guint16 current_plmn_mnc; + const gchar *current_plmn_description; + + if (qmi_message_nas_get_serving_system_output_get_current_plmn ( + output, + ¤t_plmn_mcc, + ¤t_plmn_mnc, + ¤t_plmn_description, + NULL)) { + g_print ("\tCurrent PLMN:\n" + "\t\tMCC: '%" G_GUINT16_FORMAT"'\n" + "\t\tMNC: '%" G_GUINT16_FORMAT"'\n" + "\t\tDescription: '%s'\n", + current_plmn_mcc, + current_plmn_mnc, + current_plmn_description); + } + } + + { + QmiNasNetworkNameSource network_name_source; + if (qmi_message_nas_get_serving_system_output_get_network_name_source ( + output, + &network_name_source, + NULL)) { + g_print ("\tNetwork name source: %s\n", + qmi_nas_network_name_source_get_string (network_name_source)); + } + } + + { + guint16 sid; + guint16 nid; + + if (qmi_message_nas_get_serving_system_output_get_cdma_system_id ( + output, + &sid, + &nid, + NULL)) { + g_print ("\tCDMA System ID:\n" + "\t\tSID: '%" G_GUINT16_FORMAT"'\n" + "\t\tNID: '%" G_GUINT16_FORMAT"'\n", + sid, nid); + } + } + + { + guint16 id; + gint32 latitude; + gint32 longitude; + + if (qmi_message_nas_get_serving_system_output_get_cdma_base_station_info ( + output, + &id, + &latitude, + &longitude, + NULL)) { + gdouble latitude_degrees; + gdouble longitude_degrees; + + /* TODO: give degrees, minutes, seconds */ + latitude_degrees = ((gdouble)latitude * 0.25)/3600.0; + longitude_degrees = ((gdouble)longitude * 0.25)/3600.0; + + g_print ("\tCDMA Base station info:\n" + "\t\tBase station ID: '%" G_GUINT16_FORMAT"'\n" + "\t\tLatitude: '%lf'º\n" + "\t\tLongitude: '%lf'º\n", + id, latitude_degrees, longitude_degrees); + } + } + + { + GArray *roaming_indicators; + + if (qmi_message_nas_get_serving_system_output_get_roaming_indicator_list ( + output, + &roaming_indicators, + NULL)) { + guint i; + + g_print ("\tRoaming indicators: '%u'\n", + roaming_indicators->len); + + for (i = 0; i < roaming_indicators->len; i++) { + QmiMessageNasGetServingSystemOutputRoamingIndicatorListElement *element; + + element = &g_array_index (roaming_indicators, QmiMessageNasGetServingSystemOutputRoamingIndicatorListElement, i); + g_print ("\t\t[%u]: '%s' (%s)\n", + i, + qmi_nas_roaming_indicator_status_get_string (element->roaming_indicator), + qmi_nas_radio_interface_get_string (element->radio_interface)); + } + } + } + + { + QmiNasRoamingIndicatorStatus roaming; + + if (qmi_message_nas_get_serving_system_output_get_default_roaming_indicator ( + output, + &roaming, + NULL)) { + g_print ("\tDefault roaming status: '%s'\n", + qmi_nas_roaming_indicator_status_get_string (roaming)); + } + } + + { + guint8 leap_seconds; + gint8 local_time_offset; + gboolean daylight_saving_time; + + if (qmi_message_nas_get_serving_system_output_get_time_zone_3gpp2 ( + output, + &leap_seconds, + &local_time_offset, + &daylight_saving_time, + NULL)) { + g_print ("\t3GPP2 time zone:\n" + "\t\tLeap seconds: '%u' seconds\n" + "\t\tLocal time offset: '%d' minutes\n" + "\t\tDaylight saving time: '%s'\n", + leap_seconds, + (gint)local_time_offset * 30, + daylight_saving_time ? "yes" : "no"); + } + } + + { + guint8 cdma_p_rev; + + if (qmi_message_nas_get_serving_system_output_get_cdma_p_rev ( + output, + &cdma_p_rev, + NULL)) { + g_print ("\tCDMA P_Rev: '%u'\n", cdma_p_rev); + } + } + + { + gint8 time_zone; + + if (qmi_message_nas_get_serving_system_output_get_time_zone_3gpp ( + output, + &time_zone, + NULL)) { + g_print ("\t3GPP time zone offset: '%d' minutes\n", + (gint)time_zone * 15); + } + } + + { + guint8 adjustment; + + if (qmi_message_nas_get_serving_system_output_get_daylight_saving_time_adjustment_3gpp ( + output, + &adjustment, + NULL)) { + g_print ("\t3GPP daylight saving time adjustment: '%u' hours\n", + adjustment); + } + } + + { + guint16 lac; + + if (qmi_message_nas_get_serving_system_output_get_lac_3gpp ( + output, + &lac, + NULL)) { + g_print ("\t3GPP location area code: '%" G_GUINT16_FORMAT"'\n", lac); + } + } + + { + guint32 cid; + + if (qmi_message_nas_get_serving_system_output_get_cid_3gpp ( + output, + &cid, + NULL)) { + g_print ("\t3GPP cell ID: '%u'\n", cid); + } + } + + { + gboolean concurrent; + + if (qmi_message_nas_get_serving_system_output_get_concurrent_service_info_3gpp2 ( + output, + &concurrent, + NULL)) { + g_print ("\t3GPP2 concurrent service info: '%s'\n", + concurrent ? "available" : "not available"); + } + } + + { + gboolean prl; + + if (qmi_message_nas_get_serving_system_output_get_prl_indicator_3gpp2 ( + output, + &prl, + NULL)) { + g_print ("\t3GPP2 PRL indicator: '%s'\n", + prl ? "system in PRL" : "system not in PRL"); + } + } + + { + gboolean supported; + + if (qmi_message_nas_get_serving_system_output_get_dtm_support ( + output, + &supported, + NULL)) { + g_print ("\tDual transfer mode: '%s'\n", + supported ? "supported" : "not supported"); + } + } + + { + QmiNasServiceStatus status; + QmiNasNetworkServiceDomain capability; + QmiNasServiceStatus hdr_status; + gboolean hdr_hybrid; + gboolean forbidden; + + if (qmi_message_nas_get_serving_system_output_get_detailed_service_status ( + output, + &status, + &capability, + &hdr_status, + &hdr_hybrid, + &forbidden, + NULL)) { + g_print ("\tDetailed status:\n" + "\t\tStatus: '%s'\n" + "\t\tCapability: '%s'\n" + "\t\tHDR Status: '%s'\n" + "\t\tHDR Hybrid: '%s'\n" + "\t\tForbidden: '%s'\n", + qmi_nas_service_status_get_string (status), + qmi_nas_network_service_domain_get_string (capability), + qmi_nas_service_status_get_string (hdr_status), + hdr_hybrid ? "yes" : "no", + forbidden ? "yes" : "no"); + } + } + + { + guint16 mcc; + guint8 imsi_11_12; + + if (qmi_message_nas_get_serving_system_output_get_cdma_system_info ( + output, + &mcc, + &imsi_11_12, + NULL)) { + g_print ("\tCDMA system info:\n" + "\t\tMCC: '%" G_GUINT16_FORMAT"'\n" + "\t\tIMSI_11_12: '%u'\n", + mcc, + imsi_11_12); + } + } + + { + QmiNasHdrPersonality personality; + + if (qmi_message_nas_get_serving_system_output_get_hdr_personality ( + output, + &personality, + NULL)) { + g_print ("\tHDR personality: '%s'\n", + qmi_nas_hdr_personality_get_string (personality)); + } + } + + { + guint16 tac; + + if (qmi_message_nas_get_serving_system_output_get_lte_tac ( + output, + &tac, + NULL)) { + g_print ("\tLTE tracking area code: '%" G_GUINT16_FORMAT"'\n", tac); + } + } + + { + QmiNasCallBarringStatus cs_status; + QmiNasCallBarringStatus ps_status; + + if (qmi_message_nas_get_serving_system_output_get_call_barring_status ( + output, + &cs_status, + &ps_status, + NULL)) { + g_print ("\tCall barring status:\n" + "\t\tCircuit switched: '%s'\n" + "\t\tPacket switched: '%s'\n", + qmi_nas_call_barring_status_get_string (cs_status), + qmi_nas_call_barring_status_get_string (ps_status)); + } + } + + { + guint16 code; + + if (qmi_message_nas_get_serving_system_output_get_umts_primary_scrambling_code ( + output, + &code, + NULL)) { + g_print ("\tUMTS primary scrambling code: '%" G_GUINT16_FORMAT"'\n", code); + } + } + + { + guint16 mcc; + guint16 mnc; + gboolean has_pcs_digit; + + if (qmi_message_nas_get_serving_system_output_get_mnc_pcs_digit_include_status ( + output, + &mcc, + &mnc, + &has_pcs_digit, + NULL)) { + g_print ("\tFull operator code info:\n" + "\t\tMCC: '%" G_GUINT16_FORMAT"'\n" + "\t\tMNC: '%" G_GUINT16_FORMAT"'\n" + "\t\tMNC with PCS digit: '%s'\n", + mcc, + mnc, + has_pcs_digit ? "yes" : "no"); + } + } + + qmi_message_nas_get_serving_system_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_SERVING_SYSTEM */ + +#if defined HAVE_QMI_MESSAGE_NAS_GET_SYSTEM_INFO + +static void +get_system_info_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasGetSystemInfoOutput *output; + GError *error = NULL; + + output = qmi_client_nas_get_system_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_nas_get_system_info_output_get_result (output, &error)) { + g_printerr ("error: couldn't get system info: %s\n", error->message); + g_error_free (error); + qmi_message_nas_get_system_info_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got system info:\n", + qmi_device_get_path_display (ctx->device)); + + /* CDMA 1x */ + { + QmiNasServiceStatus service_status; + gboolean preferred_data_path; + gboolean domain_valid; + QmiNasNetworkServiceDomain domain; + gboolean service_capability_valid; + QmiNasNetworkServiceDomain service_capability; + gboolean roaming_status_valid; + QmiNasRoamingStatus roaming_status; + gboolean forbidden_valid; + gboolean forbidden; + gboolean prl_match_valid; + gboolean prl_match; + gboolean p_rev_valid; + guint8 p_rev; + gboolean base_station_p_rev_valid; + guint8 base_station_p_rev; + gboolean concurrent_service_support_valid; + gboolean concurrent_service_support; + gboolean cdma_system_id_valid; + guint16 sid; + guint16 nid; + gboolean base_station_info_valid; + guint16 base_station_id; + gint32 base_station_latitude; + gint32 base_station_longitude; + gboolean packet_zone_valid; + guint16 packet_zone; + gboolean network_id_valid; + const gchar *mcc; + const gchar *mnc; + guint16 geo_system_index; + guint16 registration_period; + + if (qmi_message_nas_get_system_info_output_get_cdma_service_status ( + output, + &service_status, + &preferred_data_path, + NULL)) { + g_print ("\tCDMA 1x service:\n" + "\t\tStatus: '%s'\n" + "\t\tPreferred data path: '%s'\n", + qmi_nas_service_status_get_string (service_status), + preferred_data_path ? "yes" : "no"); + + if (qmi_message_nas_get_system_info_output_get_cdma_system_info ( + output, + &domain_valid, &domain, + &service_capability_valid, &service_capability, + &roaming_status_valid, &roaming_status, + &forbidden_valid, &forbidden, + &prl_match_valid, &prl_match, + &p_rev_valid, &p_rev, + &base_station_p_rev_valid, &base_station_p_rev, + &concurrent_service_support_valid, &concurrent_service_support, + &cdma_system_id_valid, &sid, &nid, + &base_station_info_valid, &base_station_id, &base_station_longitude, &base_station_latitude, + &packet_zone_valid, &packet_zone, + &network_id_valid, &mcc, &mnc, + NULL)) { + if (domain_valid) + g_print ("\t\tDomain: '%s'\n", qmi_nas_network_service_domain_get_string (domain)); + if (service_capability_valid) + g_print ("\t\tService capability: '%s'\n", qmi_nas_network_service_domain_get_string (service_capability)); + if (roaming_status_valid) + g_print ("\t\tRoaming status: '%s'\n", qmi_nas_roaming_status_get_string (roaming_status)); + if (forbidden_valid) + g_print ("\t\tForbidden: '%s'\n", forbidden ? "yes" : "no"); + if (prl_match_valid) + g_print ("\t\tPRL match: '%s'\n", prl_match ? "yes" : "no"); + if (p_rev_valid) + g_print ("\t\tP-Rev: '%u'\n", p_rev); + if (base_station_p_rev_valid) + g_print ("\t\tBase station P-Rev: '%u'\n", base_station_p_rev); + if (concurrent_service_support_valid) + g_print ("\t\tConcurrent service support: '%s'\n", concurrent_service_support ? "yes" : "no"); + if (cdma_system_id_valid) { + g_print ("\t\tSID: '%" G_GUINT16_FORMAT"'\n", sid); + g_print ("\t\tNID: '%" G_GUINT16_FORMAT"'\n", nid); + } + if (base_station_info_valid) { + gdouble latitude_degrees; + gdouble longitude_degrees; + + /* TODO: give degrees, minutes, seconds */ + latitude_degrees = ((gdouble)base_station_latitude * 0.25)/3600.0; + longitude_degrees = ((gdouble)base_station_longitude * 0.25)/3600.0; + g_print ("\t\tBase station ID: '%" G_GUINT16_FORMAT"'\n", base_station_id); + g_print ("\t\tBase station latitude: '%lf'º\n", latitude_degrees); + g_print ("\t\tBase station longitude: '%lf'º\n", longitude_degrees); + } + if (packet_zone_valid) + g_print ("\t\tPacket zone: '%" G_GUINT16_FORMAT "'\n", packet_zone); + if (network_id_valid) { + g_print ("\t\tMCC: '%s'\n", mcc); + if ((guchar)mnc[2] == 0xFF) + g_print ("\t\tMNC: '%.2s'\n", mnc); + else + g_print ("\t\tMNC: '%.3s'\n", mnc); + } + } + + if (qmi_message_nas_get_system_info_output_get_additional_cdma_system_info ( + output, + &geo_system_index, + ®istration_period, + NULL)) { + if (geo_system_index != 0xFFFF) + g_print ("\t\tGeo system index: '%" G_GUINT16_FORMAT "'\n", geo_system_index); + if (registration_period != 0xFFFF) + g_print ("\t\tRegistration period: '%" G_GUINT16_FORMAT "'\n", registration_period); + } + } + } + + /* CDMA 1xEV-DO */ + { + QmiNasServiceStatus service_status; + gboolean preferred_data_path; + gboolean domain_valid; + QmiNasNetworkServiceDomain domain; + gboolean service_capability_valid; + QmiNasNetworkServiceDomain service_capability; + gboolean roaming_status_valid; + QmiNasRoamingStatus roaming_status; + gboolean forbidden_valid; + gboolean forbidden; + gboolean prl_match_valid; + gboolean prl_match; + gboolean personality_valid; + QmiNasHdrPersonality personality; + gboolean protocol_revision_valid; + QmiNasHdrProtocolRevision protocol_revision; + gboolean is_856_system_id_valid; + const gchar *is_856_system_id; + guint16 geo_system_index; + + if (qmi_message_nas_get_system_info_output_get_hdr_service_status ( + output, + &service_status, + &preferred_data_path, + NULL)) { + g_print ("\tCDMA 1xEV-DO (HDR) service:\n" + "\t\tStatus: '%s'\n" + "\t\tPreferred data path: '%s'\n", + qmi_nas_service_status_get_string (service_status), + preferred_data_path ? "yes" : "no"); + + if (qmi_message_nas_get_system_info_output_get_hdr_system_info ( + output, + &domain_valid, &domain, + &service_capability_valid, &service_capability, + &roaming_status_valid, &roaming_status, + &forbidden_valid, &forbidden, + &prl_match_valid, &prl_match, + &personality_valid, &personality, + &protocol_revision_valid, &protocol_revision, + &is_856_system_id_valid, &is_856_system_id, + NULL)) { + if (domain_valid) + g_print ("\t\tDomain: '%s'\n", qmi_nas_network_service_domain_get_string (domain)); + if (service_capability_valid) + g_print ("\t\tService capability: '%s'\n", qmi_nas_network_service_domain_get_string (service_capability)); + if (roaming_status_valid) + g_print ("\t\tRoaming status: '%s'\n", qmi_nas_roaming_status_get_string (roaming_status)); + if (forbidden_valid) + g_print ("\t\tForbidden: '%s'\n", forbidden ? "yes" : "no"); + if (prl_match_valid) + g_print ("\t\tPRL match: '%s'\n", prl_match ? "yes" : "no"); + if (personality_valid) + g_print ("\t\tPersonality: '%s'\n", qmi_nas_hdr_personality_get_string (personality)); + if (protocol_revision_valid) + g_print ("\t\tProtocol revision: '%s'\n", qmi_nas_hdr_protocol_revision_get_string (protocol_revision)); + if (is_856_system_id_valid) + g_print ("\t\tIS-856 system ID: '%s'\n", is_856_system_id); + } + + if (qmi_message_nas_get_system_info_output_get_additional_hdr_system_info ( + output, + &geo_system_index, + NULL)) { + if (geo_system_index != 0xFFFF) + g_print ("\t\tGeo system index: '%" G_GUINT16_FORMAT "'\n", geo_system_index); + } + } + } + + /* GSM */ + { + QmiNasServiceStatus service_status; + QmiNasServiceStatus true_service_status; + gboolean preferred_data_path; + gboolean domain_valid; + QmiNasNetworkServiceDomain domain; + gboolean service_capability_valid; + QmiNasNetworkServiceDomain service_capability; + gboolean roaming_status_valid; + QmiNasRoamingStatus roaming_status; + gboolean forbidden_valid; + gboolean forbidden; + gboolean lac_valid; + guint16 lac; + gboolean cid_valid; + guint32 cid; + gboolean registration_reject_info_valid; + QmiNasNetworkServiceDomain registration_reject_domain; + QmiNasRejectCause registration_reject_cause; + gboolean network_id_valid; + const gchar *mcc; + const gchar *mnc; + gboolean egprs_support_valid; + gboolean egprs_support; + gboolean dtm_support_valid; + gboolean dtm_support; + guint16 geo_system_index; + QmiNasCellBroadcastCapability cell_broadcast_support; + QmiNasCallBarringStatus call_barring_status_cs; + QmiNasCallBarringStatus call_barring_status_ps; + QmiNasNetworkServiceDomain cipher_domain; + + if (qmi_message_nas_get_system_info_output_get_gsm_service_status ( + output, + &service_status, + &true_service_status, + &preferred_data_path, + NULL)) { + g_print ("\tGSM service:\n" + "\t\tStatus: '%s'\n" + "\t\tTrue Status: '%s'\n" + "\t\tPreferred data path: '%s'\n", + qmi_nas_service_status_get_string (service_status), + qmi_nas_service_status_get_string (true_service_status), + preferred_data_path ? "yes" : "no"); + + if (qmi_message_nas_get_system_info_output_get_gsm_system_info_v2 ( + output, + &domain_valid, &domain, + &service_capability_valid, &service_capability, + &roaming_status_valid, &roaming_status, + &forbidden_valid, &forbidden, + &lac_valid, &lac, + &cid_valid, &cid, + ®istration_reject_info_valid, ®istration_reject_domain, ®istration_reject_cause, + &network_id_valid, &mcc, &mnc, + &egprs_support_valid, &egprs_support, + &dtm_support_valid, &dtm_support, + NULL)) { + if (domain_valid) + g_print ("\t\tDomain: '%s'\n", qmi_nas_network_service_domain_get_string (domain)); + if (service_capability_valid) + g_print ("\t\tService capability: '%s'\n", qmi_nas_network_service_domain_get_string (service_capability)); + if (roaming_status_valid) + g_print ("\t\tRoaming status: '%s'\n", qmi_nas_roaming_status_get_string (roaming_status)); + if (forbidden_valid) + g_print ("\t\tForbidden: '%s'\n", forbidden ? "yes" : "no"); + if (lac_valid) + g_print ("\t\tLocation Area Code: '%" G_GUINT16_FORMAT"'\n", lac); + if (cid_valid) + g_print ("\t\tCell ID: '%u'\n", cid); + if (registration_reject_info_valid) + g_print ("\t\tRegistration reject: '%s' (%s)\n", + qmi_nas_network_service_domain_get_string (registration_reject_domain), + qmi_nas_reject_cause_get_string (registration_reject_cause)); + if (network_id_valid) { + g_print ("\t\tMCC: '%s'\n", mcc); + if ((guchar)mnc[2] == 0xFF) + g_print ("\t\tMNC: '%.2s'\n", mnc); + else + g_print ("\t\tMNC: '%.3s'\n", mnc); + } + if (egprs_support_valid) + g_print ("\t\tE-GPRS supported: '%s'\n", egprs_support ? "yes" : "no"); + if (dtm_support_valid) + g_print ("\t\tDual Transfer Mode supported: '%s'\n", dtm_support ? "yes" : "no"); + } + + if (qmi_message_nas_get_system_info_output_get_additional_gsm_system_info ( + output, + &geo_system_index, + &cell_broadcast_support, + NULL)) { + if (geo_system_index != 0xFFFF) + g_print ("\t\tGeo system index: '%" G_GUINT16_FORMAT "'\n", geo_system_index); + g_print ("\t\tCell broadcast support: '%s'\n", qmi_nas_cell_broadcast_capability_get_string (cell_broadcast_support)); + } + + if (qmi_message_nas_get_system_info_output_get_gsm_call_barring_status ( + output, + &call_barring_status_cs, + &call_barring_status_ps, + NULL)) { + g_print ("\t\tCall barring status (CS): '%s'\n", qmi_nas_call_barring_status_get_string (call_barring_status_cs)); + g_print ("\t\tCall barring status (PS): '%s'\n", qmi_nas_call_barring_status_get_string (call_barring_status_ps)); + } + + if (qmi_message_nas_get_system_info_output_get_gsm_cipher_domain ( + output, + &cipher_domain, + NULL)) { + g_print ("\t\tCipher Domain: '%s'\n", qmi_nas_network_service_domain_get_string (cipher_domain)); + } + } + } + + /* WCDMA */ + { + QmiNasServiceStatus service_status; + QmiNasServiceStatus true_service_status; + gboolean preferred_data_path; + gboolean domain_valid; + QmiNasNetworkServiceDomain domain; + gboolean service_capability_valid; + QmiNasNetworkServiceDomain service_capability; + gboolean roaming_status_valid; + QmiNasRoamingStatus roaming_status; + gboolean forbidden_valid; + gboolean forbidden; + gboolean lac_valid; + guint16 lac; + gboolean cid_valid; + guint32 cid; + gboolean registration_reject_info_valid; + QmiNasNetworkServiceDomain registration_reject_domain; + QmiNasRejectCause registration_reject_cause; + gboolean network_id_valid; + const gchar *mcc; + const gchar *mnc; + gboolean hs_call_status_valid; + QmiNasWcdmaHsService hs_call_status; + gboolean hs_service_valid; + QmiNasWcdmaHsService hs_service; + gboolean primary_scrambling_code_valid; + guint16 primary_scrambling_code; + guint16 geo_system_index; + QmiNasCellBroadcastCapability cell_broadcast_support; + QmiNasCallBarringStatus call_barring_status_cs; + QmiNasCallBarringStatus call_barring_status_ps; + QmiNasNetworkServiceDomain cipher_domain; + + if (qmi_message_nas_get_system_info_output_get_wcdma_service_status ( + output, + &service_status, + &true_service_status, + &preferred_data_path, + NULL)) { + g_print ("\tWCDMA service:\n" + "\t\tStatus: '%s'\n" + "\t\tTrue Status: '%s'\n" + "\t\tPreferred data path: '%s'\n", + qmi_nas_service_status_get_string (service_status), + qmi_nas_service_status_get_string (true_service_status), + preferred_data_path ? "yes" : "no"); + + if (qmi_message_nas_get_system_info_output_get_wcdma_system_info_v2 ( + output, + &domain_valid, &domain, + &service_capability_valid, &service_capability, + &roaming_status_valid, &roaming_status, + &forbidden_valid, &forbidden, + &lac_valid, &lac, + &cid_valid, &cid, + ®istration_reject_info_valid, ®istration_reject_domain, ®istration_reject_cause, + &network_id_valid, &mcc, &mnc, + &hs_call_status_valid, &hs_call_status, + &hs_service_valid, &hs_service, + &primary_scrambling_code_valid, &primary_scrambling_code, + NULL)) { + if (domain_valid) + g_print ("\t\tDomain: '%s'\n", qmi_nas_network_service_domain_get_string (domain)); + if (service_capability_valid) + g_print ("\t\tService capability: '%s'\n", qmi_nas_network_service_domain_get_string (service_capability)); + if (roaming_status_valid) + g_print ("\t\tRoaming status: '%s'\n", qmi_nas_roaming_status_get_string (roaming_status)); + if (forbidden_valid) + g_print ("\t\tForbidden: '%s'\n", forbidden ? "yes" : "no"); + if (lac_valid) + g_print ("\t\tLocation Area Code: '%" G_GUINT16_FORMAT"'\n", lac); + if (cid_valid) + g_print ("\t\tCell ID: '%u'\n", cid); + if (registration_reject_info_valid) + g_print ("\t\tRegistration reject: '%s' (%s)\n", + qmi_nas_network_service_domain_get_string (registration_reject_domain), + qmi_nas_reject_cause_get_string (registration_reject_cause)); + if (network_id_valid) { + g_print ("\t\tMCC: '%s'\n", mcc); + if ((guchar)mnc[2] == 0xFF) + g_print ("\t\tMNC: '%.2s'\n", mnc); + else + g_print ("\t\tMNC: '%.3s'\n", mnc); + } + if (hs_call_status_valid) + g_print ("\t\tHS call status: '%s'\n", qmi_nas_wcdma_hs_service_get_string (hs_call_status)); + if (hs_service_valid) + g_print ("\t\tHS service: '%s'\n", qmi_nas_wcdma_hs_service_get_string (hs_service)); + if (primary_scrambling_code_valid) + g_print ("\t\tPrimary scrambling code: '%" G_GUINT16_FORMAT"'\n", primary_scrambling_code); + } + + if (qmi_message_nas_get_system_info_output_get_additional_wcdma_system_info ( + output, + &geo_system_index, + &cell_broadcast_support, + NULL)) { + if (geo_system_index != 0xFFFF) + g_print ("\t\tGeo system index: '%" G_GUINT16_FORMAT "'\n", geo_system_index); + g_print ("\t\tCell broadcast support: '%s'\n", qmi_nas_cell_broadcast_capability_get_string (cell_broadcast_support)); + } + + if (qmi_message_nas_get_system_info_output_get_wcdma_call_barring_status ( + output, + &call_barring_status_cs, + &call_barring_status_ps, + NULL)) { + g_print ("\t\tCall barring status (CS): '%s'\n", qmi_nas_call_barring_status_get_string (call_barring_status_cs)); + g_print ("\t\tCall barring status (PS): '%s'\n", qmi_nas_call_barring_status_get_string (call_barring_status_ps)); + } + + if (qmi_message_nas_get_system_info_output_get_wcdma_cipher_domain ( + output, + &cipher_domain, + NULL)) { + g_print ("\t\tCipher Domain: '%s'\n", qmi_nas_network_service_domain_get_string (cipher_domain)); + } + } + } + + /* LTE */ + { + QmiNasServiceStatus service_status; + QmiNasServiceStatus true_service_status; + gboolean preferred_data_path; + gboolean domain_valid; + QmiNasNetworkServiceDomain domain; + gboolean service_capability_valid; + QmiNasNetworkServiceDomain service_capability; + gboolean roaming_status_valid; + QmiNasRoamingStatus roaming_status; + gboolean forbidden_valid; + gboolean forbidden; + gboolean lac_valid; + guint16 lac; + gboolean cid_valid; + guint32 cid; + gboolean registration_reject_info_valid; + QmiNasNetworkServiceDomain registration_reject_domain; + QmiNasRejectCause registration_reject_cause; + gboolean network_id_valid; + const gchar *mcc; + const gchar *mnc; + gboolean tac_valid; + guint16 tac; + guint16 geo_system_index; + gboolean voice_support; + gboolean ims_voice_support; + gboolean embms_coverage_info_support; + guint16 embms_coverage_info_trace_id; + QmiNasNetworkSelectionRegistrationRestriction registration_restriction; + QmiNasLteCellAccessStatus cell_access_status; + QmiNasLteRegistrationDomain registration_domain; + gboolean endc_available; + gboolean restrict_dcnr; + + if (qmi_message_nas_get_system_info_output_get_lte_service_status ( + output, + &service_status, + &true_service_status, + &preferred_data_path, + NULL)) { + g_print ("\tLTE service:\n" + "\t\tStatus: '%s'\n" + "\t\tTrue Status: '%s'\n" + "\t\tPreferred data path: '%s'\n", + qmi_nas_service_status_get_string (service_status), + qmi_nas_service_status_get_string (true_service_status), + preferred_data_path ? "yes" : "no"); + + if (qmi_message_nas_get_system_info_output_get_lte_system_info_v2 ( + output, + &domain_valid, &domain, + &service_capability_valid, &service_capability, + &roaming_status_valid, &roaming_status, + &forbidden_valid, &forbidden, + &lac_valid, &lac, + &cid_valid, &cid, + ®istration_reject_info_valid,®istration_reject_domain,®istration_reject_cause, + &network_id_valid, &mcc, &mnc, + &tac_valid, &tac, + NULL)) { + if (domain_valid) + g_print ("\t\tDomain: '%s'\n", qmi_nas_network_service_domain_get_string (domain)); + if (service_capability_valid) + g_print ("\t\tService capability: '%s'\n", qmi_nas_network_service_domain_get_string (service_capability)); + if (roaming_status_valid) + g_print ("\t\tRoaming status: '%s'\n", qmi_nas_roaming_status_get_string (roaming_status)); + if (forbidden_valid) + g_print ("\t\tForbidden: '%s'\n", forbidden ? "yes" : "no"); + if (lac_valid) + g_print ("\t\tLocation Area Code: '%" G_GUINT16_FORMAT"'\n", lac); + if (cid_valid) + g_print ("\t\tCell ID: '%u'\n", cid); + if (registration_reject_info_valid) + g_print ("\t\tRegistration reject: '%s' (%s)\n", + qmi_nas_network_service_domain_get_string (registration_reject_domain), + qmi_nas_reject_cause_get_string (registration_reject_cause)); + if (network_id_valid) { + g_print ("\t\tMCC: '%s'\n", mcc); + if ((guchar)mnc[2] == 0xFF) + g_print ("\t\tMNC: '%.2s'\n", mnc); + else + g_print ("\t\tMNC: '%.3s'\n", mnc); + } + if (tac_valid) + g_print ("\t\tTracking Area Code: '%" G_GUINT16_FORMAT"'\n", tac); + } + + if (qmi_message_nas_get_system_info_output_get_additional_lte_system_info ( + output, + &geo_system_index, + NULL)) { + if (geo_system_index != 0xFFFF) + g_print ("\t\tGeo system index: '%" G_GUINT16_FORMAT "'\n", geo_system_index); + } + + if (qmi_message_nas_get_system_info_output_get_lte_voice_support ( + output, + &voice_support, + NULL)) { + g_print ("\t\tVoice support: '%s'\n", voice_support ? "yes" : "no"); + } + + if (qmi_message_nas_get_system_info_output_get_ims_voice_support ( + output, + &ims_voice_support, + NULL)) { + g_print ("\t\tIMS voice support: '%s'\n", ims_voice_support ? "yes" : "no"); + } + + if (qmi_message_nas_get_system_info_output_get_lte_embms_coverage_info_support ( + output, + &embms_coverage_info_support, + NULL)) { + g_print ("\t\teMBMS coverage info support: '%s'\n", embms_coverage_info_support ? "yes" : "no"); + } + + if (qmi_message_nas_get_system_info_output_get_lte_embms_coverage_info_trace_id ( + output, + &embms_coverage_info_trace_id, + NULL)) { + g_print ("\t\teMBMS coverage info trace ID: '%" G_GUINT16_FORMAT "'\n", embms_coverage_info_trace_id); + } + + if (qmi_message_nas_get_system_info_output_get_lte_cell_access_status ( + output, + &cell_access_status, + NULL)) { + g_print ("\t\tCell access: '%s'\n", qmi_nas_lte_cell_access_status_get_string (cell_access_status)); + } + + if (qmi_message_nas_get_system_info_output_get_network_selection_registration_restriction ( + output, + ®istration_restriction, + NULL)) { + g_print ("\t\tRegistration restriction: '%s'\n", qmi_nas_network_selection_registration_restriction_get_string (registration_restriction)); + } + + if (qmi_message_nas_get_system_info_output_get_lte_registration_domain ( + output, + ®istration_domain, + NULL)) { + g_print ("\t\tRegistration domain: '%s'\n", qmi_nas_lte_registration_domain_get_string (registration_domain)); + } + + if (qmi_message_nas_get_system_info_output_get_eutra_with_nr5g_availability ( + output, + &endc_available, + NULL)) { + g_print ("\t\t5G NSA Available: '%s'\n", endc_available ? "yes" : "no"); + } + + if (qmi_message_nas_get_system_info_output_get_dcnr_restriction_info ( + output, + &restrict_dcnr, + NULL)) { + g_print ("\t\tDCNR Restriction: '%s'\n", restrict_dcnr ? "yes" : "no"); + } + } + } + + /* TD-SCDMA */ + { + QmiNasServiceStatus service_status; + QmiNasServiceStatus true_service_status; + gboolean preferred_data_path; + gboolean domain_valid; + QmiNasNetworkServiceDomain domain; + gboolean service_capability_valid; + QmiNasNetworkServiceDomain service_capability; + gboolean roaming_status_valid; + QmiNasRoamingStatus roaming_status; + gboolean forbidden_valid; + gboolean forbidden; + gboolean lac_valid; + guint16 lac; + gboolean cid_valid; + guint32 cid; + gboolean registration_reject_info_valid; + QmiNasNetworkServiceDomain registration_reject_domain; + QmiNasRejectCause registration_reject_cause; + gboolean network_id_valid; + const gchar *mcc; + const gchar *mnc; + gboolean hs_call_status_valid; + QmiNasWcdmaHsService hs_call_status; + gboolean hs_service_valid; + QmiNasWcdmaHsService hs_service; + gboolean cell_parameter_id_valid; + guint16 cell_parameter_id; + gboolean cell_broadcast_support_valid; + QmiNasCellBroadcastCapability cell_broadcast_support; + gboolean call_barring_status_cs_valid; + QmiNasCallBarringStatus call_barring_status_cs; + gboolean call_barring_status_ps_valid; + QmiNasCallBarringStatus call_barring_status_ps; + gboolean cipher_domain_valid; + QmiNasNetworkServiceDomain cipher_domain; + + if (qmi_message_nas_get_system_info_output_get_td_scdma_service_status ( + output, + &service_status, + &true_service_status, + &preferred_data_path, + NULL)) { + g_print ("\tTD-SCDMA service:\n" + "\t\tStatus: '%s'\n" + "\t\tTrue Status: '%s'\n" + "\t\tPreferred data path: '%s'\n", + qmi_nas_service_status_get_string (service_status), + qmi_nas_service_status_get_string (true_service_status), + preferred_data_path ? "yes" : "no"); + + if (qmi_message_nas_get_system_info_output_get_td_scdma_system_info_v2 ( + output, + &domain_valid, &domain, + &service_capability_valid, &service_capability, + &roaming_status_valid, &roaming_status, + &forbidden_valid, &forbidden, + &lac_valid, &lac, + &cid_valid, &cid, + ®istration_reject_info_valid, ®istration_reject_domain, ®istration_reject_cause, + &network_id_valid, &mcc, &mnc, + &hs_call_status_valid, &hs_call_status, + &hs_service_valid, &hs_service, + &cell_parameter_id_valid, &cell_parameter_id, + &cell_broadcast_support_valid, &cell_broadcast_support, + &call_barring_status_cs_valid, &call_barring_status_cs, + &call_barring_status_ps_valid, &call_barring_status_ps, + &cipher_domain_valid, &cipher_domain, + NULL)) { + if (domain_valid) + g_print ("\t\tDomain: '%s'\n", qmi_nas_network_service_domain_get_string (domain)); + if (service_capability_valid) + g_print ("\t\tService capability: '%s'\n", qmi_nas_network_service_domain_get_string (service_capability)); + if (roaming_status_valid) + g_print ("\t\tRoaming status: '%s'\n", qmi_nas_roaming_status_get_string (roaming_status)); + if (forbidden_valid) + g_print ("\t\tForbidden: '%s'\n", forbidden ? "yes" : "no"); + if (lac_valid) + g_print ("\t\tLocation Area Code: '%" G_GUINT16_FORMAT"'\n", lac); + if (cid_valid) + g_print ("\t\tCell ID: '%u'\n", cid); + if (registration_reject_info_valid) + g_print ("\t\tRegistration reject: '%s' (%s)\n", + qmi_nas_network_service_domain_get_string (registration_reject_domain), + qmi_nas_reject_cause_get_string (registration_reject_cause)); + if (network_id_valid) { + g_print ("\t\tMCC: '%s'\n", mcc); + if ((guchar)mnc[2] == 0xFF) + g_print ("\t\tMNC: '%.2s'\n", mnc); + else + g_print ("\t\tMNC: '%.3s'\n", mnc); + } + if (hs_call_status_valid) + g_print ("\t\tHS call status: '%s'\n", qmi_nas_wcdma_hs_service_get_string (hs_call_status)); + if (hs_service_valid) + g_print ("\t\tHS service: '%s'\n", qmi_nas_wcdma_hs_service_get_string (hs_service)); + if (cell_parameter_id_valid) + g_print ("\t\tCell parameter ID: '%" G_GUINT16_FORMAT"'\n", cell_parameter_id); + if (cell_broadcast_support_valid) + g_print ("\t\tCell broadcast support: '%s'\n", qmi_nas_cell_broadcast_capability_get_string (cell_broadcast_support)); + if (call_barring_status_cs_valid) + g_print ("\t\tCall barring status (CS): '%s'\n", qmi_nas_call_barring_status_get_string (call_barring_status_cs)); + if (call_barring_status_ps_valid) + g_print ("\t\tCall barring status (PS): '%s'\n", qmi_nas_call_barring_status_get_string (call_barring_status_ps)); + if (cipher_domain_valid) + g_print ("\t\tCipher Domain: '%s'\n", qmi_nas_network_service_domain_get_string (cipher_domain)); + } + } + } + + /* 5G SA */ + { + QmiNasServiceStatus service_status; + QmiNasServiceStatus true_service_status; + gboolean preferred_data_path; + gboolean domain_valid; + QmiNasNetworkServiceDomain domain; + gboolean service_capability_valid; + QmiNasNetworkServiceDomain service_capability; + gboolean roaming_status_valid; + QmiNasRoamingStatus roaming_status; + gboolean forbidden_valid; + gboolean forbidden; + gboolean lac_valid; + guint16 lac; + gboolean cid_valid; + guint32 cid; + gboolean registration_reject_info_valid; + QmiNasNetworkServiceDomain registration_reject_domain; + guint8 registration_reject_cause; + gboolean network_id_valid; + const gchar *mcc; + const gchar *mnc; + gboolean tac_valid; + guint16 tac; + + if (qmi_message_nas_get_system_info_output_get_nr5g_service_status_info ( + output, + &service_status, + &true_service_status, + &preferred_data_path, + NULL)) { + g_print ("\t5G SA service:\n" + "\t\tStatus: '%s'\n" + "\t\tTrue Status: '%s'\n" + "\t\tPreferred data path: '%s'\n", + qmi_nas_service_status_get_string (service_status), + qmi_nas_service_status_get_string (true_service_status), + preferred_data_path ? "yes" : "no"); + + if (qmi_message_nas_get_system_info_output_get_nr5g_system_info ( + output, + &domain_valid, &domain, + &service_capability_valid, &service_capability, + &roaming_status_valid, &roaming_status, + &forbidden_valid, &forbidden, + &lac_valid, &lac, + &cid_valid, &cid, + ®istration_reject_info_valid,®istration_reject_domain,®istration_reject_cause, + &network_id_valid, &mcc, &mnc, + &tac_valid, &tac, + NULL)) { + if (domain_valid) + g_print ("\t\tDomain: '%s'\n", qmi_nas_network_service_domain_get_string (domain)); + if (service_capability_valid) + g_print ("\t\tService capability: '%s'\n", qmi_nas_network_service_domain_get_string (service_capability)); + if (roaming_status_valid) + g_print ("\t\tRoaming status: '%s'\n", qmi_nas_roaming_status_get_string (roaming_status)); + if (forbidden_valid) + g_print ("\t\tForbidden: '%s'\n", forbidden ? "yes" : "no"); + if (lac_valid) + g_print ("\t\tLocation Area Code: '%" G_GUINT16_FORMAT"'\n", lac); + if (cid_valid) + g_print ("\t\tCell ID: '%u'\n", cid); + if (registration_reject_info_valid) + g_print ("\t\tRegistration reject: '%s' (%u)\n", + qmi_nas_network_service_domain_get_string (registration_reject_domain), + registration_reject_cause); + if (network_id_valid) { + g_print ("\t\tMCC: '%s'\n", mcc); + if ((guchar)mnc[2] == 0xFF) + g_print ("\t\tMNC: '%.2s'\n", mnc); + else + g_print ("\t\tMNC: '%.3s'\n", mnc); + } + if (tac_valid) + g_print ("\t\tTracking Area Code: '%" G_GUINT16_FORMAT"'\n", tac); + } + } + } + + { + GArray *nr5g_tracking_area_code; + + if (qmi_message_nas_get_system_info_output_get_nr5g_tracking_area_code ( + output, + &nr5g_tracking_area_code, + NULL)) { + guint32 tac; + + g_assert (nr5g_tracking_area_code->len == 3); + tac = ((((g_array_index (nr5g_tracking_area_code, guint8, 0) << 8) | + g_array_index (nr5g_tracking_area_code, guint8, 1)) << 8) | + g_array_index (nr5g_tracking_area_code, guint8, 2)); + + g_print ("\t5GNR Tracking Area Code: '%" G_GUINT32_FORMAT "'\n", + tac); + } + } + + /* Common */ + { + QmiNasSimRejectState sim_reject_info; + + if (qmi_message_nas_get_system_info_output_get_sim_reject_info ( + output, + &sim_reject_info, + NULL)) { + g_print ("\tSIM reject info: '%s'\n", qmi_nas_sim_reject_state_get_string (sim_reject_info)); + } + } + + qmi_message_nas_get_system_info_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_SYSTEM_INFO */ + +#if defined HAVE_QMI_MESSAGE_NAS_GET_TECHNOLOGY_PREFERENCE + +static void +get_technology_preference_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasGetTechnologyPreferenceOutput *output; + GError *error = NULL; + QmiNasRadioTechnologyPreference preference; + QmiNasPreferenceDuration duration; + g_autofree gchar *preference_string = NULL; + g_autofree gchar *persistent_preference_string = NULL; + + output = qmi_client_nas_get_technology_preference_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_nas_get_technology_preference_output_get_result (output, &error)) { + g_printerr ("error: couldn't get technology preference: %s\n", error->message); + g_error_free (error); + qmi_message_nas_get_technology_preference_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_nas_get_technology_preference_output_get_active ( + output, + &preference, + &duration, + NULL); + + preference_string = qmi_nas_radio_technology_preference_build_string_from_mask (preference); + g_print ("[%s] Successfully got technology preference\n" + "\tActive: '%s', duration: '%s'\n", + qmi_device_get_path_display (ctx->device), + VALIDATE_MASK_NONE (preference_string), + qmi_nas_preference_duration_get_string (duration)); + + if (qmi_message_nas_get_technology_preference_output_get_persistent ( + output, + &preference, + NULL)) { + persistent_preference_string = qmi_nas_radio_technology_preference_build_string_from_mask (preference); + g_print ("\tPersistent: '%s'\n", VALIDATE_MASK_NONE (persistent_preference_string)); + } + + qmi_message_nas_get_technology_preference_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_TECHNOLOGY_PREFERENCE */ + +#if defined HAVE_QMI_MESSAGE_NAS_GET_SYSTEM_SELECTION_PREFERENCE + +static void +get_system_selection_preference_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasGetSystemSelectionPreferenceOutput *output; + GError *error = NULL; + gboolean emergency_mode; + QmiNasRatModePreference mode_preference; + QmiNasRatModePreference disabled_modes; + QmiNasBandPreference band_preference; + QmiNasLteBandPreference lte_band_preference; + QmiNasTdScdmaBandPreference td_scdma_band_preference; + QmiNasCdmaPrlPreference cdma_prl_preference; + QmiNasRoamingPreference roaming_preference; + QmiNasNetworkSelectionPreference network_selection_preference; + QmiNasServiceDomainPreference service_domain_preference; + QmiNasUsagePreference usage_preference; + QmiNasGsmWcdmaAcquisitionOrderPreference gsm_wcdma_acquisition_order_preference; + QmiNasNetworkSelectionRegistrationRestriction registration_restriction; + QmiNasVoiceDomainPreference voice_domain_preference; + guint16 mcc; + guint16 mnc; + guint64 extended_lte_band_preference[4]; + guint64 nr5g_sa_band_preference[8]; + guint64 nr5g_nsa_band_preference[8]; + gboolean has_pcs_digit; + GArray *acquisition_order_preference; + + output = qmi_client_nas_get_system_selection_preference_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_nas_get_system_selection_preference_output_get_result (output, &error)) { + g_printerr ("error: couldn't get system_selection preference: %s\n", error->message); + g_error_free (error); + qmi_message_nas_get_system_selection_preference_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got system selection preference\n", + qmi_device_get_path_display (ctx->device)); + + if (qmi_message_nas_get_system_selection_preference_output_get_emergency_mode ( + output, + &emergency_mode, + NULL)) { + g_print ("\tEmergency mode: '%s'\n", + emergency_mode ? "yes" : "no"); + } + + if (qmi_message_nas_get_system_selection_preference_output_get_mode_preference ( + output, + &mode_preference, + NULL)) { + g_autofree gchar *str = NULL; + + str = qmi_nas_rat_mode_preference_build_string_from_mask (mode_preference); + g_print ("\tMode preference: '%s'\n", VALIDATE_MASK_NONE (str)); + } + + if (qmi_message_nas_get_system_selection_preference_output_get_disabled_modes ( + output, + &disabled_modes, + NULL)) { + g_autofree gchar *str = NULL; + + str = qmi_nas_rat_mode_preference_build_string_from_mask (disabled_modes); + g_print ("\tDisabled modes: '%s'\n", VALIDATE_MASK_NONE (str)); + } + + + if (qmi_message_nas_get_system_selection_preference_output_get_band_preference ( + output, + &band_preference, + NULL)) { + g_autofree gchar *str = NULL; + + str = qmi_nas_band_preference_build_string_from_mask (band_preference); + g_print ("\tBand preference: '%s'\n", VALIDATE_MASK_NONE (str)); + } + + if (qmi_message_nas_get_system_selection_preference_output_get_lte_band_preference ( + output, + <e_band_preference, + NULL)) { + g_autofree gchar *str = NULL; + + str = qmi_nas_lte_band_preference_build_string_from_mask (lte_band_preference); + g_print ("\tLTE band preference: '%s'\n", VALIDATE_MASK_NONE (str)); + } + + if (qmi_message_nas_get_system_selection_preference_output_get_extended_lte_band_preference ( + output, + &extended_lte_band_preference[0], + &extended_lte_band_preference[1], + &extended_lte_band_preference[2], + &extended_lte_band_preference[3], + NULL)) { + guint i; + gboolean first = TRUE; + + g_print ("\tLTE band preference (extended): '"); + for (i = 0; i < G_N_ELEMENTS (extended_lte_band_preference); i++) { + guint j; + + for (j = 0; j < 64; j++) { + guint band; + + if (!(extended_lte_band_preference[i] & (((guint64) 1) << j))) + continue; + band = 1 + j + (i * 64); + if (first) { + g_print ("%u", band); + first = FALSE; + } else + g_print (", %u", band); + } + } + g_print ("'\n"); + } + + if (qmi_message_nas_get_system_selection_preference_output_get_nr5g_sa_band_preference ( + output, + &nr5g_sa_band_preference[0], + &nr5g_sa_band_preference[1], + &nr5g_sa_band_preference[2], + &nr5g_sa_band_preference[3], + &nr5g_sa_band_preference[4], + &nr5g_sa_band_preference[5], + &nr5g_sa_band_preference[6], + &nr5g_sa_band_preference[7], + NULL)) { + guint i; + gboolean first = TRUE; + + g_print ("\tNR5G SA band preference: '"); + for (i = 0; i < G_N_ELEMENTS (nr5g_sa_band_preference); i++) { + guint j; + + for (j = 0; j < 64; j++) { + guint band; + + if (!(nr5g_sa_band_preference[i] & (((guint64) 1) << j))) + continue; + band = 1 + j + (i * 64); + if (first) { + g_print ("%u", band); + first = FALSE; + } else + g_print (", %u", band); + } + } + g_print ("'\n"); + } + + if (qmi_message_nas_get_system_selection_preference_output_get_nr5g_nsa_band_preference ( + output, + &nr5g_nsa_band_preference[0], + &nr5g_nsa_band_preference[1], + &nr5g_nsa_band_preference[2], + &nr5g_nsa_band_preference[3], + &nr5g_nsa_band_preference[4], + &nr5g_nsa_band_preference[5], + &nr5g_nsa_band_preference[6], + &nr5g_nsa_band_preference[7], + NULL)) { + guint i; + gboolean first = TRUE; + + g_print ("\tNR5G NSA band preference: '"); + for (i = 0; i < G_N_ELEMENTS (nr5g_nsa_band_preference); i++) { + guint j; + + for (j = 0; j < 64; j++) { + guint band; + + if (!(nr5g_nsa_band_preference[i] & (((guint64) 1) << j))) + continue; + band = 1 + j + (i * 64); + if (first) { + g_print ("%u", band); + first = FALSE; + } else + g_print (", %u", band); + } + } + g_print ("'\n"); + } + + if (qmi_message_nas_get_system_selection_preference_output_get_td_scdma_band_preference ( + output, + &td_scdma_band_preference, + NULL)) { + g_autofree gchar *str = NULL; + + str = qmi_nas_td_scdma_band_preference_build_string_from_mask (td_scdma_band_preference); + g_print ("\tTD-SCDMA band preference: '%s'\n", VALIDATE_MASK_NONE (str)); + } + + if (qmi_message_nas_get_system_selection_preference_output_get_cdma_prl_preference ( + output, + &cdma_prl_preference, + NULL)) { + g_print ("\tCDMA PRL preference: '%s'\n", + qmi_nas_cdma_prl_preference_get_string (cdma_prl_preference)); + } + + if (qmi_message_nas_get_system_selection_preference_output_get_roaming_preference ( + output, + &roaming_preference, + NULL)) { + g_print ("\tRoaming preference: '%s'\n", + qmi_nas_roaming_preference_get_string (roaming_preference)); + } + + if (qmi_message_nas_get_system_selection_preference_output_get_network_selection_preference ( + output, + &network_selection_preference, + NULL)) { + g_print ("\tNetwork selection preference: '%s'\n", + qmi_nas_network_selection_preference_get_string (network_selection_preference)); + } + + + if (qmi_message_nas_get_system_selection_preference_output_get_service_domain_preference ( + output, + &service_domain_preference, + NULL)) { + g_print ("\tService domain preference: '%s'\n", + qmi_nas_service_domain_preference_get_string (service_domain_preference)); + } + + if (qmi_message_nas_get_system_selection_preference_output_get_gsm_wcdma_acquisition_order_preference ( + output, + &gsm_wcdma_acquisition_order_preference, + NULL)) { + g_print ("\tGSM/WCDMA acquisition order preference: '%s'\n", + qmi_nas_gsm_wcdma_acquisition_order_preference_get_string (gsm_wcdma_acquisition_order_preference)); + } + + if (qmi_message_nas_get_system_selection_preference_output_get_usage_preference ( + output, + &usage_preference, + NULL)) { + g_print ("\tUsage preference: '%s'\n", + qmi_nas_usage_preference_get_string (usage_preference)); + } + + if (qmi_message_nas_get_system_selection_preference_output_get_voice_domain_preference ( + output, + &voice_domain_preference, + NULL)) { + g_print ("\tVoice domain preference: '%s'\n", + qmi_nas_voice_domain_preference_get_string (voice_domain_preference)); + } + + + if (qmi_message_nas_get_system_selection_preference_output_get_network_selection_registration_restriction ( + output, + ®istration_restriction, + NULL)) { + g_print ("\tRegistration restriction: '%s'\n", qmi_nas_network_selection_registration_restriction_get_string (registration_restriction)); + } + + if (qmi_message_nas_get_system_selection_preference_output_get_manual_network_selection ( + output, + &mcc, + &mnc, + &has_pcs_digit, + NULL)) { + g_print ("\tManual network selection:\n" + "\t\tMCC: '%" G_GUINT16_FORMAT"'\n" + "\t\tMNC: '%" G_GUINT16_FORMAT"'\n" + "\t\tMCC with PCS digit: '%s'\n", + mcc, + mnc, + has_pcs_digit ? "yes" : "no"); + } + + if (qmi_message_nas_get_system_selection_preference_output_get_acquisition_order_preference ( + output, + &acquisition_order_preference, + NULL)) { + guint i; + + g_print ("\tAcquisition order preference: '"); + for (i = 0; i < acquisition_order_preference->len; i++) { + QmiNasRadioInterface radio_interface; + + radio_interface = g_array_index (acquisition_order_preference, QmiNasRadioInterface, i); + g_print ("%s%s", + i > 0 ? ", " : "", + qmi_nas_radio_interface_get_string (radio_interface)); + } + g_print ("'\n"); + } + + qmi_message_nas_get_system_selection_preference_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_SYSTEM_SELECTION_PREFERENCE */ + +#if defined HAVE_QMI_MESSAGE_NAS_SET_SYSTEM_SELECTION_PREFERENCE + +static QmiMessageNasSetSystemSelectionPreferenceInput * +set_system_selection_preference_input_create (const gchar *str) +{ + QmiMessageNasSetSystemSelectionPreferenceInput *input = NULL; + GError *error = NULL; + gchar *rat_pref_str = NULL; + gchar *net_pref_str = NULL; + QmiNasRatModePreference rat_mode_preference = 0; + GArray *acquisition_order = NULL; + QmiNasNetworkSelectionPreference net_preference = 0; + guint16 mcc = 0; + guint16 mnc = 0; + + if (strchr (str, ',')) { + gchar **parts; + + /* Both RAT mode preference and network selection preference were given */ + parts = g_strsplit_set (str, ",", -1); + if (g_strv_length (parts) != 2) { + g_printerr ("error: failed to parse selection pref: '%s'\n", str); + g_strfreev (parts); + return NULL; + } + rat_pref_str = g_strdup (parts[0]); + net_pref_str = g_strdup (parts[1]); + g_strfreev (parts); + } else if (g_str_has_prefix (str, "automatic") || g_str_has_prefix (str, "manual")) { + /* Only network selection preference was given */ + net_pref_str = g_strdup (str); + } else { + /* Only RAT mode preference was given */ + rat_pref_str = g_strdup (str); + } + + if (net_pref_str && !qmicli_read_ssp_net_options_from_string (net_pref_str, &net_preference, &mcc, &mnc)) { + g_printerr ("error: failed to parse network preference options: '%s'\n", net_pref_str); + goto out; + } + + if (rat_pref_str && !qmicli_read_ssp_rat_options_from_string (rat_pref_str, &rat_mode_preference, &acquisition_order)) { + g_printerr ("error: failed to parse system selection preference options: '%s'\n", rat_pref_str); + goto out; + } + + /* from now on, if an error happens, the GError must be set */ + input = qmi_message_nas_set_system_selection_preference_input_new (); + + if (!qmi_message_nas_set_system_selection_preference_input_set_change_duration (input, QMI_NAS_CHANGE_DURATION_PERMANENT, &error)) + goto out; + + if (rat_mode_preference && !qmi_message_nas_set_system_selection_preference_input_set_mode_preference (input, rat_mode_preference, &error)) + goto out; + + if ((rat_mode_preference & (QMI_NAS_RAT_MODE_PREFERENCE_GSM | QMI_NAS_RAT_MODE_PREFERENCE_UMTS | QMI_NAS_RAT_MODE_PREFERENCE_LTE)) && + (!qmi_message_nas_set_system_selection_preference_input_set_gsm_wcdma_acquisition_order_preference ( + input, + QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_AUTOMATIC, + &error))) + goto out; + + if (acquisition_order && !qmi_message_nas_set_system_selection_preference_input_set_acquisition_order_preference (input, acquisition_order, &error)) + goto out; + + if (net_pref_str && !qmi_message_nas_set_system_selection_preference_input_set_network_selection_preference (input, net_preference, mcc, mnc, &error)) + goto out; + +out: + g_free (rat_pref_str); + g_free (net_pref_str); + + if (acquisition_order) + g_array_unref (acquisition_order); + + if (error) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", error->message); + g_error_free (error); + qmi_message_nas_set_system_selection_preference_input_unref (input); + return NULL; + } + + return input; +} + +static void +set_system_selection_preference_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasSetSystemSelectionPreferenceOutput *output = NULL; + GError *error = NULL; + + output = qmi_client_nas_set_system_selection_preference_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_nas_set_system_selection_preference_output_get_result (output, &error)) { + g_printerr ("error: couldn't set operating mode: %s\n", error->message); + g_error_free (error); + qmi_message_nas_set_system_selection_preference_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] System selection preference set successfully; replug your device.\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_nas_set_system_selection_preference_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_SET_SYSTEM_SELECTION_PREFERENCE */ + +#if defined HAVE_QMI_MESSAGE_NAS_NETWORK_SCAN + +static void +network_scan_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasNetworkScanOutput *output; + QmiNasNetworkScanResult network_scan_result; + GError *error = NULL; + GArray *array; + + output = qmi_client_nas_network_scan_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_nas_network_scan_output_get_result (output, &error)) { + g_printerr ("error: couldn't scan networks: %s\n", error->message); + g_error_free (error); + qmi_message_nas_network_scan_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully scanned networks\n", + qmi_device_get_path_display (ctx->device)); + + array = NULL; + if (qmi_message_nas_network_scan_output_get_network_information (output, &array, NULL)) { + guint i; + + for (i = 0; i < array->len; i++) { + QmiMessageNasNetworkScanOutputNetworkInformationElement *element; + g_autofree gchar *status_str = NULL; + + element = &g_array_index (array, QmiMessageNasNetworkScanOutputNetworkInformationElement, i); + status_str = qmi_nas_network_status_build_string_from_mask (element->network_status); + g_print ("Network [%u]:\n" + "\tMCC: '%" G_GUINT16_FORMAT"'\n" + "\tMNC: '%" G_GUINT16_FORMAT"'\n" + "\tStatus: '%s'\n" + "\tDescription: '%s'\n", + i, + element->mcc, + element->mnc, + VALIDATE_MASK_NONE (status_str), + element->description); + } + } + + array = NULL; + if (qmi_message_nas_network_scan_output_get_radio_access_technology (output, &array, NULL)) { + guint i; + + for (i = 0; i < array->len; i++) { + QmiMessageNasNetworkScanOutputRadioAccessTechnologyElement *element; + + element = &g_array_index (array, QmiMessageNasNetworkScanOutputRadioAccessTechnologyElement, i); + g_print ("Network [%u]:\n" + "\tMCC: '%" G_GUINT16_FORMAT"'\n" + "\tMNC: '%" G_GUINT16_FORMAT"'\n" + "\tRAT: '%s'\n", + i, + element->mcc, + element->mnc, + qmi_nas_radio_interface_get_string (element->radio_interface)); + } + } + + array = NULL; + if (qmi_message_nas_network_scan_output_get_mnc_pcs_digit_include_status (output, &array, NULL)) { + guint i; + + for (i = 0; i < array->len; i++) { + QmiMessageNasNetworkScanOutputMncPcsDigitIncludeStatusElement *element; + + element = &g_array_index (array, QmiMessageNasNetworkScanOutputMncPcsDigitIncludeStatusElement, i); + g_print ("Network [%u]:\n" + "\tMCC: '%" G_GUINT16_FORMAT"'\n" + "\tMNC: '%" G_GUINT16_FORMAT"'\n" + "\tMCC with PCS digit: '%s'\n", + i, + element->mcc, + element->mnc, + element->includes_pcs_digit ? "yes" : "no"); + } + } + + if (qmi_message_nas_network_scan_output_get_network_scan_result (output, + &network_scan_result, + NULL)) { + g_print ("Network scan result: %s\n", + qmi_nas_network_scan_result_get_string (network_scan_result)); + } + + qmi_message_nas_network_scan_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_NETWORK_SCAN */ + +#if defined HAVE_QMI_MESSAGE_NAS_GET_CELL_LOCATION_INFO + +static gchar * +str_from_bcd_plmn (GArray *bcd) +{ + static const gchar bcd_chars[] = "0123456789*#abc\0\0"; + gchar *str; + guint i; + guint j; + + if (!bcd || !bcd->len) + return NULL; + + str = g_malloc (1 + (bcd->len * 2)); + for (i = 0, j = 0 ; i < bcd->len; i++) { + str[j] = bcd_chars[g_array_index (bcd, guint8, i) & 0xF]; + if (str[j]) + j++; + str[j] = bcd_chars[(g_array_index (bcd, guint8, i) >> 4) & 0xF]; + if (str[j]) + j++; + } + str[j] = '\0'; + + return str; +} + +static void +get_cell_location_info_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasGetCellLocationInfoOutput *output; + GError *error = NULL; + GArray *array; + GArray *array2; + + GArray *operator; + + guint32 cell_id; + guint16 lac; + guint16 absolute_rf_channel_number; + guint8 base_station_identity_code; + guint32 timing_advance; + guint16 rx_level; + + guint16 cell_id_16; + guint16 primary_scrambling_code; + gint16 rscp; + gint16 ecio; + + guint16 system_id; + guint16 network_id; + guint16 base_station_id; + guint16 reference_pn; + guint32 latitude; + guint32 longitude; + + gboolean ue_in_idle; + guint16 tracking_area_code; + guint32 global_cell_id; + guint16 serving_cell_id; + guint8 cell_reselection_priority; + guint8 s_non_intra_search_threshold; + guint8 serving_cell_low_threshold; + guint8 s_intra_search_threshold; + + QmiNasWcdmaRrcState rrc_state; + + guint32 lte_timing_advance; + + guint32 nr5g_arfcn; + + GArray *nr5g_cell_information_plmn; + GArray *nr5g_cell_information_tac; + guint64 nr5g_cell_information_global_cell_id; + guint16 nr5g_cell_information_physical_cell_id; + gint16 nr5g_cell_information_rsrp; + gint16 nr5g_cell_information_rsrq; + gint16 nr5g_cell_information_snr; + + output = qmi_client_nas_get_cell_location_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_nas_get_cell_location_info_output_get_result (output, &error)) { + g_printerr ("error: couldn't get cell location info: %s\n", error->message); + g_error_free (error); + qmi_message_nas_get_cell_location_info_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got cell location info\n", + qmi_device_get_path_display (ctx->device)); + + array = NULL; + if (qmi_message_nas_get_cell_location_info_output_get_geran_info_v2 ( + output, + &cell_id, + &operator, + &lac, + &absolute_rf_channel_number, + &base_station_identity_code, + &timing_advance, + &rx_level, + &array, NULL)) { + guint i; + + g_print ("GERAN Info\n"); + if (cell_id == 0xFFFFFFFF) + g_print ("\tCell ID: 'unavailable'\n" + "\tPLMN: 'unavailable'\n" + "\tLocation Area Code: 'unavailable'\n"); + else { + gchar *plmn; + + plmn = str_from_bcd_plmn (operator); + g_print ("\tCell ID: '%" G_GUINT32_FORMAT"'\n" + "\tPLMN: '%s'\n" + "\tLocation Area Code: '%" G_GUINT16_FORMAT"'\n", + cell_id, + plmn, + lac); + g_free (plmn); + } + g_print ("\tGERAN Absolute RF Channel Number: '%" G_GUINT16_FORMAT"'\n" + "\tBase Station Identity Code: '%u'\n", + absolute_rf_channel_number, + base_station_identity_code); + if (timing_advance == 0xFFFFFFFF) + g_print ("\tTiming Advance: 'unavailable'\n"); + else + g_print ("\tTiming Advance: '%" G_GUINT32_FORMAT"' bit periods ('%lf' us)\n", + timing_advance, + (gdouble)timing_advance * 48.0 / 13.0); + if (rx_level == 0) + g_print ("\tRX Level: -110 dBm > level ('%" G_GUINT16_FORMAT"')\n", rx_level); + else if (rx_level == 63) + g_print ("\tRX Level: level > -48 dBm ('%" G_GUINT16_FORMAT"')\n", rx_level); + else if (rx_level > 0 && rx_level < 63) + g_print ("\tRX Level: %d dBm > level > %d dBm ('%" G_GUINT16_FORMAT"')\n", + (gint32) rx_level - 111, + (gint32) rx_level - 110, + rx_level); + else + g_print ("\tRX Level: invalid ('%" G_GUINT16_FORMAT"')\n", rx_level); + + + for (i = 0; i < array->len; i++) { + QmiMessageNasGetCellLocationInfoOutputGeranInfoV2CellElement *element; + + element = &g_array_index (array, QmiMessageNasGetCellLocationInfoOutputGeranInfoV2CellElement, i); + + g_print ("\tCell [%u]:\n", i); + if (element->cell_id == 0xFFFFFFFF) + g_print ("\t\tCell ID: 'unavailable'\n" + "\t\tPLMN: 'unavailable'\n" + "\t\tLocation Area Code: 'unavailable'\n"); + else { + gchar *plmn; + + plmn = str_from_bcd_plmn (element->plmn); + g_print ("\t\tCell ID: '%" G_GUINT32_FORMAT"'\n" + "\t\tPLMN: '%s'\n" + "\t\tLocation Area Code: '%" G_GUINT16_FORMAT"'\n", + element->cell_id, + plmn, + element->lac); + g_free (plmn); + } + g_print ("\t\tGERAN Absolute RF Channel Number: '%" G_GUINT16_FORMAT"'\n" + "\t\tBase Station Identity Code: '%u'\n", + element->geran_absolute_rf_channel_number, + element->base_station_identity_code); + if (element->rx_level == 0) + g_print ("\t\tRX Level: -110 dBm > level ('%" G_GUINT16_FORMAT"')\n", element->rx_level); + else if (element->rx_level == 63) + g_print ("\t\tRX Level: level > -48 dBm ('%" G_GUINT16_FORMAT"')\n", element->rx_level); + else if (element->rx_level > 0 && element->rx_level < 63) + g_print ("\t\tRX Level: %d dBm > level > %d dBm ('%" G_GUINT16_FORMAT"')\n", + (gint32) element->rx_level - 111, + (gint32) element->rx_level - 110, + element->rx_level); + else + g_print ("\tRX Level: invalid ('%" G_GUINT16_FORMAT"')\n", element->rx_level); + } + } + + array = NULL; + array2 = NULL; + if (qmi_message_nas_get_cell_location_info_output_get_umts_info_v2 ( + output, + &cell_id_16, + &operator, + &lac, + &absolute_rf_channel_number, + &primary_scrambling_code, + &rscp, + &ecio, + &array, &array2, NULL)) { + guint i; + gchar *plmn; + + g_print ("UMTS Info\n"); + if (cell_id_16 == 0xFFFF) + g_print ("\tCell ID: 'unavailable'\n"); + else + g_print ("\tCell ID: '%" G_GUINT16_FORMAT"'\n", cell_id_16); + plmn = str_from_bcd_plmn (operator); + g_print ("\tPLMN: '%s'\n" + "\tLocation Area Code: '%" G_GUINT16_FORMAT"'\n" + "\tUTRA Absolute RF Channel Number: '%" G_GUINT16_FORMAT"'\n" + "\tPrimary Scrambling Code: '%" G_GUINT16_FORMAT"'\n" + "\tRSCP: '%" G_GINT16_FORMAT"' dBm\n" + "\tECIO: '%" G_GINT16_FORMAT"' dBm\n", + plmn, + lac, + absolute_rf_channel_number, + primary_scrambling_code, + rscp, + ecio); + g_free (plmn); + + for (i = 0; i < array->len; i++) { + QmiMessageNasGetCellLocationInfoOutputUmtsInfoV2CellElement *element; + + element = &g_array_index (array, QmiMessageNasGetCellLocationInfoOutputUmtsInfoV2CellElement, i); + g_print ("\tCell [%u]:\n" + "\t\tUTRA Absolute RF Channel Number: '%" G_GUINT16_FORMAT"'\n" + "\t\tPrimary Scrambling Code: '%" G_GUINT16_FORMAT"'\n" + "\t\tRSCP: '%" G_GINT16_FORMAT"' dBm\n" + "\t\tECIO: '%" G_GINT16_FORMAT"' dBm\n", + i, + element->utra_absolute_rf_channel_number, + element->primary_scrambling_code, + element->rscp, + element->ecio); + } + + for (i = 0; i < array2->len; i++) { + QmiMessageNasGetCellLocationInfoOutputUmtsInfoV2NeighboringGeranElement *element; + + element = &g_array_index (array2, QmiMessageNasGetCellLocationInfoOutputUmtsInfoV2NeighboringGeranElement, i); + g_print ("\tNeighboring GERAN Cell [%u]:\n" + "\t\tGERAN Absolute RF Channel Number: '%" G_GUINT16_FORMAT"'\n", + i, + element->geran_absolute_rf_channel_number); + if (element->network_color_code == 0xFF) + g_print ("\t\tNetwork Color Code: 'unavailable'\n"); + else + g_print ("\t\tNetwork Color Code: '%u'\n", element->network_color_code); + if (element->base_station_color_code == 0xFF) + g_print ("\t\tBase Station Color Code: 'unavailable'\n"); + else + g_print ("\t\tBase Station Color Code: '%u'\n", element->base_station_color_code); + g_print ("\t\tRSSI: '%" G_GUINT16_FORMAT"'\n", + element->rssi); + } + } + + if (qmi_message_nas_get_cell_location_info_output_get_cdma_info ( + output, + &system_id, + &network_id, + &base_station_id, + &reference_pn, + &latitude, + &longitude, + NULL)) { + gdouble latitude_degrees; + gdouble longitude_degrees; + + /* TODO: give degrees, minutes, seconds */ + latitude_degrees = ((gdouble)latitude * 0.25)/3600.0; + longitude_degrees = ((gdouble)longitude * 0.25)/3600.0; + g_print ("CDMA Info\n" + "\tSystem ID: '%" G_GUINT16_FORMAT"'\n" + "\tNetwork ID: '%" G_GUINT16_FORMAT"'\n" + "\tBase Station ID: '%" G_GUINT16_FORMAT"'\n" + "\tReference PN: '%" G_GUINT16_FORMAT"'\n" + "\tLatitude: '%lf'º\n" + "\tLongitude: '%lfº'\n", + system_id, + network_id, + base_station_id, + reference_pn, + latitude_degrees, + longitude_degrees); + } + + array = NULL; + if (qmi_message_nas_get_cell_location_info_output_get_intrafrequency_lte_info_v2 ( + output, + &ue_in_idle, + &operator, + &tracking_area_code, + &global_cell_id, + &absolute_rf_channel_number, + &serving_cell_id, + &cell_reselection_priority, + &s_non_intra_search_threshold, + &serving_cell_low_threshold, + &s_intra_search_threshold, + &array, NULL)) { + guint i; + gchar *plmn; + + plmn = str_from_bcd_plmn (operator); + g_print ("Intrafrequency LTE Info\n" + "\tUE In Idle: '%s'\n" + "\tPLMN: '%s'\n" + "\tTracking Area Code: '%" G_GUINT16_FORMAT"'\n" + "\tGlobal Cell ID: '%" G_GUINT32_FORMAT"'\n" + "\tEUTRA Absolute RF Channel Number: '%" G_GUINT16_FORMAT"' (%s)\n" + "\tServing Cell ID: '%" G_GUINT16_FORMAT"'\n", + ue_in_idle ? "yes" : "no", + plmn, + tracking_area_code, + global_cell_id, + absolute_rf_channel_number, qmicli_earfcn_to_eutra_band_string (absolute_rf_channel_number), + serving_cell_id); + g_free (plmn); + if (ue_in_idle) + g_print ("\tCell Reselection Priority: '%u'\n" + "\tS Non Intra Search Threshold: '%u'\n" + "\tServing Cell Low Threshold: '%u'\n" + "\tS Intra Search Threshold: '%u'\n", + cell_reselection_priority, + s_non_intra_search_threshold, + serving_cell_low_threshold, + s_intra_search_threshold); + + for (i = 0; i < array->len; i++) { + QmiMessageNasGetCellLocationInfoOutputIntrafrequencyLteInfoV2CellElement *element; + + element = &g_array_index (array, QmiMessageNasGetCellLocationInfoOutputIntrafrequencyLteInfoV2CellElement, i); + g_print ("\tCell [%u]:\n" + "\t\tPhysical Cell ID: '%" G_GUINT16_FORMAT"'\n" + "\t\tRSRQ: '%.1lf' dB\n" + "\t\tRSRP: '%.1lf' dBm\n" + "\t\tRSSI: '%.1lf' dBm\n", + i, + element->physical_cell_id, + (gdouble) element->rsrq * 0.1, + (gdouble) element->rsrp * 0.1, + (gdouble) element->rssi * 0.1); + if (ue_in_idle) + g_print ("\t\tCell Selection RX Level: '%" G_GINT16_FORMAT"'\n", + element->cell_selection_rx_level); + } + } + + array = NULL; + if (qmi_message_nas_get_cell_location_info_output_get_interfrequency_lte_info ( + output, + &ue_in_idle, + &array, NULL)) { + guint i; + + g_print ("Interfrequency LTE Info\n" + "\tUE In Idle: '%s'\n", ue_in_idle ? "yes" : "no"); + + for (i = 0; i < array->len; i++) { + QmiMessageNasGetCellLocationInfoOutputInterfrequencyLteInfoFrequencyElement *element; + GArray *cell_array; + guint j; + + element = &g_array_index (array, QmiMessageNasGetCellLocationInfoOutputInterfrequencyLteInfoFrequencyElement, i); + g_print ("\tFrequency [%u]:\n" + "\t\tEUTRA Absolute RF Channel Number: '%" G_GUINT16_FORMAT"' (%s)\n" + "\t\tSelection RX Level Low Threshold: '%u'\n" + "\t\tCell Selection RX Level High Threshold: '%u'\n", + i, + element->eutra_absolute_rf_channel_number, qmicli_earfcn_to_eutra_band_string (element->eutra_absolute_rf_channel_number), + element->cell_selection_rx_level_low_threshold, + element->cell_selection_rx_level_high_threshold); + if (ue_in_idle) + g_print ("\t\tCell Reselection Priority: '%u'\n", + element->cell_reselection_priority); + + cell_array = element->cell; + + for (j = 0; j < cell_array->len; j++) { + QmiMessageNasGetCellLocationInfoOutputInterfrequencyLteInfoFrequencyElementCellElement *cell; + + cell = &g_array_index (cell_array, QmiMessageNasGetCellLocationInfoOutputInterfrequencyLteInfoFrequencyElementCellElement, j); + g_print ("\t\tCell [%u]:\n" + "\t\t\tPhysical Cell ID: '%" G_GUINT16_FORMAT"'\n" + "\t\t\tRSRQ: '%.1lf' dB\n" + "\t\t\tRSRP: '%.1lf' dBm\n" + "\t\t\tRSSI: '%.1lf' dBm\n" + "\t\t\tCell Selection RX Level: '%" G_GINT16_FORMAT"'\n", + j, + cell->physical_cell_id, + (gdouble) cell->rsrq * 0.1, + (gdouble) cell->rsrp * 0.1, + (gdouble) cell->rssi * 0.1, + cell->cell_selection_rx_level); + } + } + } + + array = NULL; + if (qmi_message_nas_get_cell_location_info_output_get_lte_info_neighboring_gsm ( + output, + &ue_in_idle, + &array, NULL)) { + guint i; + + g_print ("LTE Info Neighboring GSM\n" + "\tUE In Idle: '%s'\n", ue_in_idle ? "yes" : "no"); + + for (i = 0; i < array->len; i++) { + QmiMessageNasGetCellLocationInfoOutputLteInfoNeighboringGsmFrequencyElement *element; + GArray *cell_array; + guint j; + + element = &g_array_index (array, QmiMessageNasGetCellLocationInfoOutputLteInfoNeighboringGsmFrequencyElement, i); + + g_print ("\tFrequency [%u]:\n", i); + if (ue_in_idle) + g_print ("\t\tCell Reselection Priority: '%u'\n" + "\t\tCell Reselection High Threshold: '%u'\n" + "\t\tCell Reselection Low Threshold: '%u'\n" + "\t\tNCC Permitted: '0x%2X'\n", + element->cell_reselection_priority, + element->cell_reselection_high_threshold, + element->cell_reselection_low_threshold, + element->ncc_permitted); + + cell_array = element->cell; + + for (j = 0; j < cell_array->len; j++) { + QmiMessageNasGetCellLocationInfoOutputLteInfoNeighboringGsmFrequencyElementCellElement *cell; + + cell = &g_array_index (cell_array, QmiMessageNasGetCellLocationInfoOutputLteInfoNeighboringGsmFrequencyElementCellElement, j); + g_print ("\t\tCell [%u]:\n" + "\t\t\tGERAN Absolute RF Channel Number: '%" G_GUINT16_FORMAT"'\n" + "\t\t\tBand Is 1900: '%s'\n", + j, + cell->geran_absolute_rf_channel_number, + cell->band_is_1900 ? "yes" : "no"); + if (cell->cell_id_valid) + g_print ("\t\t\tBase Station Identity Code: '%u'\n", + cell->base_station_identity_code); + else + g_print ("\t\t\tBase Station Identity Code: 'unknown'\n"); + g_print ("\t\t\tRSSI: '%.1lf' dB\n" + "\t\t\tCell Selection RX Level: '%" G_GINT16_FORMAT"'\n", + (gdouble) cell->rssi * 0.1, + cell->cell_selection_rx_level); + } + } + } + + array = NULL; + if (qmi_message_nas_get_cell_location_info_output_get_lte_info_neighboring_wcdma ( + output, + &ue_in_idle, + &array, NULL)) { + guint i; + + g_print ("LTE Info Neighboring WCDMA\n" + "\tUE In Idle: '%s'\n", ue_in_idle ? "yes" : "no"); + + for (i = 0; i < array->len; i++) { + QmiMessageNasGetCellLocationInfoOutputLteInfoNeighboringWcdmaFrequencyElement *element; + GArray *cell_array; + guint j; + + element = &g_array_index (array, QmiMessageNasGetCellLocationInfoOutputLteInfoNeighboringWcdmaFrequencyElement, i); + + g_print ("\tFrequency [%u]:\n" + "\t\tUTRA Absolute RF Channel Number: '%" G_GUINT16_FORMAT"'\n", + i, + element->utra_absolute_rf_channel_number); + if (ue_in_idle) + g_print ("\t\tCell Reselection Priority: '%u'\n" + "\t\tCell Reselection High Threshold: '%" G_GINT16_FORMAT"'\n" + "\t\tCell Reselection Low Threshold: '%" G_GINT16_FORMAT"'\n", + element->cell_reselection_priority, + element->cell_reselection_high_threshold, + element->cell_reselection_low_threshold); + + cell_array = element->cell; + + for (j = 0; j < cell_array->len; j++) { + QmiMessageNasGetCellLocationInfoOutputLteInfoNeighboringWcdmaFrequencyElementCellElement *cell; + + cell = &g_array_index (cell_array, QmiMessageNasGetCellLocationInfoOutputLteInfoNeighboringWcdmaFrequencyElementCellElement, j); + g_print ("\t\tCell [%u]:\n" + "\t\t\tPrimary Scrambling Code: '%" G_GUINT16_FORMAT"'\n" + "\t\t\tCPICH RSCP: '%.1lf' dBm\n" + "\t\t\tCPICH EcNo: '%.1lf' dB\n", + j, + cell->primary_scrambling_code, + (gdouble) cell->cpich_rscp * 0.1, + (gdouble) cell->cpich_ecno * 0.1); + if (ue_in_idle) + g_print ("\t\t\tCell Selection RX Level: '%" G_GUINT16_FORMAT"'\n", + cell->cell_selection_rx_level); + } + } + } + + if (qmi_message_nas_get_cell_location_info_output_get_umts_cell_id ( + output, + &cell_id, + NULL)) { + g_print ("UMTS Cell ID: '%" G_GUINT32_FORMAT"'\n", cell_id); + } + + array = NULL; + if (qmi_message_nas_get_cell_location_info_output_get_umts_info_neighboring_lte ( + output, + &rrc_state, + &array, NULL)) { + guint i; + + g_print ("UMTS Info Neighboring LTE\n" + "\tRRC State: '%s'\n", qmi_nas_wcdma_rrc_state_get_string (rrc_state)); + + for (i = 0; i < array->len; i++) { + QmiMessageNasGetCellLocationInfoOutputUmtsInfoNeighboringLteFrequencyElement *element; + + element = &g_array_index (array, QmiMessageNasGetCellLocationInfoOutputUmtsInfoNeighboringLteFrequencyElement, i); + + g_print ("\tFrequency [%u]:\n" + "\t\tEUTRA Absolute RF Channel Number: '%" G_GUINT16_FORMAT"' (%s)\n" + "\t\tPhysical Cell ID: '%" G_GUINT16_FORMAT "'\n" + "\t\tRSRP: '%lf' dBm\n" + "\t\tRSRQ: '%lf' dB\n", + i, + element->eutra_absolute_rf_channel_number, qmicli_earfcn_to_eutra_band_string (element->eutra_absolute_rf_channel_number), + element->physical_cell_id, + (gdouble)element->rsrp, + (gdouble)element->rsrq); + if (rrc_state != QMI_NAS_WCDMA_RRC_STATE_CELL_FACH && + rrc_state != QMI_NAS_WCDMA_RRC_STATE_CELL_DCH) + g_print ("\t\tCell Selection RX Level: '%" G_GINT16_FORMAT"'\n", + element->cell_selection_rx_level); + g_print ("\t\tIs TDD?: '%s'\n", + element->is_tdd ? "yes" : "no"); + } + } + + if (qmi_message_nas_get_cell_location_info_output_get_lte_info_timing_advance ( + output, + <e_timing_advance, + NULL)) { + if (lte_timing_advance == 0xFFFFFFFF) + g_print ("LTE Timing Advance: 'unavailable'\n"); + else + g_print ("LTE Timing Advance: '%" G_GUINT32_FORMAT"' us\n", lte_timing_advance); + } + + if (qmi_message_nas_get_cell_location_info_output_get_nr5g_arfcn ( + output, + &nr5g_arfcn, + NULL)) { + g_print ("5GNR ARFCN: '%" G_GUINT32_FORMAT"'\n", nr5g_arfcn); + } + + if (qmi_message_nas_get_cell_location_info_output_get_nr5g_cell_information ( + output, + &nr5g_cell_information_plmn, + &nr5g_cell_information_tac, + &nr5g_cell_information_global_cell_id, + &nr5g_cell_information_physical_cell_id, + &nr5g_cell_information_rsrq, + &nr5g_cell_information_rsrp, + &nr5g_cell_information_snr, + NULL)) { + g_autofree gchar *plmn = NULL; + guint32 tac; + + plmn = str_from_bcd_plmn (nr5g_cell_information_plmn); + + g_assert (nr5g_cell_information_tac->len == 3); + tac = ((((g_array_index (nr5g_cell_information_tac, guint8, 0) << 8) | + g_array_index (nr5g_cell_information_tac, guint8, 1)) << 8) | + g_array_index (nr5g_cell_information_tac, guint8, 2)); + + g_print ("5GNR cell information\n" + "\tPLMN: '%s'\n" + "\tTracking Area Code: '%" G_GUINT32_FORMAT "'\n" + "\tGlobal Cell ID: '%" G_GUINT64_FORMAT "'\n" + "\tPhysical Cell ID: '%" G_GUINT16_FORMAT "'\n" + "\tRSRQ: '%.1lf dB'\n" + "\tRSRP: '%.1lf dBm'\n" + "\tSNR: '%.1lf dB'\n", + plmn, + tac, + nr5g_cell_information_global_cell_id, + nr5g_cell_information_physical_cell_id, + (0.1) * ((gdouble)nr5g_cell_information_rsrq), + (0.1) * ((gdouble)nr5g_cell_information_rsrp), + (0.1) * ((gdouble)nr5g_cell_information_snr)); + } + + qmi_message_nas_get_cell_location_info_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_CELL_LOCATION_INFO */ + +#if defined HAVE_QMI_MESSAGE_NAS_FORCE_NETWORK_SEARCH + +static void +force_network_search_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasForceNetworkSearchOutput *output; + GError *error = NULL; + + output = qmi_client_nas_force_network_search_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_nas_force_network_search_output_get_result (output, &error)) { + g_printerr ("error: couldn't force network search: %s\n", error->message); + g_error_free (error); + qmi_message_nas_force_network_search_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully forced network search\n", + qmi_device_get_path_display (ctx->device)); + qmi_message_nas_force_network_search_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_FORCE_NETWORK_SEARCH */ + +#if defined HAVE_QMI_MESSAGE_NAS_GET_OPERATOR_NAME + +static void +get_operator_name_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasGetOperatorNameOutput *output; + GError *error = NULL; + QmiNasNetworkNameDisplayCondition spn_display_condition; + const gchar *spn; + const gchar *operator_name; + GArray *array; + QmiNasPlmnEncodingScheme nitz_information_name_encoding; + QmiNasPlmnNameCountryInitials nitz_information_short_country_initials; + QmiNasPlmnNameSpareBits nitz_information_long_name_spare_bits; + QmiNasPlmnNameSpareBits nitz_information_short_name_spare_bits; + GArray *nitz_information_long_name; + GArray *nitz_information_short_name; + + output = qmi_client_nas_get_operator_name_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_nas_get_operator_name_output_get_result (output, &error)) { + g_printerr ("error: couldn't get operator name data: %s\n", error->message); + g_error_free (error); + qmi_message_nas_get_operator_name_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got operator name data\n", + qmi_device_get_path_display (ctx->device)); + + if (qmi_message_nas_get_operator_name_output_get_service_provider_name ( + output, + &spn_display_condition, + &spn, + NULL)) { + g_autofree gchar *dc_string = NULL; + + dc_string = qmi_nas_network_name_display_condition_build_string_from_mask (spn_display_condition); + g_print ("Service Provider Name\n"); + g_print ("\tDisplay Condition: '%s'\n" + "\tName : '%s'\n", + VALIDATE_MASK_NONE (dc_string), + spn); + } + + if (qmi_message_nas_get_operator_name_output_get_operator_string_name ( + output, + &operator_name, + NULL)) { + g_print ("Operator Name: '%s'\n", + operator_name); + } + + if (qmi_message_nas_get_operator_name_output_get_operator_plmn_list (output, &array, NULL)) { + guint i; + + g_print ("PLMN List:\n"); + for (i = 0; i < array->len; i++) { + QmiMessageNasGetOperatorNameOutputOperatorPlmnListElement *element; + gchar *mnc; + + element = &g_array_index (array, QmiMessageNasGetOperatorNameOutputOperatorPlmnListElement, i); + mnc = g_strdup (element->mnc); + if (strlen (mnc) >= 3 && (mnc[2] == 'F' || mnc[2] == 'f')) + mnc[2] = '\0'; + g_print ("\tMCC/MNC: '%s-%s'%s LAC Range: %u->%u\tPNN Record: %u\n", + element->mcc, + mnc, + mnc[2] == '\0' ? " " : "", + element->lac1, + element->lac2, + element->plmn_name_record_identifier); + g_free(mnc); + } + } + + if (qmi_message_nas_get_operator_name_output_get_operator_plmn_name (output, &array, NULL)) { + guint i; + + g_print ("PLMN Names:\n"); + for (i = 0; i < array->len; i++) { + QmiMessageNasGetOperatorNameOutputOperatorPlmnNameElement *element; + gchar *long_name; + gchar *short_name; + + element = &g_array_index (array, QmiMessageNasGetOperatorNameOutputOperatorPlmnNameElement, i); + long_name = qmi_nas_read_string_from_plmn_encoded_array (element->name_encoding, element->long_name); + short_name = qmi_nas_read_string_from_plmn_encoded_array (element->name_encoding, element->short_name); + g_print ("\t%d: '%s'%s%s%s\t\tCountry: '%s'\n", + i, + long_name ?: "", + short_name ? " ('" : "", + short_name ?: "", + short_name ? "')" : "", + qmi_nas_plmn_name_country_initials_get_string (element->short_country_initials)); + g_free (long_name); + g_free (short_name); + } + } + + if (qmi_message_nas_get_operator_name_output_get_nitz_information ( + output, + &nitz_information_name_encoding, + &nitz_information_short_country_initials, + &nitz_information_long_name_spare_bits, + &nitz_information_short_name_spare_bits, + &nitz_information_long_name, + &nitz_information_short_name, + NULL)) { + g_autofree gchar *long_name = NULL; + g_autofree gchar *short_name = NULL; + + long_name = qmi_nas_read_string_from_plmn_encoded_array (nitz_information_name_encoding, nitz_information_long_name); + short_name = qmi_nas_read_string_from_plmn_encoded_array (nitz_information_name_encoding, nitz_information_short_name); + g_print ("NITZ information:\n" + "\tLong Name: '%s'\n" + "\tShort Name: '%s'\n" + "\tCountry: '%s'\n", + long_name ?: "", + short_name ?: "", + qmi_nas_plmn_name_country_initials_get_string (nitz_information_short_country_initials)); + } + + qmi_message_nas_get_operator_name_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_OPERATOR_NAME */ + +#if defined HAVE_QMI_MESSAGE_NAS_GET_PLMN_NAME + +static QmiMessageNasGetPlmnNameInput * +set_plmn_name_input_plmn_create (const gchar *str) +{ + g_autoptr(GError) error = NULL; + g_autoptr(QmiMessageNasGetPlmnNameInput) input = NULL; + guint16 mcc = 0; + guint16 mnc = 0; + + if (!qmicli_read_parse_3gpp_mcc_mnc (str, &mcc, &mnc, NULL)) { + g_printerr ("error: invalid net selection MCC/MNC: '%s'\n", str); + return NULL; + } + + input = qmi_message_nas_get_plmn_name_input_new (); + if (!qmi_message_nas_get_plmn_name_input_set_plmn ( + input, + mcc, + mnc, + &error)) { + g_printerr ("error: couldn't set MCC/MNC: '%s'\n", + error->message); + g_clear_pointer(&input, qmi_message_nas_get_plmn_name_input_unref); + } + + return input; +} + +static void +get_plmn_name_ready (QmiClientNas *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageNasGetPlmnNameOutput) output = NULL; + g_autoptr(GError) error = NULL; + GArray *array; + QmiNasNetworkDescriptionEncoding plmn_name_service_provider_name_encoding; + GArray *plmn_name_service_provider_name; + QmiNasNetworkDescriptionEncoding plmn_name_short_name_encoding; + QmiNasPlmnNameCountryInitials plmn_name_short_name_country_initials; + QmiNasPlmnNameSpareBits plmn_name_short_name_spare_bits; + GArray *plmn_name_short_name; + QmiNasNetworkDescriptionEncoding plmn_name_long_name_encoding; + QmiNasPlmnNameCountryInitials plmn_name_long_name_country_initials; + QmiNasPlmnNameSpareBits plmn_name_long_name_spare_bits; + GArray *plmn_name_long_name; + + output = qmi_client_nas_get_plmn_name_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_nas_get_plmn_name_output_get_result (output, &error)) { + g_printerr ("error: couldn't get operator name data: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got plmn name data\n", + qmi_device_get_path_display (ctx->device)); + + if (qmi_message_nas_get_plmn_name_output_get_3gpp_eons_plmn_name ( + output, + &plmn_name_service_provider_name_encoding, + &plmn_name_service_provider_name, + &plmn_name_short_name_encoding, + &plmn_name_short_name_country_initials, + &plmn_name_short_name_spare_bits, + &plmn_name_short_name, + &plmn_name_long_name_encoding, + &plmn_name_long_name_country_initials, + &plmn_name_long_name_spare_bits, + &plmn_name_long_name, + NULL)) { + g_autofree gchar *long_name = NULL; + g_autofree gchar *short_name = NULL; + g_autofree gchar *service_name = NULL; + long_name = qmi_nas_read_string_from_network_description_encoded_array (plmn_name_long_name_encoding, plmn_name_long_name); + short_name = qmi_nas_read_string_from_network_description_encoded_array (plmn_name_short_name_encoding, plmn_name_short_name); + service_name = qmi_nas_read_string_from_network_description_encoded_array (plmn_name_service_provider_name_encoding, plmn_name_service_provider_name); + g_print ("3GPP EONS PLMN Name:\n" + "\tLong Name: '%s'\n" + "\tShort Name: '%s'\n" + "\tService Name: '%s'\n" + "\tCountry: '%s'\n", + long_name ?: "", + short_name ?: "", + service_name ?: "", + qmi_nas_plmn_name_country_initials_get_string (plmn_name_short_name_country_initials)); + } + + if (qmi_message_nas_get_plmn_name_output_get_plmn_name_with_language_id (output, &array, NULL)) { + guint i; + + g_print ("3GPP EONS PLMN Name with Language ID:\n"); + for (i = 0; i < array->len; i++) { + QmiMessageNasGetPlmnNameOutputPlmnNameWithLanguageIdElement *element; + g_autofree gchar *long_name; + g_autofree gchar *short_name; + + element = &g_array_index (array, QmiMessageNasGetPlmnNameOutputPlmnNameWithLanguageIdElement, i); + long_name = qmi_nas_read_string_from_plmn_encoded_array (QMI_NAS_PLMN_ENCODING_SCHEME_UCS2LE, element->long_name); + short_name = qmi_nas_read_string_from_plmn_encoded_array (QMI_NAS_PLMN_ENCODING_SCHEME_UCS2LE, element->short_name); + g_print ("\t%d: '%s'%s%s%s\t\tCountry: '%s'\n", + i, + long_name ?: "", + short_name ? " ('" : "", + short_name ?: "", + short_name ? "')" : "", + qmi_nas_plmn_language_id_get_string (element->language_id)); + } + } + + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_PLMN_NAME */ + +#if defined HAVE_QMI_MESSAGE_NAS_GET_LTE_CPHY_CA_INFO + +static void +get_lte_cphy_ca_info_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasGetLteCphyCaInfoOutput *output; + GError *error = NULL; + guint16 pci; + guint16 channel; + QmiNasDLBandwidth dl_bandwidth; + QmiNasActiveBand band; + QmiNasScellState state; + guint8 scell_index; + GArray *array; + + output = qmi_client_nas_get_lte_cphy_ca_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_nas_get_lte_cphy_ca_info_output_get_result (output, &error)) { + g_printerr ("error: couldn't get carrier aggregation info: %s\n", error->message); + g_error_free (error); + qmi_message_nas_get_lte_cphy_ca_info_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got carrier aggregation info\n", + qmi_device_get_path_display (ctx->device)); + + if (qmi_message_nas_get_lte_cphy_ca_info_output_get_dl_bandwidth ( + output, + &dl_bandwidth, + NULL)) { + g_print ("DL Bandwidth: '%s'\n", + qmi_nas_dl_bandwidth_get_string (dl_bandwidth)); + } + + if (qmi_message_nas_get_lte_cphy_ca_info_output_get_phy_ca_agg_pcell_info ( + output, + &pci, + &channel, + &dl_bandwidth, + &band, + NULL)) { + g_print ("Primary Cell Info\n"); + g_print ("\tPhysical Cell ID: '%" G_GUINT16_FORMAT"'\n" + "\tRX Channel: '%" G_GUINT16_FORMAT"'\n" + "\tDL Bandwidth: '%s'\n" + "\tLTE Band: '%s'\n", + pci, channel, + qmi_nas_dl_bandwidth_get_string (dl_bandwidth), + qmi_nas_active_band_get_string (band)); + } + + if (qmi_message_nas_get_lte_cphy_ca_info_output_get_phy_ca_agg_secondary_cells ( + output, &array, NULL)) { + guint i; + + if (!array->len) + g_print ("No Secondary Cells\n"); + for (i = 0; i < array->len; i++) { + QmiMessageNasGetLteCphyCaInfoOutputPhyCaAggSecondaryCellsSsc *e; + e = &g_array_index (array, QmiMessageNasGetLteCphyCaInfoOutputPhyCaAggSecondaryCellsSsc, i); + g_print ("Secondary Cell %u Info\n" + "\tPhysical Cell ID: '%" G_GUINT16_FORMAT"'\n" + "\tRX Channel: '%" G_GUINT16_FORMAT"'\n" + "\tDL Bandwidth: '%s'\n" + "\tLTE Band: '%s'\n" + "\tState: '%s'\n" + "\tCell index: '%u'\n", + i + 1, e->physical_cell_id, e->rx_channel, + qmi_nas_dl_bandwidth_get_string (e->dl_bandwidth), + qmi_nas_active_band_get_string (e->lte_band), + qmi_nas_scell_state_get_string (e->state), + e->cell_index); + } + + } else { + if (qmi_message_nas_get_lte_cphy_ca_info_output_get_phy_ca_agg_scell_info ( + output, + &pci, + &channel, + &dl_bandwidth, + &band, + &state, + NULL)) { + g_print ("Secondary Cell Info\n"); + g_print ("\tPhysical Cell ID: '%" G_GUINT16_FORMAT"'\n" + "\tRX Channel: '%" G_GUINT16_FORMAT"'\n" + "\tDL Bandwidth: '%s'\n" + "\tLTE Band: '%s'\n" + "\tState: '%s'\n", + pci, channel, + qmi_nas_dl_bandwidth_get_string (dl_bandwidth), + qmi_nas_active_band_get_string (band), + qmi_nas_scell_state_get_string (state)); + } + + if (qmi_message_nas_get_lte_cphy_ca_info_output_get_scell_index ( + output, + &scell_index, + NULL)) { + g_print ("Secondary Cell index: '%u'\n", scell_index); + } + } + + qmi_message_nas_get_lte_cphy_ca_info_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_LTE_CPHY_CA_INFO */ + +#if defined HAVE_QMI_MESSAGE_NAS_GET_RF_BAND_INFORMATION + +static void +get_rf_band_info_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasGetRfBandInformationOutput *output; + GError *error = NULL; + GArray *band_array = NULL; + GArray *extended_band_array = NULL; + GArray *bandwidth_array = NULL; + guint i; + + output = qmi_client_nas_get_rf_band_information_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_nas_get_rf_band_information_output_get_result (output, &error)) { + g_printerr ("error: couldn't get rf band info: %s\n", error->message); + g_error_free (error); + qmi_message_nas_get_rf_band_information_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got RF band info\n", + qmi_device_get_path_display (ctx->device)); + + if (!qmi_message_nas_get_rf_band_information_output_get_list ( + output, + &band_array, + &error)) { + g_printerr ("error: couldn't get rf band list: %s\n", error->message); + g_error_free (error); + qmi_message_nas_get_rf_band_information_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("Band Information:\n"); + for (i = 0; band_array && i < band_array->len; i++) { + QmiMessageNasGetRfBandInformationOutputListElement *info; + + info = &g_array_index (band_array, QmiMessageNasGetRfBandInformationOutputListElement, i); + g_print ("\tRadio Interface: '%s'\n" + "\tActive Band Class: '%s'\n" + "\tActive Channel: '%" G_GUINT16_FORMAT"'\n", + qmi_nas_radio_interface_get_string (info->radio_interface), + qmi_nas_active_band_get_string (info->active_band_class), + info->active_channel); + } + + if (qmi_message_nas_get_rf_band_information_output_get_extended_list ( + output, + &extended_band_array, + NULL)) { + g_print ("Band Information (Extended):\n"); + for (i = 0; extended_band_array && i < extended_band_array->len; i++) { + QmiMessageNasGetRfBandInformationOutputExtendedListElement *info; + + info = &g_array_index (extended_band_array, QmiMessageNasGetRfBandInformationOutputExtendedListElement, i); + g_print ("\tRadio Interface: '%s'\n" + "\tActive Band Class: '%s'\n" + "\tActive Channel: '%" G_GUINT32_FORMAT"'\n", + qmi_nas_radio_interface_get_string (info->radio_interface), + qmi_nas_active_band_get_string (info->active_band_class), + info->active_channel); + } + } + + if (qmi_message_nas_get_rf_band_information_output_get_bandwidth_list ( + output, + &bandwidth_array, + NULL)) { + g_print ("Bandwidth:\n"); + for (i = 0; bandwidth_array && i < bandwidth_array->len; i++) { + QmiMessageNasGetRfBandInformationOutputBandwidthListElement *info; + info = &g_array_index (bandwidth_array, QmiMessageNasGetRfBandInformationOutputBandwidthListElement, i); + g_print ("\tRadio Interface: '%s'\n" + "\tBandwidth: '%s'\n", + qmi_nas_radio_interface_get_string (info->radio_interface), + qmi_nas_dl_bandwidth_get_string (info->bandwidth)); + } + } + + qmi_message_nas_get_rf_band_information_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_RF_BAND_INFORMATION */ + +#if defined HAVE_QMI_MESSAGE_NAS_GET_DRX + +static void +get_drx_ready (QmiClientNas *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageNasGetDrxOutput) output = NULL; + g_autoptr(GError) error = NULL; + QmiNasDrx drx = QMI_NAS_DRX_UNKNOWN; + + output = qmi_client_nas_get_drx_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_nas_get_drx_output_get_result (output, &error)) { + g_printerr ("error: couldn't get DRX: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_nas_get_drx_output_get_info (output, &drx, NULL)) { + g_printerr ("error: DRX info not provided\n"); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got DRX: %s\n", + qmi_device_get_path_display (ctx->device), + qmi_nas_drx_get_string (drx)); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_DRX */ + +#if defined HAVE_QMI_MESSAGE_NAS_GET_SUPPORTED_MESSAGES + +static void +get_supported_messages_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasGetSupportedMessagesOutput *output; + GError *error = NULL; + GArray *bytearray = NULL; + gchar *str; + + output = qmi_client_nas_get_supported_messages_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_nas_get_supported_messages_output_get_result (output, &error)) { + g_printerr ("error: couldn't get supported NAS messages: %s\n", error->message); + g_error_free (error); + qmi_message_nas_get_supported_messages_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got supported NAS messages:\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_nas_get_supported_messages_output_get_list (output, &bytearray, NULL); + str = qmicli_get_supported_messages_list (bytearray ? (const guint8 *)bytearray->data : NULL, + bytearray ? bytearray->len : 0); + g_print ("%s", str); + g_free (str); + + qmi_message_nas_get_supported_messages_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_GET_SUPPORTED_MESSAGES */ + +#if defined HAVE_QMI_MESSAGE_NAS_SWI_GET_STATUS + +static void +swi_get_status_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasSwiGetStatusOutput *output; + GError *error = NULL; + + gint8 temperature; + QmiNasSwiModemMode modem_mode; + QmiNasSwiSystemMode system_mode; + QmiNasSwiImsRegState ims_reg_state; + QmiNasSwiPsState ps_state; + + QmiNasActiveBand band; + QmiNasDLBandwidth bandwidth; + guint16 rx_channel; + guint16 tx_channel; + QmiNasSwiEmmState emm_state; + guint8 emm_sub_state; + QmiNasSwiEmmConnectionState emm_conn_state; + + output = qmi_client_nas_swi_get_status_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_nas_swi_get_status_output_get_result (output, &error)) { + g_printerr ("error: couldn't get status: %s\n", error->message); + g_error_free (error); + qmi_message_nas_swi_get_status_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got status:\n", + qmi_device_get_path_display (ctx->device)); + + if (qmi_message_nas_swi_get_status_output_get_common_info_v2 ( + output, + &temperature, + &modem_mode, + &system_mode, + &ims_reg_state, + &ps_state, + NULL)) { + g_print ("Common Info:\n" + "\tTemperature: '%d'\n" + "\tModem mode: '%s'\n" + "\tSystem mode: '%s'\n" + "\tIMS registration state: '%s'\n" + "\tPacket service state: '%s'\n", + temperature, + qmi_nas_swi_modem_mode_get_string (modem_mode), + qmi_nas_swi_system_mode_get_string (system_mode), + qmi_nas_swi_ims_reg_state_get_string (ims_reg_state), + qmi_nas_swi_ps_state_get_string (ps_state)); + } + + if (qmi_message_nas_swi_get_status_output_get_lte_info ( + output, + &band, + &bandwidth, + &rx_channel, + &tx_channel, + &emm_state, + &emm_sub_state, + &emm_conn_state, + NULL)) { + g_print ("LTE info:\n" + "\tBand: '%s'\n" + "\tBandwidth: '%s'\n" + "\tRX channel: '%" G_GUINT16_FORMAT"'\n" + "\tTX channel: '%" G_GUINT16_FORMAT"'\n" + "\tEMM state: '%s'\n" + "\tEMM sub state: '%u'\n" + "\tEMM connection state: '%s'\n", + qmi_nas_active_band_get_string (band), + qmi_nas_dl_bandwidth_get_string (bandwidth), + rx_channel, + tx_channel, + qmi_nas_swi_emm_state_get_string (emm_state), + emm_sub_state, + qmi_nas_swi_emm_connection_state_get_string (emm_conn_state)); + } + + qmi_message_nas_swi_get_status_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_SWI_GET_STATUS */ + +#if defined HAVE_QMI_MESSAGE_NAS_RESET + +static void +reset_ready (QmiClientNas *client, + GAsyncResult *res) +{ + QmiMessageNasResetOutput *output; + GError *error = NULL; + + output = qmi_client_nas_reset_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_nas_reset_output_get_result (output, &error)) { + g_printerr ("error: couldn't reset the NAS service: %s\n", error->message); + g_error_free (error); + qmi_message_nas_reset_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully performed NAS service reset\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_nas_reset_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_NAS_RESET */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_nas_run (QmiDevice *device, + QmiClientNas *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_NAS_GET_SIGNAL_STRENGTH + if (get_signal_strength_flag) { + QmiMessageNasGetSignalStrengthInput *input; + + input = get_signal_strength_input_create (); + + g_debug ("Asynchronously getting signal strength..."); + qmi_client_nas_get_signal_strength (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_signal_strength_ready, + NULL); + qmi_message_nas_get_signal_strength_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_GET_SIGNAL_INFO + if (get_signal_info_flag) { + g_debug ("Asynchronously getting signal info..."); + qmi_client_nas_get_signal_info (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_signal_info_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_GET_TX_RX_INFO + if (get_tx_rx_info_str) { + QmiMessageNasGetTxRxInfoInput *input; + QmiNasRadioInterface interface; + + input = get_tx_rx_info_input_create (get_tx_rx_info_str, + &interface); + if (!input) { + operation_shutdown (FALSE); + return; + } + + g_debug ("Asynchronously getting TX/RX info..."); + qmi_client_nas_get_tx_rx_info (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_tx_rx_info_ready, + GUINT_TO_POINTER (interface)); + qmi_message_nas_get_tx_rx_info_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_GET_HOME_NETWORK + if (get_home_network_flag) { + g_debug ("Asynchronously getting home network..."); + qmi_client_nas_get_home_network (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_home_network_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_GET_PREFERRED_NETWORKS + if (get_preferred_networks_flag) { + g_debug ("Asynchronously getting preferred networks..."); + qmi_client_nas_get_preferred_networks (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_preferred_networks_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_SET_PREFERRED_NETWORKS + if (set_preferred_networks_str) { + QmiMessageNasSetPreferredNetworksInput *input; + g_debug ("Asynchronously setting preferred networks..."); + + input = set_preferred_networks_input_create (set_preferred_networks_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_nas_set_preferred_networks (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_preferred_networks_ready, + NULL); + qmi_message_nas_set_preferred_networks_input_ref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_GET_SERVING_SYSTEM + if (get_serving_system_flag) { + g_debug ("Asynchronously getting serving system..."); + qmi_client_nas_get_serving_system (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_serving_system_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_GET_SYSTEM_INFO + if (get_system_info_flag) { + g_debug ("Asynchronously getting system info..."); + qmi_client_nas_get_system_info (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_system_info_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_GET_TECHNOLOGY_PREFERENCE + if (get_technology_preference_flag) { + g_debug ("Asynchronously getting technology preference..."); + qmi_client_nas_get_technology_preference (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_technology_preference_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_GET_SYSTEM_SELECTION_PREFERENCE + if (get_system_selection_preference_flag) { + g_debug ("Asynchronously getting system selection preference..."); + qmi_client_nas_get_system_selection_preference (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_system_selection_preference_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_SET_SYSTEM_SELECTION_PREFERENCE + if (set_system_selection_preference_str) { + QmiMessageNasSetSystemSelectionPreferenceInput *input; + g_debug ("Asynchronously setting system selection preference..."); + + input = set_system_selection_preference_input_create (set_system_selection_preference_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_nas_set_system_selection_preference (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_system_selection_preference_ready, + NULL); + qmi_message_nas_set_system_selection_preference_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_NETWORK_SCAN + if (network_scan_flag) { + g_debug ("Asynchronously scanning networks..."); + qmi_client_nas_network_scan (ctx->client, + NULL, + 300, /* this operation takes a lot of time! */ + ctx->cancellable, + (GAsyncReadyCallback)network_scan_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_GET_CELL_LOCATION_INFO + if (get_cell_location_info_flag) { + g_debug ("Asynchronously getting cell location info ..."); + qmi_client_nas_get_cell_location_info (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_cell_location_info_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_FORCE_NETWORK_SEARCH + if (force_network_search_flag) { + g_debug ("Forcing network search..."); + qmi_client_nas_force_network_search (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)force_network_search_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_GET_OPERATOR_NAME + if (get_operator_name_flag) { + g_debug ("Asynchronously getting operator name data..."); + qmi_client_nas_get_operator_name (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_operator_name_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_GET_PLMN_NAME + if (get_plmn_name_str) { + + g_autoptr(QmiMessageNasGetPlmnNameInput) input = NULL; + input = set_plmn_name_input_plmn_create(get_plmn_name_str); + + g_debug ("Asynchronously getting plmn name data..."); + qmi_client_nas_get_plmn_name (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_plmn_name_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_GET_LTE_CPHY_CA_INFO + if (get_lte_cphy_ca_info_flag) { + g_debug ("Asynchronously getting carrier aggregation info ..."); + qmi_client_nas_get_lte_cphy_ca_info (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_lte_cphy_ca_info_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_GET_RF_BAND_INFORMATION + if (get_rf_band_info_flag) { + g_debug ("Asynchronously getting RF band info ..."); + qmi_client_nas_get_rf_band_information (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_rf_band_info_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_GET_DRX + if (get_drx_flag) { + g_debug ("Asynchronously getting DRX ..."); + qmi_client_nas_get_drx (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_drx_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_GET_SUPPORTED_MESSAGES + if (get_supported_messages_flag) { + g_debug ("Asynchronously getting supported NAS messages..."); + qmi_client_nas_get_supported_messages (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_supported_messages_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_SWI_GET_STATUS + if (swi_get_status_flag) { + g_debug ("Asynchronously getting status (Sierra Wireless specific)..."); + qmi_client_nas_swi_get_status (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)swi_get_status_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_NAS_RESET + if (reset_flag) { + g_debug ("Asynchronously resetting NAS service..."); + qmi_client_nas_reset (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)reset_ready, + NULL); + return; + } +#endif + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_NAS */ diff --git a/pkgs/qmi-nmea/qmicli-pbm.c b/pkgs/qmi-nmea/qmicli-pbm.c new file mode 100644 index 0000000..27f3199 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-pbm.c @@ -0,0 +1,349 @@ +/* -*- 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-2017 Aleksander Morgado + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" + +#if defined HAVE_QMI_SERVICE_PBM + +#undef VALIDATE_MASK_NONE +#define VALIDATE_MASK_NONE(str) (str ? str : "none") + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientPbm *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gboolean get_all_capabilities_flag; +static gboolean noop_flag; + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_PBM_GET_ALL_CAPABILITIES + { "pbm-get-all-capabilities", 0, 0, G_OPTION_ARG_NONE, &get_all_capabilities_flag, + "Get all phonebook capabilities", + NULL + }, +#endif + { "pbm-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a PBM client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_pbm_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("pbm", + "PBM options:", + "Show Phonebook Management options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_pbm_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (get_all_capabilities_flag + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many PBM actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->client) + g_object_unref (context->client); + 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); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_PBM_GET_ALL_CAPABILITIES + +static void +get_all_capabilities_ready (QmiClientPbm *client, + GAsyncResult *res) +{ + GError *error = NULL; + QmiMessagePbmGetAllCapabilitiesOutput *output; + GArray *capability_basic_information = NULL; + GArray *group_capability = NULL; + GArray *additional_number_capability = NULL; + GArray *email_capability = NULL; + GArray *second_name_capability = NULL; + GArray *hidden_records_capability = NULL; + GArray *grouping_information_alpha_string_capability = NULL; + GArray *additional_number_alpha_string_capability = NULL; + guint i, j; + + output = qmi_client_pbm_get_all_capabilities_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_pbm_get_all_capabilities_output_get_result (output, &error)) { + g_printerr ("error: couldn't get capabilities: %s\n", error->message); + g_error_free (error); + qmi_message_pbm_get_all_capabilities_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_pbm_get_all_capabilities_output_get_capability_basic_information (output, &capability_basic_information, NULL); + qmi_message_pbm_get_all_capabilities_output_get_group_capability (output, &group_capability, NULL); + qmi_message_pbm_get_all_capabilities_output_get_additional_number_capability (output, &additional_number_capability, NULL); + qmi_message_pbm_get_all_capabilities_output_get_email_capability (output, &email_capability, NULL); + qmi_message_pbm_get_all_capabilities_output_get_second_name_capability (output, &second_name_capability, NULL); + qmi_message_pbm_get_all_capabilities_output_get_hidden_records_capability (output, &hidden_records_capability, NULL); + qmi_message_pbm_get_all_capabilities_output_get_grouping_information_alpha_string_capability (output, &grouping_information_alpha_string_capability, NULL); + qmi_message_pbm_get_all_capabilities_output_get_additional_number_alpha_string_capability (output, &additional_number_alpha_string_capability, NULL); + + g_print ("[%s] Phonebook capabilities:%s\n", + qmi_device_get_path_display (ctx->device), + (capability_basic_information || + group_capability || + additional_number_capability || + email_capability || + second_name_capability || + hidden_records_capability || + grouping_information_alpha_string_capability || + additional_number_alpha_string_capability) ? "" : " none"); + + if (capability_basic_information) { + g_print ("Capability basic information:\n"); + for (i = 0; i < capability_basic_information->len; i++) { + QmiMessagePbmGetAllCapabilitiesOutputCapabilityBasicInformationElement *session; + + session = &g_array_index (capability_basic_information, + QmiMessagePbmGetAllCapabilitiesOutputCapabilityBasicInformationElement, + i); + g_print ("\t[%s]:\n", qmi_pbm_session_type_get_string (session->session_type)); + for (j = 0; j < session->phonebooks->len; j++) { + QmiMessagePbmGetAllCapabilitiesOutputCapabilityBasicInformationElementPhonebooksElement *phonebook; + g_autofree gchar *phonebook_type_str = NULL; + + phonebook = &g_array_index (session->phonebooks, + QmiMessagePbmGetAllCapabilitiesOutputCapabilityBasicInformationElementPhonebooksElement, + j); + phonebook_type_str = qmi_pbm_phonebook_type_build_string_from_mask (phonebook->phonebook_type); + g_print ("\t\t[%s]:\n", VALIDATE_MASK_NONE (phonebook_type_str)); + g_print ("\t\t\tUsed records: %" G_GUINT16_FORMAT "\n", phonebook->used_records); + g_print ("\t\t\tMaximum records: %" G_GUINT16_FORMAT "\n", phonebook->maximum_records); + g_print ("\t\t\tMaximum number length: %u\n", phonebook->maximum_number_length); + g_print ("\t\t\tMaximum name length: %u\n", phonebook->maximum_name_length); + } + } + } + + if (group_capability) { + g_print ("Group capability:\n"); + for (i = 0; i < group_capability->len; i++) { + QmiMessagePbmGetAllCapabilitiesOutputGroupCapabilityElement *session; + + session = &g_array_index (group_capability, + QmiMessagePbmGetAllCapabilitiesOutputGroupCapabilityElement, + i); + g_print ("\t[%s]:\n", qmi_pbm_session_type_get_string (session->session_type)); + g_print ("\t\tMaximum groups: %u\n", session->maximum_groups); + g_print ("\t\tMaximum group tag length: %u\n", session->maximum_group_tag_length); + } + } + + if (additional_number_capability) { + g_print ("Additional number capability:\n"); + for (i = 0; i < additional_number_capability->len; i++) { + QmiMessagePbmGetAllCapabilitiesOutputAdditionalNumberCapabilityElement *session; + + session = &g_array_index (additional_number_capability, + QmiMessagePbmGetAllCapabilitiesOutputAdditionalNumberCapabilityElement, + i); + g_print ("\t[%s]:\n", qmi_pbm_session_type_get_string (session->session_type)); + g_print ("\t\tMaximum additional numbers: %u\n", session->maximum_additional_numbers); + g_print ("\t\tMaximum additional number length: %u\n", session->maximum_additional_number_length); + g_print ("\t\tMaximum additional number tag length: %u\n", session->maximum_additional_number_tag_length); + } + } + + if (email_capability) { + g_print ("Email capability:\n"); + for (i = 0; i < email_capability->len; i++) { + QmiMessagePbmGetAllCapabilitiesOutputEmailCapabilityElement *session; + + session = &g_array_index (email_capability, + QmiMessagePbmGetAllCapabilitiesOutputEmailCapabilityElement, + i); + g_print ("\t[%s]:\n", qmi_pbm_session_type_get_string (session->session_type)); + g_print ("\t\tMaximum emails: %u\n", session->maximum_emails); + g_print ("\t\tMaximum email address length: %u\n", session->maximum_email_address_length); + } + } + + if (second_name_capability) { + g_print ("Second name capability:\n"); + for (i = 0; i < second_name_capability->len; i++) { + QmiMessagePbmGetAllCapabilitiesOutputSecondNameCapabilityElement *session; + + session = &g_array_index (second_name_capability, + QmiMessagePbmGetAllCapabilitiesOutputSecondNameCapabilityElement, + i); + g_print ("\t[%s]:\n", qmi_pbm_session_type_get_string (session->session_type)); + g_print ("\t\tMaximum second name length: %u\n", session->maximum_second_name_length); + } + } + + if (hidden_records_capability) { + g_print ("Hidden records capability:\n"); + for (i = 0; i < hidden_records_capability->len; i++) { + QmiMessagePbmGetAllCapabilitiesOutputHiddenRecordsCapabilityElement *session; + + session = &g_array_index (hidden_records_capability, + QmiMessagePbmGetAllCapabilitiesOutputHiddenRecordsCapabilityElement, + i); + g_print ("\t[%s]:\n", qmi_pbm_session_type_get_string (session->session_type)); + g_print ("\t\tSupported: %s\n", session->supported ? "yes" : "no"); + } + } + + if (grouping_information_alpha_string_capability) { + g_print ("Alpha string capability:\n"); + for (i = 0; i < grouping_information_alpha_string_capability->len; i++) { + QmiMessagePbmGetAllCapabilitiesOutputGroupingInformationAlphaStringCapabilityElement *session; + + session = &g_array_index (grouping_information_alpha_string_capability, + QmiMessagePbmGetAllCapabilitiesOutputGroupingInformationAlphaStringCapabilityElement, + i); + g_print ("\t[%s]:\n", qmi_pbm_session_type_get_string (session->session_type)); + g_print ("\t\tMaximum records: %u\n", session->maximum_records); + g_print ("\t\tUsed records: %u\n", session->used_records); + g_print ("\t\tMaximum string length: %u\n", session->maximum_string_length); + } + } + + if (additional_number_alpha_string_capability) { + g_print ("Additional number alpha string capability:\n"); + for (i = 0; i < additional_number_alpha_string_capability->len; i++) { + QmiMessagePbmGetAllCapabilitiesOutputAdditionalNumberAlphaStringCapabilityElement *session; + + session = &g_array_index (additional_number_alpha_string_capability, + QmiMessagePbmGetAllCapabilitiesOutputAdditionalNumberAlphaStringCapabilityElement, + i); + g_print ("\t[%s]:\n", qmi_pbm_session_type_get_string (session->session_type)); + g_print ("\t\tMaximum records: %u\n", session->maximum_records); + g_print ("\t\tUsed records: %u\n", session->used_records); + g_print ("\t\tMaximum string length: %u\n", session->maximum_string_length); + } + } + + qmi_message_pbm_get_all_capabilities_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_PBM_GET_ALL_CAPABILITIES */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_pbm_run (QmiDevice *device, + QmiClientPbm *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_PBM_GET_ALL_CAPABILITIES + if (get_all_capabilities_flag) { + g_debug ("Asynchronously getting phonebook capabilities..."); + qmi_client_pbm_get_all_capabilities (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_all_capabilities_ready, + NULL); + return; + } +#endif + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_PBM */ diff --git a/pkgs/qmi-nmea/qmicli-pdc.c b/pkgs/qmi-nmea/qmicli-pdc.c new file mode 100644 index 0000000..4e7a19c --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-pdc.c @@ -0,0 +1,1475 @@ +/* -*- 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-2022 Aleksander Morgado + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#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, + ¤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); + 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 */ diff --git a/pkgs/qmi-nmea/qmicli-qmiwwan.c b/pkgs/qmi-nmea/qmicli-qmiwwan.c new file mode 100644 index 0000000..4e66d8d --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-qmiwwan.c @@ -0,0 +1,193 @@ +/* -*- 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) 2021 Aleksander Morgado + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +/* Options */ +static gboolean get_wwan_iface_flag; +static gboolean get_expected_data_format_flag; +static gchar *set_expected_data_format_str; + +static GOptionEntry entries[] = { + { "get-wwan-iface", 'w', 0, G_OPTION_ARG_NONE, &get_wwan_iface_flag, + "Get the associated WWAN iface name", + NULL + }, + { "get-expected-data-format", 'e', 0, G_OPTION_ARG_NONE, &get_expected_data_format_flag, + "Get the expected data format in the WWAN iface", + NULL + }, + { "set-expected-data-format", 'E', 0, G_OPTION_ARG_STRING, &set_expected_data_format_str, + "Set the expected data format in the WWAN iface", + "[802-3|raw-ip|qmap-pass-through]" + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_qmiwwan_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("qmiwwan", + "qmi_wwan specific options:", + "Show qmi_wwan driver specific options", NULL, NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_qmiwwan_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (get_wwan_iface_flag + + get_expected_data_format_flag + + !!set_expected_data_format_str); + + if (n_actions > 1) { + g_printerr ("error: too many qmi_wwan specific actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +/******************************************************************************/ + +static gboolean +device_set_expected_data_format_cb (QmiDevice *dev) +{ + QmiDeviceExpectedDataFormat expected; + GError *error = NULL; + + if (!qmicli_read_device_expected_data_format_from_string (set_expected_data_format_str, &expected) || + expected == QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN) + g_printerr ("error: invalid requested data format: %s", set_expected_data_format_str); + else if (!qmi_device_set_expected_data_format (dev, expected, &error)) { + g_printerr ("error: cannot set expected data format: %s\n", error->message); + g_error_free (error); + } else + g_print ("[%s] expected data format set to: %s\n", + qmi_device_get_path_display (dev), + qmi_device_expected_data_format_get_string (expected)); + + /* We're done now */ + qmicli_async_operation_done (!error, FALSE); + + g_object_unref (dev); + return FALSE; +} + +static void +device_set_expected_data_format (QmiDevice *dev) +{ + g_debug ("Setting expected WWAN data format this control port..."); + g_idle_add ((GSourceFunc) device_set_expected_data_format_cb, g_object_ref (dev)); +} + +static gboolean +device_get_expected_data_format_cb (QmiDevice *dev) +{ + QmiDeviceExpectedDataFormat expected; + GError *error = NULL; + + expected = qmi_device_get_expected_data_format (dev, &error); + if (expected == QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN) { + g_printerr ("error: cannot get expected data format: %s\n", error->message); + g_error_free (error); + } else + g_print ("%s\n", qmi_device_expected_data_format_get_string (expected)); + + /* We're done now */ + qmicli_async_operation_done (!error, FALSE); + + g_object_unref (dev); + return FALSE; +} + +static void +device_get_expected_data_format (QmiDevice *dev) +{ + g_debug ("Getting expected WWAN data format this control port..."); + g_idle_add ((GSourceFunc) device_get_expected_data_format_cb, g_object_ref (dev)); +} + +static gboolean +device_get_wwan_iface_cb (QmiDevice *dev) +{ + const gchar *wwan_iface; + + wwan_iface = qmi_device_get_wwan_iface (dev); + if (!wwan_iface) + g_printerr ("error: cannot get WWAN interface name\n"); + else + g_print ("%s\n", wwan_iface); + + /* We're done now */ + qmicli_async_operation_done (!!wwan_iface, FALSE); + + g_object_unref (dev); + return FALSE; +} + +static void +device_get_wwan_iface (QmiDevice *dev) +{ + g_debug ("Getting WWAN iface for this control port..."); + g_idle_add ((GSourceFunc) device_get_wwan_iface_cb, g_object_ref (dev)); +} + +/******************************************************************************/ +/* Common */ + +void +qmicli_qmiwwan_run (QmiDevice *device, + GCancellable *cancellable) +{ + if (get_wwan_iface_flag) + device_get_wwan_iface (device); + else if (get_expected_data_format_flag) + device_get_expected_data_format (device); + else if (set_expected_data_format_str) + device_set_expected_data_format (device); + else + g_warn_if_reached (); +} diff --git a/pkgs/qmi-nmea/qmicli-qos.c b/pkgs/qmi-nmea/qmicli-qos.c new file mode 100644 index 0000000..ede7c79 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-qos.c @@ -0,0 +1,430 @@ +/* -*- 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) 2018 Aleksander Morgado + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_QOS + +#undef VALIDATE_UNKNOWN +#define VALIDATE_UNKNOWN(str) (str ? str : "unknown") + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientQos *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gint get_flow_status_int = -1; +static gboolean get_network_status_flag; +static gint swi_read_data_stats_int = -1; +static gboolean reset_flag; +static gboolean noop_flag; + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_QOS_GET_FLOW_STATUS + { "qos-get-flow-status", 0, 0, G_OPTION_ARG_INT, &get_flow_status_int, + "Get QoS flow status", + "[QoS ID]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_QOS_GET_NETWORK_STATUS + { "qos-get-network-status", 0, 0, G_OPTION_ARG_NONE, &get_network_status_flag, + "Gets the network status", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_QOS_SWI_READ_DATA_STATS + { "qos-swi-read-data-stats", 0, 0, G_OPTION_ARG_INT, &swi_read_data_stats_int, + "Read data stats (Sierra Wireless specific)", + "[APN ID]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_QOS_RESET + { "qos-reset", 0, 0, G_OPTION_ARG_NONE, &reset_flag, + "Reset the service state", + NULL + }, +#endif + { "qos-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a QOS client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_qos_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("qos", + "QoS options:", + "Show Quality of Service options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_qos_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = ((get_flow_status_int >= 0) + + get_network_status_flag + + (swi_read_data_stats_int >= 0) + + reset_flag + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many QoS actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->cancellable) + g_object_unref (context->cancellable); + if (context->device) + g_object_unref (context->device); + if (context->client) + g_object_unref (context->client); + g_slice_free (Context, context); +} + +static void +operation_shutdown (gboolean operation_status) +{ + /* Cleanup context and finish async operation */ + context_free (ctx); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_QOS_GET_FLOW_STATUS + +static void +get_flow_status_ready (QmiClientQos *client, + GAsyncResult *res) +{ + QmiMessageQosGetFlowStatusOutput *output; + GError *error = NULL; + QmiQosStatus flow_status; + + output = qmi_client_qos_get_flow_status_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_qos_get_flow_status_output_get_result (output, &error)) { + g_printerr ("error: couldn't get QoS flow status: %s\n", error->message); + g_error_free (error); + qmi_message_qos_get_flow_status_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_qos_get_flow_status_output_get_value (output, &flow_status, NULL); + + g_print ("[%s] QoS flow status: %s\n", + qmi_device_get_path_display (ctx->device), + qmi_qos_status_get_string (flow_status)); + + qmi_message_qos_get_flow_status_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_QOS_GET_FLOW_STATUS */ + +#if defined HAVE_QMI_MESSAGE_QOS_GET_NETWORK_STATUS + +static void +get_network_status_ready (QmiClientQos *client, + GAsyncResult *res) +{ + QmiMessageQosGetNetworkStatusOutput *output; + GError *error = NULL; + gboolean qos_supported; + + output = qmi_client_qos_get_network_status_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_qos_get_network_status_output_get_result (output, &error)) { + g_printerr ("error: couldn't get network status: %s\n", error->message); + g_error_free (error); + qmi_message_qos_get_network_status_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_qos_get_network_status_output_get_qos_supported (output, &qos_supported, NULL); + + g_print ("[%s] QoS %ssupported in network\n", + qmi_device_get_path_display (ctx->device), + qos_supported ? "" : "not "); + + qmi_message_qos_get_network_status_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_QOS_GET_NETWORK_STATUS */ + +#if defined HAVE_QMI_MESSAGE_QOS_SWI_READ_DATA_STATS + +static void +swi_read_data_stats_ready (QmiClientQos *client, + GAsyncResult *res) +{ + QmiMessageQosSwiReadDataStatsOutput *output; + GError *error = NULL; + guint32 apn_id = 0; + guint32 apn_tx_packets = 0; + guint32 apn_tx_packets_dropped = 0; + guint32 apn_rx_packets = 0; + guint64 apn_tx_bytes = 0; + guint64 apn_tx_bytes_dropped = 0; + guint64 apn_rx_bytes = 0; + GArray *flow = NULL; + + output = qmi_client_qos_swi_read_data_stats_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_qos_swi_read_data_stats_output_get_result (output, &error)) { + g_printerr ("error: couldn't read data stats: %s\n", error->message); + g_error_free (error); + qmi_message_qos_swi_read_data_stats_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] QoS data stats read\n", + qmi_device_get_path_display (ctx->device)); + + if (qmi_message_qos_swi_read_data_stats_output_get_apn ( + output, + &apn_id, + &apn_tx_packets, + &apn_tx_packets_dropped, + &apn_rx_packets, + &apn_tx_bytes, + &apn_tx_bytes_dropped, + &apn_rx_bytes, + NULL)) { + g_print (" APN ID: %u\n", apn_id); + g_print (" TX packets: %u\n", apn_tx_packets); + g_print (" TX packets dropped: %u\n", apn_tx_packets_dropped); + g_print (" RX packets: %u\n", apn_rx_packets); + g_print (" TX bytes: %" G_GUINT64_FORMAT "\n", apn_tx_bytes); + g_print (" TX bytes dropped: %" G_GUINT64_FORMAT "\n", apn_tx_bytes_dropped); + g_print (" RX bytes: %" G_GUINT64_FORMAT "\n", apn_rx_bytes); + } + + if (qmi_message_qos_swi_read_data_stats_output_get_flow ( + output, + &flow, + NULL)) { + guint i; + + for (i = 0; i < flow->len; i++) { + QmiMessageQosSwiReadDataStatsOutputFlowElement *element; + + element = &g_array_index(flow, QmiMessageQosSwiReadDataStatsOutputFlowElement, i); + + g_print (" Flow %u\n", i); + g_print (" Bearer ID: %u\n", element->bearer_id); + g_print (" TX packets: %u\n", element->tx_packets); + g_print (" TX packets dropped: %u\n", element->tx_packets_dropped); + g_print (" TX bytes: %" G_GUINT64_FORMAT "\n", element->tx_bytes); + g_print (" TX bytes dropped: %" G_GUINT64_FORMAT "\n", element->tx_bytes_dropped); + } + } + + qmi_message_qos_swi_read_data_stats_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_QOS_SWI_READ_DATA_STATS */ + +#if defined HAVE_QMI_MESSAGE_QOS_RESET + +static void +reset_ready (QmiClientQos *client, + GAsyncResult *res) +{ + QmiMessageQosResetOutput *output; + GError *error = NULL; + + output = qmi_client_qos_reset_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_qos_reset_output_get_result (output, &error)) { + g_printerr ("error: couldn't reset the QoS service: %s\n", error->message); + g_error_free (error); + qmi_message_qos_reset_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully performed QoS service reset\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_qos_reset_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_QOS_RESET */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_qos_run (QmiDevice *device, + QmiClientQos *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_QOS_GET_FLOW_STATUS + if (get_flow_status_int >= 0) { + QmiMessageQosGetFlowStatusInput *input; + + input = qmi_message_qos_get_flow_status_input_new (); + qmi_message_qos_get_flow_status_input_set_qos_id (input, get_flow_status_int, NULL); + g_debug ("Asynchronously getting QoS flow status..."); + qmi_client_qos_get_flow_status (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_flow_status_ready, + NULL); + qmi_message_qos_get_flow_status_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_QOS_GET_NETWORK_STATUS + if (get_network_status_flag) { + g_debug ("Asynchronously getting network status..."); + qmi_client_qos_get_network_status (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_network_status_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_QOS_SWI_READ_DATA_STATS + if (swi_read_data_stats_int >= 0) { + QmiMessageQosSwiReadDataStatsInput *input; + + input = qmi_message_qos_swi_read_data_stats_input_new (); + qmi_message_qos_swi_read_data_stats_input_set_apn_id (input, swi_read_data_stats_int, NULL); + g_debug ("Asynchronously reading data stats..."); + qmi_client_qos_swi_read_data_stats (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)swi_read_data_stats_ready, + NULL); + qmi_message_qos_swi_read_data_stats_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_QOS_RESET + if (reset_flag) { + g_debug ("Asynchronously resetting QoS service..."); + qmi_client_qos_reset (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)reset_ready, + NULL); + return; + } +#endif + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_QOS */ diff --git a/pkgs/qmi-nmea/qmicli-sar.c b/pkgs/qmi-nmea/qmicli-sar.c new file mode 100644 index 0000000..2b5498e --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-sar.c @@ -0,0 +1,295 @@ +/* -*- 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) 2020 Google Inc. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_SAR + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientSar *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gchar *rf_set_state_str; +static gboolean rf_get_state_flag; +static gboolean noop_flag; + +#undef VALIDATE_UNKNOWN +#define VALIDATE_UNKNOWN(str) (str ? str : "unknown") + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_SAR_RF_GET_STATE + { "sar-rf-get-state", 0, 0, G_OPTION_ARG_NONE, &rf_get_state_flag, + "Get RF state", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_SAR_RF_SET_STATE + { "sar-rf-set-state", 0, 0, G_OPTION_ARG_STRING, &rf_set_state_str, + "Set RF state.", + "[(state number)]" + }, +#endif + { "sar-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a SAR client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_sar_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("sar", + "SAR options:", + "Show Specific Absorption Rate options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_sar_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (!!rf_set_state_str + + rf_get_state_flag + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many SAR actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->client) + g_object_unref (context->client); + 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); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_SAR_RF_GET_STATE + +static void +rf_get_state_ready (QmiClientSar *client, + GAsyncResult *res) +{ + QmiMessageSarRfGetStateOutput *output; + GError *error = NULL; + QmiSarRfState rf_state; + + output = qmi_client_sar_rf_get_state_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_sar_rf_get_state_output_get_result (output, &error)) { + g_printerr ("error: couldn't get SAR RF state: %s\n", error->message); + g_error_free (error); + qmi_message_sar_rf_get_state_output_unref (output); + operation_shutdown (FALSE); + return; + } + + + qmi_message_sar_rf_get_state_output_get_state (output, &rf_state, NULL); + g_print ("[%s] Successfully got SAR RF state: %s\n", + qmi_device_get_path_display (ctx->device), + qmi_sar_rf_state_get_string (rf_state)); + + qmi_message_sar_rf_get_state_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_SAR_RF_GET_STATE */ + +#if defined HAVE_QMI_MESSAGE_SAR_RF_SET_STATE + +static QmiMessageSarRfSetStateInput * +rf_set_state_input_create (const gchar *str) +{ + QmiMessageSarRfSetStateInput *input = NULL; + QmiSarRfState rf_state; + + if (qmicli_read_sar_rf_state_from_string (str, &rf_state)) { + GError *error = NULL; + + input = qmi_message_sar_rf_set_state_input_new (); + if (!qmi_message_sar_rf_set_state_input_set_state ( + input, + rf_state, + &error)) { + g_printerr ("error: couldn't create input data: '%s'\n", + error->message); + g_error_free (error); + qmi_message_sar_rf_set_state_input_unref (input); + input = NULL; + } + } + + return input; +} + +static void +rf_set_state_ready (QmiClientSar *client, + GAsyncResult *res) +{ + QmiMessageSarRfSetStateOutput *output; + GError *error = NULL; + + output = qmi_client_sar_rf_set_state_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_sar_rf_set_state_output_get_result (output, &error)) { + g_printerr ("error: couldn't set RF state: %s\n", error->message); + g_error_free (error); + + qmi_message_sar_rf_set_state_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] RF state set successfully\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_sar_rf_set_state_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_SAR_RF_SET_STATE */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_sar_run (QmiDevice *device, + QmiClientSar *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new0 (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_SAR_RF_GET_STATE + + if (rf_get_state_flag) { + g_debug ("Asynchronously getting RF power state..."); + qmi_client_sar_rf_get_state (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback) rf_get_state_ready, + NULL); + return; + } + +#endif + +#if defined HAVE_QMI_MESSAGE_SAR_RF_SET_STATE + + if (rf_set_state_str) { + QmiMessageSarRfSetStateInput *input; + + g_debug ("Asynchronously setting RF power state..."); + input = rf_set_state_input_create (rf_set_state_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_sar_rf_set_state (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) rf_set_state_ready, + NULL); + qmi_message_sar_rf_set_state_input_unref (input); + return; + } + +#endif /* HAVE_QMI_MESSAGE_SAR_RF_SET_STATE */ + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_SAR */ diff --git a/pkgs/qmi-nmea/qmicli-uim.c b/pkgs/qmi-nmea/qmicli-uim.c new file mode 100644 index 0000000..2238c55 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-uim.c @@ -0,0 +1,3452 @@ +/* -*- 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) 2012-2017 Aleksander Morgado + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_UIM + +#undef VALIDATE_MASK_NONE +#define VALIDATE_MASK_NONE(str) (str ? str : "none") + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientUim *client; + GCancellable *cancellable; + + /* For Slot Status indication */ + guint slot_status_indication_id; + guint refresh_indication_id; +} Context; +static Context *ctx; + +/* Options */ +static gchar *read_transparent_str; +static gchar *read_record_str; +static gchar *set_pin_protection_str; +static gchar *verify_pin_str; +static gchar *unblock_pin_str; +static gchar *change_pin_str; +static gchar *get_file_attributes_str; +static gchar *sim_power_on_str; +static gchar *sim_power_off_str; +static gchar *change_provisioning_session_str; +static gchar *switch_slot_str; +static gchar *depersonalization_str; +static gchar *remote_unlock_str; +static gchar *open_logical_channel_str; +static gchar *close_logical_channel_str; +static gchar *send_apdu_str; +static gchar **monitor_refresh_file_array; +static gboolean get_card_status_flag; +static gboolean get_supported_messages_flag; +static gboolean get_slot_status_flag; +static gboolean monitor_slot_status_flag; +static gboolean reset_flag; +static gboolean monitor_refresh_all_flag; +static gboolean noop_flag; +static gboolean get_configuration_flag; + +#undef VALIDATE_UNKNOWN +#define VALIDATE_UNKNOWN(str) (str ? str : "unknown") + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_UIM_SET_PIN_PROTECTION + { "uim-set-pin-protection", 0, 0, G_OPTION_ARG_STRING, &set_pin_protection_str, + "Set PIN protection (allowed keys: session-type ((primary|secondary|tertiary|quarternary|quinary)-gw-provisioning|card-slot-[1-5]))", + "[(PIN1|PIN2|UPIN),(disable|enable),(current PIN)[,\"key=value,...\"]]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_VERIFY_PIN + { "uim-verify-pin", 0, 0, G_OPTION_ARG_STRING, &verify_pin_str, + "Verify PIN (allowed keys: session-type ((primary|secondary|tertiary|quarternary|quinary)-gw-provisioning|card-slot-[1-5]))", + "[(PIN1|PIN2|UPIN),(current PIN)[,\"key=value,...\"]]", + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_UNBLOCK_PIN + { "uim-unblock-pin", 0, 0, G_OPTION_ARG_STRING, &unblock_pin_str, + "Unblock PIN (allowed keys: session-type ((primary|secondary|tertiary|quarternary|quinary)-gw-provisioning|card-slot-[1-5]))", + "[(PIN1|PIN2|UPIN),(PUK),(new PIN)[,\"key=value,...\"]]", + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_CHANGE_PIN + { "uim-change-pin", 0, 0, G_OPTION_ARG_STRING, &change_pin_str, + "Change PIN (allowed keys: session-type ((primary|secondary|tertiary|quarternary|quinary)-gw-provisioning|card-slot-[1-5]))", + "[(PIN1|PIN2|UPIN),(old PIN),(new PIN)[,\"key=value,...\"]]", + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_READ_TRANSPARENT + { "uim-read-transparent", 0, 0, G_OPTION_ARG_STRING, &read_transparent_str, + "Read a transparent file given the file path", + "[0xNNNN,0xNNNN,...]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_GET_FILE_ATTRIBUTES + { "uim-get-file-attributes", 0, 0, G_OPTION_ARG_STRING, &get_file_attributes_str, + "Get the attributes of a given file", + "[0xNNNN,0xNNNN,...]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_READ_RECORD + { "uim-read-record", 0, 0, G_OPTION_ARG_STRING, &read_record_str, + "Read a record from given file (allowed keys: record-number, record-length, file ([0xNNNN-0xNNNN,...])", + "[\"key=value,...\"]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_GET_CARD_STATUS + { "uim-get-card-status", 0, 0, G_OPTION_ARG_NONE, &get_card_status_flag, + "Get card status", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_GET_SUPPORTED_MESSAGES + { "uim-get-supported-messages", 0, 0, G_OPTION_ARG_NONE, &get_supported_messages_flag, + "Get supported messages", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_POWER_ON_SIM + { "uim-sim-power-on", 0, 0, G_OPTION_ARG_STRING, &sim_power_on_str, + "Power on SIM card", + "[(slot number)]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_POWER_OFF_SIM + { "uim-sim-power-off", 0, 0, G_OPTION_ARG_STRING, &sim_power_off_str, + "Power off SIM card", + "[(slot number)]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_CHANGE_PROVISIONING_SESSION + { "uim-change-provisioning-session", 0, 0, G_OPTION_ARG_STRING, &change_provisioning_session_str, + "Change provisioning session (allowed keys: session-type ((primary|secondary|tertiary|quarternary|quinary)-gw-provisioning), activate (yes|no), slot, aid)", + "[\"key=value,...\"]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS + { "uim-get-slot-status", 0, 0, G_OPTION_ARG_NONE, &get_slot_status_flag, + "Get slot status", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_SWITCH_SLOT && defined HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS + { "uim-switch-slot", 0, 0, G_OPTION_ARG_STRING, &switch_slot_str, + "Switch active physical slot", + "[(slot number)]" + }, +#endif +#if defined HAVE_QMI_INDICATION_UIM_SLOT_STATUS + { "uim-monitor-slot-status", 0, 0, G_OPTION_ARG_NONE, &monitor_slot_status_flag, + "Watch for slot status indications", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_RESET + { "uim-reset", 0, 0, G_OPTION_ARG_NONE, &reset_flag, + "Reset the service state", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER + { "uim-monitor-refresh-file", 0, 0, G_OPTION_ARG_STRING_ARRAY, &monitor_refresh_file_array, + "Watch for REFRESH events for given file paths", + "[0xNNNN,0xNNNN,...]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER_ALL + { "uim-monitor-refresh-all", 0, 0, G_OPTION_ARG_NONE, &monitor_refresh_all_flag, + "Watch for REFRESH events for any file", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_GET_CONFIGURATION + { "uim-get-configuration", 0, 0, G_OPTION_ARG_NONE, &get_configuration_flag, + "Get personalization status of the modem", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_DEPERSONALIZATION + { "uim-depersonalization", 0, 0, G_OPTION_ARG_STRING, &depersonalization_str, + "Deactivates or unblocks personalization feature", + "[(feature),(operation),(control key)[,(slot number)]]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_REMOTE_UNLOCK + { "uim-remote-unlock", 0, 0, G_OPTION_ARG_STRING, &remote_unlock_str, + "Updates the SimLock configuration data", + "[XX:XX:...]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_OPEN_LOGICAL_CHANNEL + { "uim-open-logical-channel", 0, 0, G_OPTION_ARG_STRING, &open_logical_channel_str, + "Open logical channel", + "[(slot number),(aid)]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_LOGICAL_CHANNEL + { "uim-close-logical-channel", 0, 0, G_OPTION_ARG_STRING, &close_logical_channel_str, + "Close logical channel", + "[(slot number),(channel ID)]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_UIM_SEND_APDU + { "uim-send-apdu", 0, 0, G_OPTION_ARG_STRING, &send_apdu_str, + "Send APDU", + "[(slot number),(channel ID),(apdu)]" + }, +#endif + { "uim-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a UIM client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_uim_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("uim", + "UIM options:", + "Show User Identity Module options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_uim_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (!!set_pin_protection_str + + !!verify_pin_str + + !!unblock_pin_str + + !!change_pin_str + + !!read_transparent_str + + !!read_record_str + + !!get_file_attributes_str + + !!sim_power_on_str + + !!sim_power_off_str + + !!change_provisioning_session_str + + !!switch_slot_str + + !!monitor_refresh_file_array + + !!depersonalization_str + + !!remote_unlock_str + + !!open_logical_channel_str + + !!close_logical_channel_str + + !!send_apdu_str + + get_card_status_flag + + get_supported_messages_flag + + get_slot_status_flag + + monitor_slot_status_flag + + reset_flag + + monitor_refresh_all_flag + + get_configuration_flag + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many UIM actions requested\n"); + exit (EXIT_FAILURE); + } + + if (monitor_slot_status_flag || monitor_refresh_file_array || monitor_refresh_all_flag) + qmicli_expect_indications (); + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->slot_status_indication_id) + g_signal_handler_disconnect (context->client, + context->slot_status_indication_id); + if (context->refresh_indication_id) + g_signal_handler_disconnect (context->client, + context->refresh_indication_id); + + if (context->client) + g_object_unref (context->client); + 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); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_UIM_SET_PIN_PROTECTION || \ + defined HAVE_QMI_MESSAGE_UIM_VERIFY_PIN || \ + defined HAVE_QMI_MESSAGE_UIM_UNBLOCK_PIN ||\ + defined HAVE_QMI_MESSAGE_UIM_CHANGE_PIN + +static gboolean +provisioning_session_type_handle (const gchar *key, + const gchar *value, + GError **error, + gpointer user_data) +{ + QmiUimSessionType *session_type = (QmiUimSessionType *) user_data; + + if (!value || !value[0]) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "key '%s' requires a value", key); + return FALSE; + } + + if (g_ascii_strcasecmp (key, "session-type") == 0) { + if (!qmicli_read_uim_session_type_from_string (value, session_type)) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "invalid session type value: %s (not a valid enum)", value); + return FALSE; + } + return TRUE; + } + + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "Unrecognized option '%s'", key); + return FALSE; +} + +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_SET_PIN_PROTECTION + +static QmiMessageUimSetPinProtectionInput * +set_pin_protection_input_create (const gchar *str) +{ + QmiMessageUimSetPinProtectionInput *input = NULL; + QmiUimSessionType session_type = QMI_UIM_SESSION_TYPE_CARD_SLOT_1; + gchar **split; + guint len_split; + GError *error = NULL; + QmiUimPinId pin_id; + gboolean enable_disable; + gchar *current_pin; + + /* Prepare inputs. + * Format of the string is: + * "[(PIN1|PIN2|UPIN),(disable|enable),(current PIN)[,'key=value,...']]" with valid key = (session-type) + */ + split = g_strsplit (str, ",", 4); + len_split = g_strv_length (split); + + /* Parse optional kv-pairs */ + if (len_split >= 4) { + if (!qmicli_parse_key_value_string (split[3], &error, provisioning_session_type_handle, &session_type)) { + g_printerr ("error: could not parse input string '%s': %s\n", str, error->message); + } + } + + if (error == NULL && + qmicli_read_uim_pin_id_from_string (split[0], &pin_id) && + qmicli_read_enable_disable_from_string (split[1], &enable_disable) && + qmicli_read_non_empty_string (split[2], "current PIN", ¤t_pin)) { + GArray *placeholder_aid; + + placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8)); + + input = qmi_message_uim_set_pin_protection_input_new (); + if (!qmi_message_uim_set_pin_protection_input_set_info ( + input, + pin_id, + enable_disable, + current_pin, + &error) || + !qmi_message_uim_set_pin_protection_input_set_session ( + input, + session_type, + placeholder_aid, /* ignored */ + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + qmi_message_uim_set_pin_protection_input_unref (input); + input = NULL; + } + g_array_unref (placeholder_aid); + } + g_strfreev (split); + g_clear_error (&error); + + return input; +} + +static void +set_pin_protection_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimSetPinProtectionOutput *output; + GError *error = NULL; + + output = qmi_client_uim_set_pin_protection_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_uim_set_pin_protection_output_get_result (output, &error)) { + guint8 verify_retries_left; + guint8 unblock_retries_left; + + g_printerr ("error: couldn't set PIN protection: %s\n", error->message); + g_error_free (error); + + if (qmi_message_uim_set_pin_protection_output_get_retries_remaining ( + output, + &verify_retries_left, + &unblock_retries_left, + NULL)) { + g_printerr ("[%s] Retries left:\n" + "\tVerify: %u\n" + "\tUnblock: %u\n", + qmi_device_get_path_display (ctx->device), + verify_retries_left, + unblock_retries_left); + } + + qmi_message_uim_set_pin_protection_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] PIN protection updated\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_uim_set_pin_protection_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_SET_PIN_PROTECTION */ + +#if defined HAVE_QMI_MESSAGE_UIM_VERIFY_PIN + +static QmiMessageUimVerifyPinInput * +verify_pin_input_create (const gchar *str) +{ + QmiMessageUimVerifyPinInput *input = NULL; + QmiUimSessionType session_type = QMI_UIM_SESSION_TYPE_CARD_SLOT_1; + gchar **split; + guint len_split; + GError *error = NULL; + QmiUimPinId pin_id; + gchar *current_pin; + + /* Prepare inputs. + * Format of the string is: + * "[(PIN1|PIN2),(current PIN)[,'key=value,...']]" with valid key = (session-type) + */ + split = g_strsplit (str, ",", 3); + len_split = g_strv_length (split); + + /* Parse optional kv-pairs */ + if (len_split >= 3) { + if (!qmicli_parse_key_value_string (split[2], &error, provisioning_session_type_handle, &session_type)) { + g_printerr ("error: could not parse input string '%s': %s\n", str, error->message); + } + } + + if (error == NULL && + qmicli_read_uim_pin_id_from_string (split[0], &pin_id) && + qmicli_read_non_empty_string (split[1], "current PIN", ¤t_pin)) { + GArray *placeholder_aid; + + placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8)); + + input = qmi_message_uim_verify_pin_input_new (); + if (!qmi_message_uim_verify_pin_input_set_info ( + input, + pin_id, + current_pin, + &error) || + !qmi_message_uim_verify_pin_input_set_session ( + input, + session_type, + placeholder_aid, /* ignored */ + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + qmi_message_uim_verify_pin_input_unref (input); + input = NULL; + } + g_array_unref (placeholder_aid); + } + g_strfreev (split); + g_clear_error (&error); + + return input; +} + +static void +verify_pin_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimVerifyPinOutput *output; + GError *error = NULL; + + output = qmi_client_uim_verify_pin_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_uim_verify_pin_output_get_result (output, &error)) { + guint8 verify_retries_left; + guint8 unblock_retries_left; + + g_printerr ("error: couldn't verify PIN: %s\n", error->message); + g_error_free (error); + + if (qmi_message_uim_verify_pin_output_get_retries_remaining ( + output, + &verify_retries_left, + &unblock_retries_left, + NULL)) { + g_printerr ("[%s] Retries left:\n" + "\tVerify: %u\n" + "\tUnblock: %u\n", + qmi_device_get_path_display (ctx->device), + verify_retries_left, + unblock_retries_left); + } + + qmi_message_uim_verify_pin_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] PIN verified successfully\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_uim_verify_pin_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_VERIFY_PIN */ + +#if defined HAVE_QMI_MESSAGE_UIM_UNBLOCK_PIN + +static QmiMessageUimUnblockPinInput * +unblock_pin_input_create (const gchar *str) +{ + QmiMessageUimUnblockPinInput *input = NULL; + QmiUimSessionType session_type = QMI_UIM_SESSION_TYPE_CARD_SLOT_1; + gchar **split; + guint len_split; + GError *error = NULL; + QmiUimPinId pin_id; + gchar *puk; + gchar *new_pin; + + /* Prepare inputs. + * Format of the string is: + * "[(PIN|PIN2),(PUK),(new PIN)[,'key=value,...']]" with valid key = (session-type) + */ + split = g_strsplit (str, ",", 4); + len_split = g_strv_length (split); + + /* Parse optional kv-pairs */ + if (len_split >= 4) { + if (!qmicli_parse_key_value_string (split[3], &error, provisioning_session_type_handle, &session_type)) { + g_printerr ("error: could not parse input string '%s': %s\n", str, error->message); + } + } + + if (error == NULL && + qmicli_read_uim_pin_id_from_string (split[0], &pin_id) && + qmicli_read_non_empty_string (split[1], "PUK", &puk) && + qmicli_read_non_empty_string (split[2], "new PIN", &new_pin)) { + GArray *placeholder_aid; + + placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8)); + + input = qmi_message_uim_unblock_pin_input_new (); + if (!qmi_message_uim_unblock_pin_input_set_info ( + input, + pin_id, + puk, + new_pin, + &error) || + !qmi_message_uim_unblock_pin_input_set_session ( + input, + session_type, + placeholder_aid, /* ignored */ + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_uim_unblock_pin_input_unref (input); + input = NULL; + } + g_array_unref (placeholder_aid); + } + g_strfreev (split); + g_clear_error (&error); + + return input; +} + +static void +unblock_pin_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimUnblockPinOutput *output; + GError *error = NULL; + + output = qmi_client_uim_unblock_pin_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_uim_unblock_pin_output_get_result (output, &error)) { + guint8 verify_retries_left; + guint8 unblock_retries_left; + + g_printerr ("error: couldn't unblock PIN: %s\n", error->message); + g_error_free (error); + + if (qmi_message_uim_unblock_pin_output_get_retries_remaining ( + output, + &verify_retries_left, + &unblock_retries_left, + NULL)) { + g_printerr ("[%s] Retries left:\n" + "\tVerify: %u\n" + "\tUnblock: %u\n", + qmi_device_get_path_display (ctx->device), + verify_retries_left, + unblock_retries_left); + } + + qmi_message_uim_unblock_pin_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] PIN unblocked successfully\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_uim_unblock_pin_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_UNBLOCK_PIN */ + +#if defined HAVE_QMI_MESSAGE_UIM_CHANGE_PIN + +static QmiMessageUimChangePinInput * +change_pin_input_create (const gchar *str) +{ + QmiMessageUimChangePinInput *input = NULL; + QmiUimSessionType session_type = QMI_UIM_SESSION_TYPE_CARD_SLOT_1; + gchar **split; + guint len_split; + GError *error = NULL; + QmiUimPinId pin_id; + gchar *old_pin; + gchar *new_pin; + + /* Prepare inputs. + * Format of the string is: + * "[(PIN1|PIN2),(old PIN),(new PIN)[,'key=value,...']]" with valid key = (session-type) + */ + split = g_strsplit (str, ",", 4); + len_split = g_strv_length (split); + + /* Parse optional kv-pairs */ + if (len_split >= 4) { + if (!qmicli_parse_key_value_string (split[3], &error, provisioning_session_type_handle, &session_type)) { + g_printerr ("error: could not parse input string '%s': %s\n", str, error->message); + } + } + + if (error == NULL && + qmicli_read_uim_pin_id_from_string (split[0], &pin_id) && + qmicli_read_non_empty_string (split[1], "old PIN", &old_pin) && + qmicli_read_non_empty_string (split[2], "new PIN", &new_pin)) { + GArray *placeholder_aid; + + placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8)); + + input = qmi_message_uim_change_pin_input_new (); + if (!qmi_message_uim_change_pin_input_set_info ( + input, + pin_id, + old_pin, + new_pin, + &error) || + !qmi_message_uim_change_pin_input_set_session ( + input, + session_type, + placeholder_aid, /* ignored */ + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + qmi_message_uim_change_pin_input_unref (input); + input = NULL; + } + g_array_unref (placeholder_aid); + } + g_strfreev (split); + g_clear_error (&error); + + return input; +} + +static void +change_pin_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimChangePinOutput *output; + GError *error = NULL; + + output = qmi_client_uim_change_pin_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_uim_change_pin_output_get_result (output, &error)) { + guint8 verify_retries_left; + guint8 unblock_retries_left; + + g_printerr ("error: couldn't change PIN: %s\n", error->message); + g_error_free (error); + + if (qmi_message_uim_change_pin_output_get_retries_remaining ( + output, + &verify_retries_left, + &unblock_retries_left, + NULL)) { + g_printerr ("[%s] Retries left:\n" + "\tVerify: %u\n" + "\tUnblock: %u\n", + qmi_device_get_path_display (ctx->device), + verify_retries_left, + unblock_retries_left); + } + + qmi_message_uim_change_pin_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] PIN changed successfully\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_uim_change_pin_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_CHANGE_PIN */ + +#if defined HAVE_QMI_MESSAGE_UIM_GET_SUPPORTED_MESSAGES + +static void +get_supported_messages_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimGetSupportedMessagesOutput *output; + GError *error = NULL; + GArray *bytearray = NULL; + gchar *str; + + output = qmi_client_uim_get_supported_messages_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_uim_get_supported_messages_output_get_result (output, &error)) { + g_printerr ("error: couldn't get supported UIM messages: %s\n", error->message); + g_error_free (error); + qmi_message_uim_get_supported_messages_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got supported UIM messages:\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_uim_get_supported_messages_output_get_list (output, &bytearray, NULL); + str = qmicli_get_supported_messages_list (bytearray ? (const guint8 *)bytearray->data : NULL, + bytearray ? bytearray->len : 0); + g_print ("%s", str); + g_free (str); + + qmi_message_uim_get_supported_messages_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_GET_SUPPORTED_MESSAGES */ + +#if defined HAVE_QMI_MESSAGE_UIM_POWER_ON_SIM + +static QmiMessageUimPowerOnSimInput * +power_on_sim_input_create (const gchar *slot_str) +{ + QmiMessageUimPowerOnSimInput *input; + guint slot; + GError *error = NULL; + + if (!qmicli_read_uint_from_string (slot_str, &slot) || (slot > G_MAXUINT8)) { + g_printerr ("error: invalid slot number\n"); + return NULL; + } + + input = qmi_message_uim_power_on_sim_input_new (); + + if (!qmi_message_uim_power_on_sim_input_set_slot (input, slot, &error)) { + g_printerr ("error: could not create SIM power on input: %s\n", error->message); + g_error_free (error); + qmi_message_uim_power_on_sim_input_unref (input); + input = NULL; + } + + return input; +} + +static void +power_on_sim_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimPowerOnSimOutput *output; + GError *error = NULL; + + output = qmi_client_uim_power_on_sim_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_uim_power_on_sim_output_get_result (output, &error)) { + g_printerr ("error: could not power on SIM: %s\n", error->message); + g_error_free (error); + qmi_message_uim_power_on_sim_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully performed SIM power on\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_uim_power_on_sim_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_POWER_ON_SIM */ + +#if defined HAVE_QMI_MESSAGE_UIM_POWER_OFF_SIM + +static QmiMessageUimPowerOffSimInput * +power_off_sim_input_create (const gchar *slot_str) +{ + QmiMessageUimPowerOffSimInput *input; + guint slot; + GError *error = NULL; + + if (!qmicli_read_uint_from_string (slot_str, &slot) || (slot > G_MAXUINT8)) { + g_printerr ("error: invalid slot number\n"); + return NULL; + } + + input = qmi_message_uim_power_off_sim_input_new (); + + if (!qmi_message_uim_power_off_sim_input_set_slot (input, slot, &error)) { + g_printerr ("error: could not create SIM power off input: %s\n", error->message); + g_error_free (error); + qmi_message_uim_power_off_sim_input_unref (input); + input = NULL; + } + + return input; +} + +static void +power_off_sim_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimPowerOffSimOutput *output; + GError *error = NULL; + + output = qmi_client_uim_power_off_sim_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_uim_power_off_sim_output_get_result (output, &error)) { + g_printerr ("error: could not power off SIM: %s\n", error->message); + g_error_free (error); + qmi_message_uim_power_off_sim_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully performed SIM power off\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_uim_power_off_sim_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_POWER_OFF_SIM */ + +#if defined HAVE_QMI_MESSAGE_UIM_CHANGE_PROVISIONING_SESSION + +typedef struct { + QmiUimSessionType session_type; + gboolean session_type_set; + gboolean activate; + gboolean activate_set; + guint slot; + GArray *aid; +} SetChangeProvisioningSessionProperties; + +static gboolean +set_change_provisioning_session_properties_handle (const gchar *key, + const gchar *value, + GError **error, + gpointer user_data) +{ + SetChangeProvisioningSessionProperties *props = (SetChangeProvisioningSessionProperties *) user_data; + + if (!value || !value[0]) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "key '%s' requires a value", key); + return FALSE; + } + + if (g_ascii_strcasecmp (key, "session-type") == 0) { + if (!qmicli_read_uim_session_type_from_string (value, &props->session_type)) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "invalid session type value: %s (not a valid enum)", value); + return FALSE; + } + props->session_type_set = TRUE; + return TRUE; + } + + if (g_ascii_strcasecmp (key, "activate") == 0) { + if (!qmicli_read_yes_no_from_string (value, &props->activate)) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "invalid activate value: %s (not a boolean)", value); + return FALSE; + } + props->activate_set = TRUE; + return TRUE; + } + + if (g_ascii_strcasecmp (key, "slot") == 0) { + if (!qmicli_read_uint_from_string (value, &props->slot)) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "invalid slot value: %s (not a number)", value); + return FALSE; + } + if (props->slot > G_MAXUINT8) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "invalid slot value: %s (out of range)", value); + return FALSE; + } + return TRUE; + } + + if (g_ascii_strcasecmp (key, "aid") == 0) { + if (!qmicli_read_raw_data_from_string (value, &props->aid)) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "invalid aid value: %s (not an hex string)", value); + return FALSE; + } + return TRUE; + } + + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "Unrecognized option '%s'", key); + return FALSE; +} + +static QmiMessageUimChangeProvisioningSessionInput * +change_provisioning_session_input_create (const gchar *str) +{ + QmiMessageUimChangeProvisioningSessionInput *input; + GError *error = NULL; + SetChangeProvisioningSessionProperties props = { 0 }; + + input = qmi_message_uim_change_provisioning_session_input_new (); + + if (!qmicli_parse_key_value_string (str, + &error, + set_change_provisioning_session_properties_handle, + &props)) { + g_printerr ("error: could not parse input string '%s': %s\n", str, error->message); + g_error_free (error); + g_clear_pointer (&input, qmi_message_uim_change_provisioning_session_input_unref); + goto out; + } + + if (!props.session_type_set || !props.activate_set) { + g_printerr ("error: mandatory fields 'session-type' and 'activate' not given\n"); + g_clear_pointer (&input, qmi_message_uim_change_provisioning_session_input_unref); + goto out; + } + + qmi_message_uim_change_provisioning_session_input_set_session_change ( + input, + props.session_type, + props.activate, + NULL); + + if (props.slot || props.aid) { + GArray *aid = NULL; + + aid = props.aid ? g_array_ref (props.aid) : g_array_new (FALSE, FALSE, sizeof (guint8)); + qmi_message_uim_change_provisioning_session_input_set_application_information ( + input, + props.slot, + aid, + NULL); + g_array_unref (aid); + } + +out: + return input; +} + +static void +change_provisioning_session_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimChangeProvisioningSessionOutput *output; + GError *error = NULL; + + output = qmi_client_uim_change_provisioning_session_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_uim_change_provisioning_session_output_get_result (output, &error)) { + g_printerr ("error: could not power off SIM: %s\n", error->message); + g_error_free (error); + qmi_message_uim_change_provisioning_session_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully changed provisioning session\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_uim_change_provisioning_session_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_CHANGE_PROVISIONING_SESSION */ + +#if (defined HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS || \ + defined HAVE_QMI_INDICATION_UIM_SLOT_STATUS) + +static const gchar bcd_chars[] = "0123456789\0\0\0\0\0\0"; + +static gchar * +decode_iccid (const gchar *bcd, gsize bcd_len) +{ + GString *str; + gsize i; + + if (!bcd) + return NULL; + + str = g_string_sized_new (bcd_len * 2 + 1); + for (i = 0; i < bcd_len; i++) { + str = g_string_append_c (str, bcd_chars[bcd[i] & 0xF]); + str = g_string_append_c (str, bcd_chars[(bcd[i] >> 4) & 0xF]); + } + return g_string_free (str, FALSE); +} + +#define EID_LENGTH 16 + +static gchar * +decode_eid (const gchar *eid, gsize eid_len) +{ + GString *str; + gsize i; + + if (!eid) + return NULL; + if (eid_len != EID_LENGTH) + return NULL; + + str = g_string_sized_new (eid_len * 2 + 1); + for (i = 0; i < eid_len; i++) { + str = g_string_append_c (str, bcd_chars[(eid[i] >> 4) & 0xF]); + str = g_string_append_c (str, bcd_chars[eid[i] & 0xF]); + } + return g_string_free (str, FALSE); +} + +static void +print_slot_status (GArray *physical_slots, + GArray *ext_information, + GArray *slot_eids) +{ + guint i; + + if (ext_information && physical_slots->len != ext_information->len) { + g_print ("Malformed extended information data"); + ext_information = NULL; + } + + if (slot_eids && physical_slots->len != slot_eids->len) { + g_print ("Malformed slot EID data"); + slot_eids = NULL; + } + + for (i = 0; i < physical_slots->len; i++) { + QmiPhysicalSlotStatusSlot *slot_status; + QmiPhysicalSlotInformationSlot *slot_info = NULL; + QmiSlotEidElement *slot_eid = NULL; + g_autofree gchar *iccid = NULL; + g_autofree gchar *eid = NULL; + + slot_status = &g_array_index (physical_slots, QmiPhysicalSlotStatusSlot, i); + + g_print (" Physical slot %u:\n", i + 1); + g_print (" Card status: %s\n", + qmi_uim_physical_card_state_get_string (slot_status->physical_card_status)); + g_print (" Slot status: %s\n", + qmi_uim_slot_state_get_string (slot_status->physical_slot_status)); + + if (slot_status->physical_slot_status == QMI_UIM_SLOT_STATE_ACTIVE) + g_print (" Logical slot: %u\n", slot_status->logical_slot); + + if (slot_status->physical_card_status != QMI_UIM_PHYSICAL_CARD_STATE_PRESENT) + continue; + + if (slot_status->iccid->len) + iccid = decode_iccid (slot_status->iccid->data, slot_status->iccid->len); + g_print (" ICCID: %s\n", VALIDATE_UNKNOWN (iccid)); + + /* Extended information, if available */ + if (!ext_information) + continue; + + slot_info = &g_array_index (ext_information, QmiPhysicalSlotInformationSlot, i); + g_print (" Protocol: %s\n", + qmi_uim_card_protocol_get_string (slot_info->card_protocol)); + g_print (" Num apps: %u\n", slot_info->valid_applications); + g_print (" Is eUICC: %s\n", slot_info->is_euicc ? "yes" : "no"); + + /* EID info, if available and this is an eUICC */ + if (!slot_info->is_euicc || !slot_eids) + continue; + + slot_eid = &g_array_index (slot_eids, QmiSlotEidElement, i); + if (slot_eid->eid) + eid = decode_eid (slot_eid->eid->data, slot_eid->eid->len); + g_print (" EID: %s\n", VALIDATE_UNKNOWN (eid)); + } +} + +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS + +static void +get_slot_status_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimGetSlotStatusOutput *output; + GArray *physical_slots; + GArray *ext_information = NULL; + GArray *slot_eids = NULL; + GError *error = NULL; + + output = qmi_client_uim_get_slot_status_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_uim_get_slot_status_output_get_result (output, &error)) { + g_printerr ("error: could not get slots status: %s\n", error->message); + g_error_free (error); + qmi_message_uim_get_slot_status_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got slots status\n", + qmi_device_get_path_display (ctx->device)); + + if (!qmi_message_uim_get_slot_status_output_get_physical_slot_status ( + output, &physical_slots, &error)) { + g_printerr ("error: could not parse slots status response: %s\n", error->message); + g_error_free (error); + qmi_message_uim_get_slot_status_output_unref (output); + operation_shutdown (FALSE); + return; + } + + /* Both of these are recoverable, just print less information per slot */ + qmi_message_uim_get_slot_status_output_get_physical_slot_information (output, &ext_information, NULL); + qmi_message_uim_get_slot_status_output_get_slot_eid (output, &slot_eids, NULL); + + g_print ("[%s] %u physical slots found:\n", + qmi_device_get_path_display (ctx->device), physical_slots->len); + + print_slot_status (physical_slots, ext_information, slot_eids); + + qmi_message_uim_get_slot_status_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS */ + +#if defined HAVE_QMI_MESSAGE_UIM_SWITCH_SLOT && defined HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS + +static void +switch_slot_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimSwitchSlotOutput *output; + GError *error = NULL; + + output = qmi_client_uim_switch_slot_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_uim_switch_slot_output_get_result (output, &error)) { + g_printerr ("error: couldn't switch slots: %s\n", error->message); + g_error_free (error); + qmi_message_uim_switch_slot_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully switched slots\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_uim_switch_slot_output_unref (output); + operation_shutdown (TRUE); +} + +static QmiMessageUimSwitchSlotInput * +switch_slot_input_create (guint logical_slot, + guint physical_slot) +{ + QmiMessageUimSwitchSlotInput *input; + GError *error = NULL; + + input = qmi_message_uim_switch_slot_input_new (); + + if (!qmi_message_uim_switch_slot_input_set_logical_slot (input, logical_slot, &error) || + !qmi_message_uim_switch_slot_input_set_physical_slot (input, physical_slot, &error)) { + g_printerr ("error: could not create switch slot input: %s\n", error->message); + g_error_free (error); + g_clear_pointer (&input, qmi_message_uim_switch_slot_input_unref); + } + + return input; +} + +static void +get_active_logical_slot_ready (QmiClientUim *client, + GAsyncResult *res, + gpointer user_data) +{ + QmiMessageUimGetSlotStatusOutput *output; + QmiMessageUimSwitchSlotInput *input; + GArray *physical_slots; + guint physical_slot_id; + guint active_logical_slot_id = 0; + guint i; + GError *error = NULL; + + physical_slot_id = GPOINTER_TO_UINT (user_data); + + output = qmi_client_uim_get_slot_status_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_uim_get_slot_status_output_get_result (output, &error)) { + g_printerr ("error: could not get slots status: %s\n", error->message); + g_error_free (error); + qmi_message_uim_get_slot_status_output_unref (output); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_uim_get_slot_status_output_get_physical_slot_status ( + output, &physical_slots, &error)) { + g_printerr ("error: could not parse slots status response: %s\n", error->message); + g_error_free (error); + qmi_message_uim_get_slot_status_output_unref (output); + operation_shutdown (FALSE); + return; + } + + /* Ensure the physical slot is available. */ + if (physical_slot_id > physical_slots->len) { + g_printerr ("error: physical slot %u is unavailable\n", physical_slot_id); + qmi_message_uim_get_slot_status_output_unref (output); + operation_shutdown (FALSE); + return; + } + + /* Find active logical slot */ + for (i = 0; i < physical_slots->len; i++) { + QmiPhysicalSlotStatusSlot *slot_status; + + slot_status = &g_array_index (physical_slots, QmiPhysicalSlotStatusSlot, i); + if (slot_status->physical_slot_status == QMI_UIM_SLOT_STATE_ACTIVE) { + active_logical_slot_id = slot_status->logical_slot; + break; + } + } + qmi_message_uim_get_slot_status_output_unref (output); + + if (active_logical_slot_id == 0) { + g_printerr ("error: no active logical slot\n"); + operation_shutdown (FALSE); + return; + } + + input = switch_slot_input_create (active_logical_slot_id, physical_slot_id); + qmi_client_uim_switch_slot (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)switch_slot_ready, + ctx); + qmi_message_uim_switch_slot_input_unref (input); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_SWITCH_SLOT && HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS */ + +#if defined HAVE_QMI_INDICATION_UIM_SLOT_STATUS + +static void +monitoring_cancelled (GCancellable *cancellable) +{ + operation_shutdown (TRUE); +} + +static void +slot_status_received (QmiClientUim *client, + QmiIndicationUimSlotStatusOutput *output) +{ + GArray *physical_slots; + GArray *ext_information = NULL; + GArray *slot_eids = NULL; + GError *error = NULL; + + g_print ("[%s] Received slot status indication:\n", + qmi_device_get_path_display (ctx->device)); + + if (!qmi_indication_uim_slot_status_output_get_physical_slot_status ( + output, &physical_slots, &error)) { + g_printerr ("error: could not parse slots status: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + /* Both of these are recoverable, just print less information per slot */ + qmi_indication_uim_slot_status_output_get_physical_slot_information (output, &ext_information, NULL); + qmi_indication_uim_slot_status_output_get_slot_eid (output, &slot_eids, NULL); + + print_slot_status (physical_slots, ext_information, slot_eids); +} + +static void +register_physical_slot_status_events_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimRegisterEventsOutput *output; + GError *error = NULL; + + output = qmi_client_uim_register_events_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_uim_register_events_output_get_result (output, &error)) { + g_printerr ("error: could not register slot status change events: %s\n", error->message); + qmi_message_uim_register_events_output_unref (output); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + g_debug ("Registered physical slot status change events..."); + ctx->slot_status_indication_id = + g_signal_connect (ctx->client, + "slot-status", + G_CALLBACK (slot_status_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_physical_slot_status_events (void) +{ + QmiMessageUimRegisterEventsInput *re_input; + + re_input = qmi_message_uim_register_events_input_new (); + qmi_message_uim_register_events_input_set_event_registration_mask ( + re_input, QMI_UIM_EVENT_REGISTRATION_FLAG_PHYSICAL_SLOT_STATUS, NULL); + qmi_client_uim_register_events ( + ctx->client, + re_input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) register_physical_slot_status_events_ready, + NULL); + qmi_message_uim_register_events_input_unref (re_input); +} + +#endif /* HAVE_QMI_INDICATION_UIM_SLOT_STATUS */ + +#if defined HAVE_QMI_MESSAGE_UIM_RESET + +static void +reset_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimResetOutput *output; + GError *error = NULL; + + output = qmi_client_uim_reset_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_uim_reset_output_get_result (output, &error)) { + g_printerr ("error: couldn't reset the UIM service: %s\n", error->message); + g_error_free (error); + qmi_message_uim_reset_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully performed UIM service reset\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_uim_reset_output_unref (output); + operation_shutdown (TRUE); +} + +#endif + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +#if defined HAVE_QMI_MESSAGE_UIM_GET_CARD_STATUS + +static void +get_card_status_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimGetCardStatusOutput *output; + GError *error = NULL; + guint16 index_gw_primary; + guint16 index_1x_primary; + guint16 index_gw_secondary; + guint16 index_1x_secondary; + GArray *cards; + guint i; + + output = qmi_client_uim_get_card_status_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_uim_get_card_status_output_get_result (output, &error)) { + g_printerr ("error: couldn't get card status: %s\n", error->message); + g_error_free (error); + qmi_message_uim_get_card_status_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got card status\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_uim_get_card_status_output_get_card_status ( + output, + &index_gw_primary, + &index_1x_primary, + &index_gw_secondary, + &index_1x_secondary, + &cards, + NULL); + + g_print ("Provisioning applications:\n"); + if (index_gw_primary == 0xFFFF) + g_print ("\tPrimary GW: session doesn't exist\n"); + else + g_print ("\tPrimary GW: slot '%u', application '%u'\n", + ((index_gw_primary & 0xFF00) >> 8) + 1, + ((index_gw_primary & 0x00FF)) + 1); + + if (index_1x_primary == 0xFFFF) + g_print ("\tPrimary 1X: session doesn't exist\n"); + else + g_print ("\tPrimary 1X: slot '%u', application '%u'\n", + ((index_1x_primary & 0xFF00) >> 8) + 1, + ((index_1x_primary & 0x00FF)) + 1); + + if (index_gw_secondary == 0xFFFF) + g_print ("\tSecondary GW: session doesn't exist\n"); + else + g_print ("\tSecondary GW: slot '%u', application '%u'\n", + ((index_gw_secondary & 0xFF00) >> 8) + 1, + ((index_gw_secondary & 0x00FF)) + 1); + + if (index_1x_secondary == 0xFFFF) + g_print ("\tSecondary 1X: session doesn't exist\n"); + else + g_print ("\tSecondary 1X: slot '%u', application '%u'\n", + ((index_1x_secondary & 0xFF00) >> 8) + 1, + ((index_1x_secondary & 0x00FF)) + 1); + + for (i = 0; i < cards->len; i++) { + QmiMessageUimGetCardStatusOutputCardStatusCardsElement *card; + guint j; + + card = &g_array_index (cards, QmiMessageUimGetCardStatusOutputCardStatusCardsElement, i); + + g_print ("Slot [%u]:\n", i + 1); + + if (card->card_state != QMI_UIM_CARD_STATE_ERROR) + g_print ("\tCard state: '%s'\n", + qmi_uim_card_state_get_string (card->card_state)); + else + g_print ("\tCard state: '%s: %s (%u)'\n", + qmi_uim_card_state_get_string (card->card_state), + VALIDATE_UNKNOWN (qmi_uim_card_error_get_string (card->error_code)), + card->error_code); + g_print ("\tUPIN state: '%s'\n" + "\t\tUPIN retries: '%u'\n" + "\t\tUPUK retries: '%u'\n", + qmi_uim_pin_state_get_string (card->upin_state), + card->upin_retries, + card->upuk_retries); + + for (j = 0; j < card->applications->len; j++) { + QmiMessageUimGetCardStatusOutputCardStatusCardsElementApplicationsElementV2 *app; + gchar *str; + + app = &g_array_index (card->applications, QmiMessageUimGetCardStatusOutputCardStatusCardsElementApplicationsElementV2, j); + + str = qmicli_get_raw_data_printable (app->application_identifier_value, 80, ""); + + g_print ("\tApplication [%u]:\n" + "\t\tApplication type: '%s (%u)'\n" + "\t\tApplication state: '%s'\n" + "\t\tApplication ID:\n" + "\t\t\t%s", + j + 1, + VALIDATE_UNKNOWN (qmi_uim_card_application_type_get_string (app->type)), app->type, + qmi_uim_card_application_state_get_string (app->state), + str); + + if (app->personalization_state == QMI_UIM_CARD_APPLICATION_PERSONALIZATION_STATE_CODE_REQUIRED || + app->personalization_state == QMI_UIM_CARD_APPLICATION_PERSONALIZATION_STATE_PUK_CODE_REQUIRED) + g_print ("\t\tPersonalization state: '%s' (feature: %s)\n" + "\t\t\tDisable retries: '%u'\n" + "\t\t\tUnblock retries: '%u'\n", + qmi_uim_card_application_personalization_state_get_string (app->personalization_state), + qmi_uim_card_application_personalization_feature_status_get_string (app->personalization_feature), + app->personalization_retries, + app->personalization_unblock_retries); + else + g_print ("\t\tPersonalization state: '%s'\n", + qmi_uim_card_application_personalization_state_get_string (app->personalization_state)); + + g_print ("\t\tUPIN replaces PIN1: '%s'\n", + app->upin_replaces_pin1 ? "yes" : "no"); + + g_print ("\t\tPIN1 state: '%s'\n" + "\t\t\tPIN1 retries: '%u'\n" + "\t\t\tPUK1 retries: '%u'\n" + "\t\tPIN2 state: '%s'\n" + "\t\t\tPIN2 retries: '%u'\n" + "\t\t\tPUK2 retries: '%u'\n", + qmi_uim_pin_state_get_string (app->pin1_state), + app->pin1_retries, + app->puk1_retries, + qmi_uim_pin_state_get_string (app->pin2_state), + app->pin2_retries, + app->puk2_retries); + g_free (str); + } + } + + qmi_message_uim_get_card_status_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* defined HAVE_QMI_MESSAGE_UIM_GET_CARD_STATUS */ + +#if defined HAVE_QMI_MESSAGE_UIM_READ_TRANSPARENT || \ + defined HAVE_QMI_MESSAGE_UIM_READ_RECORD || \ + defined HAVE_QMI_MESSAGE_UIM_GET_FILE_ATTRIBUTES + +static gboolean +get_sim_file_id_and_path_with_separator (const gchar *file_path_str, + guint16 *file_id, + GArray **file_path, + const gchar *separator) +{ + guint i; + gchar **split; + + split = g_strsplit (file_path_str, separator, -1); + if (!split) { + g_printerr ("error: invalid file path given: '%s'\n", file_path_str); + return FALSE; + } + + *file_path = g_array_sized_new (FALSE, + FALSE, + sizeof (guint8), + g_strv_length (split) - 1); + + *file_id = 0; + for (i = 0; split[i]; i++) { + gulong path_item; + + path_item = (strtoul (split[i], NULL, 16)) & 0xFFFF; + + /* If there are more fields, this is part of the path; otherwise it's + * the file id */ + if (split[i + 1]) { + guint8 val; + + val = path_item & 0xFF; + g_array_append_val (*file_path, val); + val = (path_item >> 8) & 0xFF; + g_array_append_val (*file_path, val); + } else { + *file_id = path_item; + } + } + + g_strfreev (split); + + if (*file_id == 0) { + g_array_unref (*file_path); + g_printerr ("error: invalid file path given: '%s'\n", file_path_str); + return FALSE; + } + + return TRUE; +} + +#endif /* HAVE_QMI_MESSAGE_UIM_READ_TRANSPARENT + * HAVE_QMI_MESSAGE_UIM_READ_RECORD + * HAVE_QMI_MESSAGE_UIM_GET_FILE_ATTRIBUTES */ + +#if defined HAVE_QMI_MESSAGE_UIM_READ_TRANSPARENT || \ + defined HAVE_QMI_MESSAGE_UIM_GET_FILE_ATTRIBUTES || \ + defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER + +static gboolean +get_sim_file_id_and_path (const gchar *file_path_str, + guint16 *file_id, + GArray **file_path) +{ + return get_sim_file_id_and_path_with_separator (file_path_str, file_id, file_path, ","); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_READ_TRANSPARENT + * HAVE_QMI_MESSAGE_UIM_GET_FILE_ATTRIBUTES + * HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER */ + +#if defined HAVE_QMI_MESSAGE_UIM_READ_TRANSPARENT + +static void +read_transparent_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimReadTransparentOutput *output; + GError *error = NULL; + guint8 sw1 = 0; + guint8 sw2 = 0; + GArray *read_result = NULL; + + output = qmi_client_uim_read_transparent_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_uim_read_transparent_output_get_result (output, &error)) { + g_printerr ("error: couldn't read transparent file from the UIM: %s\n", error->message); + g_error_free (error); + + /* Card result */ + if (qmi_message_uim_read_transparent_output_get_card_result ( + output, + &sw1, + &sw2, + NULL)) { + g_print ("Card result:\n" + "\tSW1: '0x%02x'\n" + "\tSW2: '0x%02x'\n", + sw1, sw2); + } + + qmi_message_uim_read_transparent_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully read information from the UIM:\n", + qmi_device_get_path_display (ctx->device)); + + /* Card result */ + if (qmi_message_uim_read_transparent_output_get_card_result ( + output, + &sw1, + &sw2, + NULL)) { + g_print ("Card result:\n" + "\tSW1: '0x%02x'\n" + "\tSW2: '0x%02x'\n", + sw1, sw2); + } + + /* Read result */ + if (qmi_message_uim_read_transparent_output_get_read_result ( + output, + &read_result, + NULL)) { + gchar *str; + + str = qmicli_get_raw_data_printable (read_result, 80, "\t"); + g_print ("Read result:\n" + "%s\n", + str); + g_free (str); + } + + qmi_message_uim_read_transparent_output_unref (output); + operation_shutdown (TRUE); +} + +static QmiMessageUimReadTransparentInput * +read_transparent_build_input (const gchar *file_path_str) +{ + QmiMessageUimReadTransparentInput *input; + guint16 file_id = 0; + GArray *file_path = NULL; + GArray *placeholder_aid; + + if (!get_sim_file_id_and_path (file_path_str, &file_id, &file_path)) + return NULL; + + placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8)); + + input = qmi_message_uim_read_transparent_input_new (); + qmi_message_uim_read_transparent_input_set_session ( + input, + QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING, + placeholder_aid, /* ignored */ + NULL); + qmi_message_uim_read_transparent_input_set_file ( + input, + file_id, + file_path, + NULL); + qmi_message_uim_read_transparent_input_set_read_information (input, 0, 0, NULL); + g_array_unref (file_path); + g_array_unref (placeholder_aid); + return input; +} + +#endif /* HAVE_QMI_MESSAGE_UIM_READ_TRANSPARENT */ + +#if defined HAVE_QMI_MESSAGE_UIM_READ_RECORD + +static void +read_record_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimReadRecordOutput *output; + GError *error = NULL; + guint8 sw1 = 0; + guint8 sw2 = 0; + GArray *read_result = NULL; + + output = qmi_client_uim_read_record_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_uim_read_record_output_get_result (output, &error)) { + g_printerr ("error: couldn't read record file from the UIM: %s\n", error->message); + g_error_free (error); + + /* Card result */ + if (qmi_message_uim_read_record_output_get_card_result ( + output, + &sw1, + &sw2, + NULL)) { + g_print ("Card result:\n" + "\tSW1: '0x%02x'\n" + "\tSW2: '0x%02x'\n", + sw1, sw2); + } + + qmi_message_uim_read_record_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully read information from the UIM:\n", + qmi_device_get_path_display (ctx->device)); + + /* Card result */ + if (qmi_message_uim_read_record_output_get_card_result ( + output, + &sw1, + &sw2, + NULL)) { + g_print ("Card result:\n" + "\tSW1: '0x%02x'\n" + "\tSW2: '0x%02x'\n", + sw1, sw2); + } + + /* Read result */ + if (qmi_message_uim_read_record_output_get_read_result ( + output, + &read_result, + NULL)) { + gchar *str; + + str = qmicli_get_raw_data_printable (read_result, 80, "\t"); + g_print ("Read result:\n" + "%s\n", + str); + g_free (str); + } + + qmi_message_uim_read_record_output_unref (output); + operation_shutdown (TRUE); +} + +typedef struct { + char *file; + guint16 record_number; + guint16 record_length; +} SetReadRecordProperties; + +static gboolean +set_read_record_properties_handle (const gchar *key, + const gchar *value, + GError **error, + gpointer user_data) +{ + SetReadRecordProperties *props = (SetReadRecordProperties *) user_data; + + if (!value || !value[0]) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "key '%s' requires a value", + key); + return FALSE; + } + + if (g_ascii_strcasecmp (key, "file") == 0) { + props->file = g_strdup (value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "record-number") == 0) { + guint aux; + + if (!qmicli_read_uint_from_string (value, &aux) || (aux > G_MAXUINT16)) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "failed reading key 'record-number' as 16bit value"); + return FALSE; + } + props->record_number = (guint16) aux; + return TRUE; + } + + if (g_ascii_strcasecmp (key, "record-length") == 0) { + guint aux; + + if (!qmicli_read_uint_from_string (value, &aux) || (aux > G_MAXUINT16)) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "failed reading key 'record-length' as 16bit value"); + return FALSE; + } + props->record_length = (guint16) aux; + return TRUE; + } + + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "Unrecognized option '%s'", + key); + return FALSE; +} + +static QmiMessageUimReadRecordInput * +read_record_input_create (const gchar *str) +{ + GError *error = NULL; + QmiMessageUimReadRecordInput *input = NULL; + SetReadRecordProperties props = { + .file = NULL, + .record_number = 0, + .record_length = 0, + }; + guint16 file_id = 0; + GArray *file_path = NULL; + GArray *placeholder_aid; + + if (!qmicli_parse_key_value_string (str, + &error, + set_read_record_properties_handle, + &props)) { + g_printerr ("error: could not parse input string '%s': %s\n", + str, + error->message); + g_error_free (error); + goto out; + } + + if (!get_sim_file_id_and_path_with_separator (props.file, &file_id, &file_path, "-")) + goto out; + + placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8)); + + input = qmi_message_uim_read_record_input_new (); + + qmi_message_uim_read_record_input_set_session ( + input, + QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING, + placeholder_aid, /* ignored */ + NULL); + qmi_message_uim_read_record_input_set_file ( + input, + file_id, + file_path, + NULL); + qmi_message_uim_read_record_input_set_record ( + input, + props.record_number, + props.record_length, + NULL); + + g_array_unref (placeholder_aid); + +out: + free (props.file); + g_array_unref (file_path); + return input; +} + +#endif /* HAVE_QMI_MESSAGE_UIM_READ_RECORD */ + +#if defined HAVE_QMI_MESSAGE_UIM_GET_FILE_ATTRIBUTES + +static void +get_file_attributes_ready (QmiClientUim *client, + GAsyncResult *res, + gchar *file_name) +{ + QmiMessageUimGetFileAttributesOutput *output; + GError *error = NULL; + guint8 sw1 = 0; + guint8 sw2 = 0; + guint16 file_size; + guint16 file_id; + QmiUimFileType file_type; + guint16 record_size; + guint16 record_count; + QmiUimSecurityAttributeLogic read_security_attributes_logic; + QmiUimSecurityAttribute read_security_attributes; + QmiUimSecurityAttributeLogic write_security_attributes_logic; + QmiUimSecurityAttribute write_security_attributes; + QmiUimSecurityAttributeLogic increase_security_attributes_logic; + QmiUimSecurityAttribute increase_security_attributes; + QmiUimSecurityAttributeLogic deactivate_security_attributes_logic; + QmiUimSecurityAttribute deactivate_security_attributes; + QmiUimSecurityAttributeLogic activate_security_attributes_logic; + QmiUimSecurityAttribute activate_security_attributes; + GArray *raw = NULL; + + output = qmi_client_uim_get_file_attributes_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + g_free (file_name); + return; + } + + if (!qmi_message_uim_get_file_attributes_output_get_result (output, &error)) { + g_printerr ("error: couldn't get '%s' file attributes from the UIM: %s\n", + file_name, + error->message); + g_error_free (error); + + /* Card result */ + if (qmi_message_uim_get_file_attributes_output_get_card_result ( + output, + &sw1, + &sw2, + NULL)) { + g_print ("Card result:\n" + "\tSW1: '0x%02x'\n" + "\tSW2: '0x%02x'\n", + sw1, sw2); + } + + qmi_message_uim_get_file_attributes_output_unref (output); + operation_shutdown (FALSE); + g_free (file_name); + return; + } + + g_print ("[%s] Successfully got file '%s' attributes from the UIM:\n", + file_name, + qmi_device_get_path_display (ctx->device)); + + /* Card result */ + if (qmi_message_uim_get_file_attributes_output_get_card_result ( + output, + &sw1, + &sw2, + NULL)) { + g_print ("Card result:\n" + "\tSW1: '0x%02x'\n" + "\tSW2: '0x%02x'\n", + sw1, sw2); + } + + /* File attributes */ + if (qmi_message_uim_get_file_attributes_output_get_file_attributes ( + output, + &file_size, + &file_id, + &file_type, + &record_size, + &record_count, + &read_security_attributes_logic, + &read_security_attributes, + &write_security_attributes_logic, + &write_security_attributes, + &increase_security_attributes_logic, + &increase_security_attributes, + &deactivate_security_attributes_logic, + &deactivate_security_attributes, + &activate_security_attributes_logic, + &activate_security_attributes, + &raw, + NULL)) { + g_autofree gchar *read_security_attributes_str = NULL; + g_autofree gchar *write_security_attributes_str = NULL; + g_autofree gchar *increase_security_attributes_str = NULL; + g_autofree gchar *deactivate_security_attributes_str = NULL; + g_autofree gchar *activate_security_attributes_str = NULL; + g_autofree gchar *raw_str = NULL; + + g_print ("File attributes:\n"); + g_print ("\tFile size: %u\n", (guint)file_size); + g_print ("\tFile ID: %u\n", (guint)file_id); + g_print ("\tFile type: %s\n", qmi_uim_file_type_get_string (file_type)); + g_print ("\tRecord size: %u\n", (guint)record_size); + g_print ("\tRecord count: %u\n", (guint)record_count); + + read_security_attributes_str = qmi_uim_security_attribute_build_string_from_mask (read_security_attributes); + g_print ("\tRead security attributes: (%s) %s\n", + qmi_uim_security_attribute_logic_get_string (read_security_attributes_logic), + VALIDATE_MASK_NONE (read_security_attributes_str)); + + write_security_attributes_str = qmi_uim_security_attribute_build_string_from_mask (write_security_attributes); + g_print ("\tWrite security attributes: (%s) %s\n", + qmi_uim_security_attribute_logic_get_string (write_security_attributes_logic), + VALIDATE_MASK_NONE (write_security_attributes_str)); + + increase_security_attributes_str = qmi_uim_security_attribute_build_string_from_mask (increase_security_attributes); + g_print ("\tIncrease security attributes: (%s) %s\n", + qmi_uim_security_attribute_logic_get_string (increase_security_attributes_logic), + VALIDATE_MASK_NONE (increase_security_attributes_str)); + + deactivate_security_attributes_str = qmi_uim_security_attribute_build_string_from_mask (deactivate_security_attributes); + g_print ("\tDeactivate security attributes: (%s) %s\n", + qmi_uim_security_attribute_logic_get_string (deactivate_security_attributes_logic), + VALIDATE_MASK_NONE (deactivate_security_attributes_str)); + + activate_security_attributes_str = qmi_uim_security_attribute_build_string_from_mask (activate_security_attributes); + g_print ("\tActivate security attributes: (%s) %s\n", + qmi_uim_security_attribute_logic_get_string (activate_security_attributes_logic), + VALIDATE_MASK_NONE (activate_security_attributes_str)); + + raw_str = qmicli_get_raw_data_printable (raw, 80, "\t"); + g_print ("\tRaw: %s\n", raw_str); + } + + qmi_message_uim_get_file_attributes_output_unref (output); + operation_shutdown (TRUE); +} + +static QmiMessageUimGetFileAttributesInput * +get_file_attributes_build_input (const gchar *file_path_str) +{ + QmiMessageUimGetFileAttributesInput *input; + guint16 file_id = 0; + GArray *file_path = NULL; + GArray *placeholder_aid; + + if (!get_sim_file_id_and_path (file_path_str, &file_id, &file_path)) + return NULL; + + placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8)); + + input = qmi_message_uim_get_file_attributes_input_new (); + qmi_message_uim_get_file_attributes_input_set_session ( + input, + QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING, + placeholder_aid, /* ignored */ + NULL); + qmi_message_uim_get_file_attributes_input_set_file ( + input, + file_id, + file_path, + NULL); + g_array_unref (placeholder_aid); + g_array_unref (file_path); + return input; +} + +#endif /* HAVE_QMI_MESSAGE_UIM_GET_FILE_ATTRIBUTES */ + +#if defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER || \ + defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER_ALL + +static void +refresh_complete_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimRefreshCompleteOutput *output; + GError *error = NULL; + + output = qmi_client_uim_refresh_complete_finish (client, res, &error); + if (!output) { + g_printerr ("error: refresh complete failed: %s\n", error->message); + g_error_free (error); + return; + } + + /* Ignore error, just log it as warning. In case we send complete message when + * the modem does not expect it, we could get an error that is harmless. + */ + if (!qmi_message_uim_refresh_complete_output_get_result (output, &error)) { + g_warning ("refresh complete failed: %s\n", error->message); + g_error_free (error); + } else + g_debug ("Refresh complete OK."); + + qmi_message_uim_refresh_complete_output_unref (output); +} + +static void +refresh_complete (QmiClientUim *client, + gboolean success) +{ + QmiMessageUimRefreshCompleteInput *refresh_complete_input; + GArray *placeholder_aid; + + placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8)); + + refresh_complete_input = qmi_message_uim_refresh_complete_input_new (); + qmi_message_uim_refresh_complete_input_set_session ( + refresh_complete_input, + QMI_UIM_SESSION_TYPE_CARD_SLOT_1, + placeholder_aid, /* ignored */ + NULL); + qmi_message_uim_refresh_complete_input_set_info ( + refresh_complete_input, + success, + NULL); + + qmi_client_uim_refresh_complete ( + ctx->client, + refresh_complete_input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) refresh_complete_ready, + NULL); + qmi_message_uim_refresh_complete_input_unref (refresh_complete_input); + g_array_unref (placeholder_aid); +} + +static void +refresh_received (QmiClientUim *client, + QmiIndicationUimRefreshOutput *output) +{ + QmiUimRefreshStage stage; + QmiUimRefreshMode mode; + GArray *files = NULL; + GError *error = NULL; + guint i, j; + + g_print ("[%s] Received refresh indication:\n", + qmi_device_get_path_display (ctx->device)); + if (!qmi_indication_uim_refresh_output_get_event ( + output, &stage, &mode, NULL, NULL, &files, &error)) { + g_printerr ("error: could not parse refresh ind: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + g_print (" Refresh stage: %s\n", + qmi_uim_refresh_stage_get_string (stage)); + g_print (" Refresh mode: %s\n", + qmi_uim_refresh_mode_get_string (mode)); + g_print (" Files:\n"); + if (files && files->len > 0) + for (i = 0; i < files->len; i++) { + QmiIndicationUimRefreshOutputEventFilesElement *file; + GArray *path; + + file = &g_array_index (files, QmiIndicationUimRefreshOutputEventFilesElement, i); + g_print (" 0x%x; path:", + file->file_id); + path = file->path; + if (path && path->len >= 2) + for (j = 0; j < path->len / 2; j++) { + guint16 path_component; + path_component = g_array_index (path, guint8, j * 2) | + ((guint16)g_array_index (path, guint8, j * 2 + 1) << 8); + g_print (" 0x%x", path_component); + } + else + g_print (" "); + g_print ("\n"); + } + else + g_print (" \n"); + /* Send refresh complete message only in start stage and only if the + * mode is something other than reset. + */ + if (stage == QMI_UIM_REFRESH_STAGE_START && mode != QMI_UIM_REFRESH_MODE_RESET) + refresh_complete (client, TRUE); +} + +static void +refresh_cancelled (GCancellable *cancellable) +{ + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER + * HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER_ALL */ + +#if defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER + +static void +register_refresh_events_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimRefreshRegisterOutput *output; + GError *error = NULL; + + output = qmi_client_uim_refresh_register_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_uim_refresh_register_output_get_result (output, &error)) { + g_printerr ("error: could not register refresh file events: %s\n", error->message); + qmi_message_uim_refresh_register_output_unref (output); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + g_debug ("Registered refresh file 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 (refresh_cancelled), + NULL, + NULL); + qmi_message_uim_refresh_register_output_unref (output); +} + +static void +register_refresh_events (gchar **file_path_array) +{ + QmiMessageUimRefreshRegisterInput *refresh_input; + GArray *placeholder_aid; + GArray *file_list; + QmiMessageUimRefreshRegisterInputInfoFilesElement file_element; + guint i; + + file_list = g_array_new (FALSE, FALSE, sizeof (QmiMessageUimRefreshRegisterInputInfoFilesElement)); + while (*file_path_array) { + memset (&file_element, 0, sizeof (file_element)); + if (!get_sim_file_id_and_path (*file_path_array, &file_element.file_id, &file_element.path)) + goto out; + + g_array_append_val (file_list, file_element); + file_path_array++; + } + + placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8)); + refresh_input = qmi_message_uim_refresh_register_input_new (); + qmi_message_uim_refresh_register_input_set_session ( + refresh_input, + QMI_UIM_SESSION_TYPE_CARD_SLOT_1, + placeholder_aid, /* ignored */ + NULL); + qmi_message_uim_refresh_register_input_set_info ( + refresh_input, + TRUE, + FALSE, + file_list, + NULL); + + qmi_client_uim_refresh_register ( + ctx->client, + refresh_input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) register_refresh_events_ready, + NULL); + qmi_message_uim_refresh_register_input_unref (refresh_input); + g_array_unref (placeholder_aid); + +out: + for (i = 0; i < file_list->len; i++) { + QmiMessageUimRefreshRegisterInputInfoFilesElement *file; + file = &g_array_index (file_list, QmiMessageUimRefreshRegisterInputInfoFilesElement, i); + g_array_unref (file->path); + } + g_array_unref (file_list); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER */ + +#if defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER_ALL + +static void +register_refresh_all_events_ready (QmiClientUim *client, + GAsyncResult *res) +{ + QmiMessageUimRefreshRegisterAllOutput *output; + GError *error = NULL; + + output = qmi_client_uim_refresh_register_all_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_uim_refresh_register_all_output_get_result (output, &error)) { + g_printerr ("error: could not register refresh file events: %s\n", error->message); + qmi_message_uim_refresh_register_all_output_unref (output); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + g_debug ("Registered refresh all file 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 (refresh_cancelled), + NULL, + NULL); + qmi_message_uim_refresh_register_all_output_unref (output); +} + +static void +register_refresh_all_events (void) +{ + QmiMessageUimRefreshRegisterAllInput *refresh_all_input; + GArray *placeholder_aid; + + refresh_all_input = qmi_message_uim_refresh_register_all_input_new (); + placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8)); + qmi_message_uim_refresh_register_all_input_set_session ( + refresh_all_input, + QMI_UIM_SESSION_TYPE_CARD_SLOT_1, + placeholder_aid, /* ignored */ + NULL); + + qmi_message_uim_refresh_register_all_input_set_info ( + refresh_all_input, + TRUE, + NULL); + qmi_client_uim_refresh_register_all ( + ctx->client, + refresh_all_input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) register_refresh_all_events_ready, + NULL); + qmi_message_uim_refresh_register_all_input_unref (refresh_all_input); + g_array_unref (placeholder_aid); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER_ALL */ + +#if defined HAVE_QMI_MESSAGE_UIM_GET_CONFIGURATION + +static QmiMessageUimGetConfigurationInput * +get_configuration_input_create (void) +{ + QmiMessageUimGetConfigurationInput *input; + + input = qmi_message_uim_get_configuration_input_new (); + + qmi_message_uim_get_configuration_input_set_configuration_mask ( + input, + QMI_UIM_CONFIGURATION_PERSONALIZATION_STATUS, + NULL); + + return input; +} + +static void +get_configuration_ready (QmiClientUim *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageUimGetConfigurationOutput) output = NULL; + g_autoptr(GError) error = NULL; + GArray *elements = NULL; + GArray *other_slots = NULL; + + output = qmi_client_uim_get_configuration_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_uim_get_configuration_output_get_result (output, &error)) { + g_printerr ("error: get configuration failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("Configuration successfully retrieved\n"); + + /* Other slots TLV contains info for slots > 1 */ + qmi_message_uim_get_configuration_output_get_personalization_status_other (output, &other_slots, NULL); + + if (qmi_message_uim_get_configuration_output_get_personalization_status (output, &elements, NULL)) { + if (elements->len == 0) + g_print ("Personalization features%s: all disabled\n", + other_slots ? " in slot 1" : ""); + else { + QmiMessageUimGetConfigurationOutputPersonalizationStatusElement *element; + guint i; + + g_print ("Personalization features%s:\n", + other_slots ? " in slot 1" : ""); + for (i = 0; i < elements->len; i++) { + element = &g_array_index (elements, + QmiMessageUimGetConfigurationOutputPersonalizationStatusElement, + i); + g_print ("\tPersonalization: %s\n" + "\t\tVerify left: %u\n" + "\t\tUnblock left: %u\n", + qmi_uim_card_application_personalization_feature_get_string (element->feature), + element->verify_left, + element->unblock_left); + } + } + } + + if (other_slots) { + if (other_slots->len == 0) + g_print ("Personalization features in other slots: all disabled\n"); + else { + guint i_slot; + + for (i_slot = 0; i_slot < other_slots->len; i_slot++) { + QmiMessageUimGetConfigurationOutputPersonalizationStatusOtherElement *slot_element; + guint i; + + slot_element = &g_array_index (other_slots, QmiMessageUimGetConfigurationOutputPersonalizationStatusOtherElement, i_slot); + if (!slot_element->slot) + continue; + + g_print ("Personalization features in slot %u:\n", i_slot + 2); + for (i = 0; i < slot_element->slot->len; i++) { + QmiMessageUimGetConfigurationOutputPersonalizationStatusOtherElementSlotElement *element; + + element = &g_array_index (slot_element->slot, + QmiMessageUimGetConfigurationOutputPersonalizationStatusOtherElementSlotElement, + i); + g_print ("\tPersonalization: %s\n" + "\t\tVerify left: %u\n" + "\t\tUnblock left: %u\n", + qmi_uim_card_application_personalization_feature_get_string (element->feature), + element->verify_left, + element->unblock_left); + } + } + } + } + + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_GET_CONFIGURATION */ + +#if defined HAVE_QMI_MESSAGE_UIM_DEPERSONALIZATION + +static QmiMessageUimDepersonalizationInput * +depersonalization_input_create (const gchar *str) +{ + g_auto(GStrv) split = NULL; + QmiMessageUimDepersonalizationInput *input = NULL; + QmiUimCardApplicationPersonalizationFeature feature; + QmiUimDepersonalizationOperation operation; + const gchar *control_key; + guint slot = 0; + + /* Prepare inputs. + * Format of the string is: + * "[(feature),(operation),(control key)[,(slot number)]]" + */ + split = g_strsplit (str, ",", -1); + + if (!split[0] || !qmicli_read_uim_card_application_personalization_feature_from_string (split[0], &feature)) { + g_printerr ("error: invalid personalization feature\n"); + return NULL; + } + + if (!split[1] || !qmicli_read_uim_depersonalization_operation_from_string (split[1], &operation)) { + g_printerr ("error: invalid depersonalization operation\n"); + return NULL; + } + + if (!split[2]) { + g_printerr ("error: missing control key\n"); + return NULL; + } + control_key = split[2]; + + if (g_strv_length (split) > 3) { + if (!qmicli_read_uint_from_string (split[3], &slot) || (slot < 1) || (slot > 5)) { + g_printerr ("error: invalid slot number\n"); + return NULL; + } + } + + input = qmi_message_uim_depersonalization_input_new (); + qmi_message_uim_depersonalization_input_set_info (input, feature, operation, control_key, NULL); + + /* skip setting slot if not given by the user */ + if (slot > 0) + qmi_message_uim_depersonalization_input_set_slot (input, slot, NULL); + + return input; +} + +static void +depersonalization_ready (QmiClientUim *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageUimDepersonalizationOutput) output = NULL; + g_autoptr(GError) error = NULL; + guint8 unblock_left; + guint8 verify_left; + + output = qmi_client_uim_depersonalization_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (qmi_message_uim_depersonalization_output_get_result (output, &error)) { + g_print ("Modem was unlocked successfully\n"); + operation_shutdown (TRUE); + return; + } + + g_printerr ("error: depersonalization failed: %s\n", error->message); + if (qmi_message_uim_depersonalization_output_get_retries_remaining ( + output, + &verify_left, + &unblock_left, + NULL)) { + g_printerr ("Retries left:\n" + "\tVerify: %u\n" + "\tUnblock: %u\n", + verify_left, + unblock_left); + } + + operation_shutdown (FALSE); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_DEPERSONALIZATION */ + +#if defined HAVE_QMI_MESSAGE_UIM_REMOTE_UNLOCK + +static QmiMessageUimRemoteUnlockInput * +remote_unlock_input_create (const gchar *simlock_data_str) +{ + QmiMessageUimRemoteUnlockInput *input; + GArray *simlock_data = NULL; + + if (!qmicli_read_raw_data_from_string (simlock_data_str, &simlock_data)) + return NULL; + + input = qmi_message_uim_remote_unlock_input_new (); + + if (simlock_data->len <= 1024) { + qmi_message_uim_remote_unlock_input_set_simlock_data (input, + simlock_data, + NULL); + } else { + qmi_message_uim_remote_unlock_input_set_simlock_extended_data (input, + simlock_data, + NULL); + } + + g_array_unref (simlock_data); + return input; +} + +static void +remote_unlock_ready (QmiClientUim *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageUimRemoteUnlockOutput) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_uim_remote_unlock_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_uim_remote_unlock_output_get_result (output, &error)) { + g_printerr ("error: remote unlock operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("Remote unlock operation successfully completed\n"); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_REMOTE_UNLOCK */ + +#if defined HAVE_QMI_MESSAGE_UIM_OPEN_LOGICAL_CHANNEL + +static QmiMessageUimOpenLogicalChannelInput * +open_logical_channel_input_create (const gchar *str) +{ + QmiMessageUimOpenLogicalChannelInput *input; + g_auto(GStrv) split = NULL; + guint slot; + g_autoptr(GArray) aid_data = NULL; + + /* Prepare inputs. + * Format of the string is: + * "[(slot number),(aid)]" + */ + split = g_strsplit (str, ",", -1); + + if (!split[0] || !qmicli_read_uint_from_string (split[0], &slot) || (slot > G_MAXUINT8)) { + g_printerr ("error: invalid slot number\n"); + return NULL; + } + + /* AID is optional */ + if (split[1]) { + if (!qmicli_read_raw_data_from_string (split[1], &aid_data)) { + g_printerr ("error: invalid AID data\n"); + return NULL; + } + } + + input = qmi_message_uim_open_logical_channel_input_new (); + qmi_message_uim_open_logical_channel_input_set_slot (input, slot, NULL); + if (aid_data) + qmi_message_uim_open_logical_channel_input_set_aid (input, aid_data, NULL); + + return input; +} + +static void +open_logical_channel_ready (QmiClientUim *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageUimOpenLogicalChannelOutput) output = NULL; + g_autoptr(GError) error = NULL; + guint8 channel_id; + + output = qmi_client_uim_open_logical_channel_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_uim_open_logical_channel_output_get_result (output, &error)) { + g_printerr ("error: open logical channel operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_uim_open_logical_channel_output_get_channel_id (output, &channel_id, &error)) { + g_printerr ("error: get channel id operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("Open Logical Channel operation successfully completed: %d\n", channel_id); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_OPEN_LOGICAL_CHANNEL */ + +#if defined HAVE_QMI_MESSAGE_UIM_LOGICAL_CHANNEL + +static QmiMessageUimLogicalChannelInput * +close_logical_channel_input_create (const gchar *str) +{ + QmiMessageUimLogicalChannelInput *input; + g_auto(GStrv) split = NULL; + guint slot; + guint channel_id; + + /* Prepare inputs. + * Format of the string is: + * "[(slot number),(channel ID)]" + */ + split = g_strsplit (str, ",", -1); + + if (!split[0] || !qmicli_read_uint_from_string (split[0], &slot) || (slot > G_MAXUINT8)) { + g_printerr ("error: invalid slot number\n"); + return NULL; + } + + if (!split[1] || !qmicli_read_uint_from_string (split[1], &channel_id) || (channel_id > G_MAXUINT8)) { + g_printerr ("error: invalid channel ID\n"); + return NULL; + } + + input = qmi_message_uim_logical_channel_input_new (); + qmi_message_uim_logical_channel_input_set_slot (input, slot, NULL); + qmi_message_uim_logical_channel_input_set_channel_id (input, channel_id, NULL); + + return input; +} + +static void +close_logical_channel_ready (QmiClientUim *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageUimLogicalChannelOutput) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_uim_logical_channel_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_uim_logical_channel_output_get_result (output, &error)) { + g_printerr ("error: close logical channel operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("Close Logical Channel operation successfully completed\n"); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_LOGICAL_CHANNEL */ + +#if defined HAVE_QMI_MESSAGE_UIM_SEND_APDU + +static QmiMessageUimSendApduInput * +send_apdu_input_create (const gchar *str) +{ + QmiMessageUimSendApduInput *input; + g_auto(GStrv) split = NULL; + guint slot; + guint channel_id; + g_autoptr(GArray) apdu_data = NULL; + + /* Prepare inputs. + * Format of the string is: + * "[(slot number),(channel ID),(apdu)]" + */ + split = g_strsplit (str, ",", -1); + + if (!split[0] || !qmicli_read_uint_from_string (split[0], &slot) || (slot > G_MAXUINT8)) { + g_printerr ("error: invalid slot number\n"); + return NULL; + } + + if (!split[1] || !qmicli_read_uint_from_string (split[1], &channel_id) || (channel_id > G_MAXUINT8)) { + g_printerr ("error: invalid channel ID\n"); + return NULL; + } + + if (!split[2] || !qmicli_read_raw_data_from_string (split[2], &apdu_data)) { + g_printerr ("error: invalid APDU data\n"); + return NULL; + } + + input = qmi_message_uim_send_apdu_input_new (); + qmi_message_uim_send_apdu_input_set_slot (input, slot, NULL); + qmi_message_uim_send_apdu_input_set_channel_id (input, channel_id, NULL); + qmi_message_uim_send_apdu_input_set_apdu (input, apdu_data, NULL); + + return input; +} + +static void +send_apdu_ready (QmiClientUim *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageUimSendApduOutput) output = NULL; + g_autoptr(GError) error = NULL; + GArray *apdu_res = NULL; + gchar *apdu_res_hex; + + output = qmi_client_uim_send_apdu_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_uim_send_apdu_output_get_result (output, &error)) { + g_printerr ("error: send apdu operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_uim_send_apdu_output_get_apdu_response (output, &apdu_res, &error)) { + g_printerr ("error: get apdu response operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("Send APDU operation successfully completed:"); + apdu_res_hex = qmi_common_str_hex (apdu_res->data, apdu_res->len, ':'); + g_print (" %s\n", apdu_res_hex); + g_free (apdu_res_hex); + + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_UIM_SEND_APDU */ + +void +qmicli_uim_run (QmiDevice *device, + QmiClientUim *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new0 (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_UIM_SET_PIN_PROTECTION + if (set_pin_protection_str) { + QmiMessageUimSetPinProtectionInput *input; + + g_debug ("Asynchronously setting PIN protection..."); + input = set_pin_protection_input_create (set_pin_protection_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_uim_set_pin_protection (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_pin_protection_ready, + NULL); + qmi_message_uim_set_pin_protection_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_VERIFY_PIN + if (verify_pin_str) { + QmiMessageUimVerifyPinInput *input; + + g_debug ("Asynchronously verifying PIN..."); + input = verify_pin_input_create (verify_pin_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_uim_verify_pin (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)verify_pin_ready, + NULL); + qmi_message_uim_verify_pin_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_UNBLOCK_PIN + if (unblock_pin_str) { + QmiMessageUimUnblockPinInput *input; + + g_debug ("Asynchronously unblocking PIN..."); + input = unblock_pin_input_create (unblock_pin_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_uim_unblock_pin (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)unblock_pin_ready, + NULL); + qmi_message_uim_unblock_pin_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_CHANGE_PIN + if (change_pin_str) { + QmiMessageUimChangePinInput *input; + + g_debug ("Asynchronously changing PIN..."); + input = change_pin_input_create (change_pin_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_uim_change_pin (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)change_pin_ready, + NULL); + qmi_message_uim_change_pin_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_READ_TRANSPARENT + if (read_transparent_str) { + QmiMessageUimReadTransparentInput *input; + + input = read_transparent_build_input (read_transparent_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + g_debug ("Asynchronously reading transparent file at '%s'...", + read_transparent_str); + qmi_client_uim_read_transparent (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)read_transparent_ready, + NULL); + qmi_message_uim_read_transparent_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_READ_RECORD + if (read_record_str) { + QmiMessageUimReadRecordInput *input; + + input = read_record_input_create (read_record_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + g_debug ("Asynchronously reading record file at '%s'...", + read_record_str); + qmi_client_uim_read_record (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)read_record_ready, + NULL); + qmi_message_uim_read_record_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_GET_FILE_ATTRIBUTES + if (get_file_attributes_str) { + QmiMessageUimGetFileAttributesInput *input; + + input = get_file_attributes_build_input (get_file_attributes_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + g_debug ("Asynchronously reading attributes of file '%s'...", + get_file_attributes_str); + qmi_client_uim_get_file_attributes (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_file_attributes_ready, + NULL); + qmi_message_uim_get_file_attributes_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_GET_CARD_STATUS + if (get_card_status_flag) { + g_debug ("Asynchronously getting card status..."); + qmi_client_uim_get_card_status (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_card_status_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_GET_SUPPORTED_MESSAGES + if (get_supported_messages_flag) { + g_debug ("Asynchronously getting supported UIM messages..."); + qmi_client_uim_get_supported_messages (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_supported_messages_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_POWER_ON_SIM + if (sim_power_on_str) { + QmiMessageUimPowerOnSimInput *input; + + g_debug ("Asynchronously power on SIM card"); + input = power_on_sim_input_create (sim_power_on_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_uim_power_on_sim (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)power_on_sim_ready, + NULL); + qmi_message_uim_power_on_sim_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_POWER_OFF_SIM + if (sim_power_off_str) { + QmiMessageUimPowerOffSimInput *input; + + g_debug ("Asynchronously power off SIM card"); + input = power_off_sim_input_create (sim_power_off_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_uim_power_off_sim (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)power_off_sim_ready, + NULL); + qmi_message_uim_power_off_sim_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_CHANGE_PROVISIONING_SESSION + if (change_provisioning_session_str) { + QmiMessageUimChangeProvisioningSessionInput *input; + + g_debug ("Asynchronously changing provisioning session"); + input = change_provisioning_session_input_create (change_provisioning_session_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_uim_change_provisioning_session (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)change_provisioning_session_ready, + NULL); + qmi_message_uim_change_provisioning_session_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS + /* Request to get slot status? */ + if (get_slot_status_flag) { + g_debug ("Asynchronously getting slot status..."); + qmi_client_uim_get_slot_status (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_slot_status_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_SWITCH_SLOT && defined HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS + /* Request to change active slot? */ + if (switch_slot_str) { + guint physical_slot; + + if (!qmicli_read_uint_from_string (switch_slot_str, &physical_slot) || + (physical_slot < 1) || (physical_slot > G_MAXUINT8)) { + g_printerr ("error: invalid slot number\n"); + return; + } + + g_debug ("Asynchronously switching active slot"); + + qmi_client_uim_get_slot_status (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_active_logical_slot_ready, + GUINT_TO_POINTER (physical_slot)); + return; + } +#endif + +#if defined HAVE_QMI_INDICATION_UIM_SLOT_STATUS + /* Watch for slot status changes? */ + if (monitor_slot_status_flag) { + g_debug ("Listening for slot status changes..."); + register_physical_slot_status_events (); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER + if (monitor_refresh_file_array) { + g_debug ("Listening for refresh events..."); + register_refresh_events (monitor_refresh_file_array); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER_ALL + if (monitor_refresh_all_flag) { + g_debug ("Listening for all refresh events..."); + register_refresh_all_events (); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_RESET + /* Request to reset UIM service? */ + if (reset_flag) { + g_debug ("Asynchronously resetting UIM service..."); + qmi_client_uim_reset (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)reset_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_GET_CONFIGURATION + /* Request to get personalization status? */ + if (get_configuration_flag) { + g_autoptr(QmiMessageUimGetConfigurationInput) input = NULL; + + g_debug ("Asynchronously getting UIM configuration..."); + input = get_configuration_input_create (); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_uim_get_configuration (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_configuration_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_DEPERSONALIZATION + /* Request to depersonalize the modem? */ + if (depersonalization_str) { + g_autoptr(QmiMessageUimDepersonalizationInput) input = NULL; + + g_debug ("Asynchronously removing personalization..."); + input = depersonalization_input_create (depersonalization_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_uim_depersonalization (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)depersonalization_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_REMOTE_UNLOCK + /* Request to perform remote unlock operations? */ + if (remote_unlock_str) { + g_autoptr(QmiMessageUimRemoteUnlockInput) input = NULL; + + g_debug ("Asynchronously updating SimLock data..."); + input = remote_unlock_input_create (remote_unlock_str); + if (!input) { + g_printerr ("error: couldn't parse the input string as a bytearray\n"); + operation_shutdown (FALSE); + return; + } + + qmi_client_uim_remote_unlock (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)remote_unlock_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_OPEN_LOGICAL_CHANNEL + /* Request to open logical channel? */ + if (open_logical_channel_str) { + g_autoptr(QmiMessageUimOpenLogicalChannelInput) input = NULL; + + g_debug ("Asynchronously opening logical channel..."); + input = open_logical_channel_input_create (open_logical_channel_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_uim_open_logical_channel (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)open_logical_channel_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_LOGICAL_CHANNEL + /* Request to close logical channel? */ + if (close_logical_channel_str) { + g_autoptr(QmiMessageUimLogicalChannelInput) input = NULL; + + g_debug ("Asynchronously closing logical channel..."); + input = close_logical_channel_input_create (close_logical_channel_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_uim_logical_channel (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)close_logical_channel_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_UIM_SEND_APDU + /* Request to send APDU? */ + if (send_apdu_str) { + g_autoptr(QmiMessageUimSendApduInput) input = NULL; + + g_debug ("Asynchronously sending APDU..."); + input = send_apdu_input_create (send_apdu_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + qmi_client_uim_send_apdu (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)send_apdu_ready, + NULL); + return; + } +#endif + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_UIM */ diff --git a/pkgs/qmi-nmea/qmicli-voice.c b/pkgs/qmi-nmea/qmicli-voice.c new file mode 100644 index 0000000..1c2f72c --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-voice.c @@ -0,0 +1,379 @@ +/* -*- 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) 2014-2017 Aleksander Morgado + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_VOICE + +#undef VALIDATE_MASK_NONE +#define VALIDATE_MASK_NONE(str) (str ? str : "none") + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientVoice *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gboolean get_config_flag; +static gboolean get_supported_messages_flag; +static gboolean noop_flag; + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_VOICE_GET_CONFIG + { "voice-get-config", 0, 0, G_OPTION_ARG_NONE, &get_config_flag, + "Get Voice service configuration", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_VOICE_GET_SUPPORTED_MESSAGES + { "voice-get-supported-messages", 0, 0, G_OPTION_ARG_NONE, &get_supported_messages_flag, + "Get supported messages", + NULL + }, +#endif + { "voice-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a VOICE client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_voice_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("voice", + "VOICE options:", + "Show Voice Service options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_voice_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (get_config_flag + + get_supported_messages_flag + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many VOICE actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->client) + g_object_unref (context->client); + 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); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_VOICE_GET_CONFIG + +static void +get_config_ready (QmiClientVoice *client, + GAsyncResult *res) +{ + QmiMessageVoiceGetConfigOutput *output; + GError *error = NULL; + QmiVoiceDomain current_voice_domain_preference; + QmiVoicePrivacy current_voice_privacy_preference; + gboolean current_amr_status_gsm; + QmiVoiceWcdmaAmrStatus current_amr_status_wcdma; + guint8 current_preferred_voice_so_nam_id; + gboolean current_preferred_voice_so_evrc_capability; + QmiVoiceServiceOption current_preferred_voice_so_home_page_voice_service_option; + QmiVoiceServiceOption current_preferred_voice_so_home_origination_voice_service_option; + QmiVoiceServiceOption current_preferred_voice_so_roaming_origination_voice_service_option; + QmiVoiceTtyMode current_tty_mode; + guint8 roam_timer_count_nam_id; + guint32 roam_timer_count_roam_timer; + guint8 air_timer_count_nam_id; + guint32 air_timer_count_air_timer; + gboolean auto_answer_status; + + output = qmi_client_voice_get_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_voice_get_config_output_get_result (output, &error)) { + g_printerr ("error: couldn't get Voice configuration: %s\n", error->message); + g_error_free (error); + qmi_message_voice_get_config_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully retrieved Voice configuration:\n", + qmi_device_get_path_display (ctx->device)); + + if (qmi_message_voice_get_config_output_get_auto_answer_status ( + output, + &auto_answer_status, + NULL)) + g_print ("Auto Answer Status: '%s'", + auto_answer_status ? "enabled" : "disabled"); + + if (qmi_message_voice_get_config_output_get_air_timer_count ( + output, + &air_timer_count_nam_id, + &air_timer_count_air_timer, + NULL)) + g_print ("Air Timer Count:\n" + "\tNAM ID: '%u'" + "\tTimer: '%u'", + air_timer_count_nam_id, + air_timer_count_air_timer); + + if (qmi_message_voice_get_config_output_get_roam_timer_count ( + output, + &roam_timer_count_nam_id, + &roam_timer_count_roam_timer, + NULL)) + g_print ("Roam Timer Count:\n" + "\tNAM ID: '%u'" + "\tTimer: '%u'", + roam_timer_count_nam_id, + roam_timer_count_roam_timer); + + if (qmi_message_voice_get_config_output_get_current_tty_mode ( + output, + ¤t_tty_mode, + NULL)) + g_print ("Current TTY mode: '%s'", + qmi_voice_tty_mode_get_string (current_tty_mode)); + + if (qmi_message_voice_get_config_output_get_current_preferred_voice_so ( + output, + ¤t_preferred_voice_so_nam_id, + ¤t_preferred_voice_so_evrc_capability, + ¤t_preferred_voice_so_home_page_voice_service_option, + ¤t_preferred_voice_so_home_origination_voice_service_option, + ¤t_preferred_voice_so_roaming_origination_voice_service_option, + NULL)) { + g_print ("Current Preferred Voice SO:\n" + "\tNAM ID: '%u'\n" + "\tEVRC capability: '%s'\n" + "\tHome Page Voice SO: '%s'\n" + "\tHome Origination Voice SO: '%s'\n" + "\tRoaming Origination Voice SO: '%s'\n", + current_preferred_voice_so_nam_id, + current_preferred_voice_so_evrc_capability ? "enabled" : "disabled", + qmi_voice_service_option_get_string (current_preferred_voice_so_home_page_voice_service_option), + qmi_voice_service_option_get_string (current_preferred_voice_so_home_origination_voice_service_option), + qmi_voice_service_option_get_string (current_preferred_voice_so_roaming_origination_voice_service_option)); + } + + if (qmi_message_voice_get_config_output_get_current_amr_status ( + output, + ¤t_amr_status_gsm, ¤t_amr_status_wcdma, + NULL)) { + gchar *current_amr_status_wcdma_str; + + current_amr_status_wcdma_str = qmi_voice_wcdma_amr_status_build_string_from_mask (current_amr_status_wcdma); + g_print ("AMR Status:\n" + "\tGSM: '%s'\n" + "\tWCDMA: '%s' (0x%04X)\n", + current_amr_status_gsm ? "enabled" : "disabled", + VALIDATE_MASK_NONE (current_amr_status_wcdma_str), + current_amr_status_wcdma); + g_free (current_amr_status_wcdma_str); + } + + if (qmi_message_voice_get_config_output_get_current_voice_privacy_preference ( + output, + ¤t_voice_privacy_preference, + NULL)) + g_print ("Current Voice Privacy Preference: '%s'\n", + qmi_voice_privacy_get_string (current_voice_privacy_preference)); + + if (qmi_message_voice_get_config_output_get_current_voice_domain_preference ( + output, + ¤t_voice_domain_preference, + NULL)) + g_print ("Current Voice Domain Preference: '%s'\n", + qmi_voice_domain_get_string (current_voice_domain_preference)); + + qmi_message_voice_get_config_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_VOICE_GET_CONFIG */ + +#if defined HAVE_QMI_MESSAGE_VOICE_GET_SUPPORTED_MESSAGES + +static void +get_supported_messages_ready (QmiClientVoice *client, + GAsyncResult *res) +{ + QmiMessageVoiceGetSupportedMessagesOutput *output; + GError *error = NULL; + GArray *bytearray = NULL; + gchar *str; + + output = qmi_client_voice_get_supported_messages_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_voice_get_supported_messages_output_get_result (output, &error)) { + g_printerr ("error: couldn't get supported VOICE messages: %s\n", error->message); + g_error_free (error); + qmi_message_voice_get_supported_messages_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got supported VOICE messages:\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_voice_get_supported_messages_output_get_list (output, &bytearray, NULL); + str = qmicli_get_supported_messages_list (bytearray ? (const guint8 *)bytearray->data : NULL, + bytearray ? bytearray->len : 0); + g_print ("%s", str); + g_free (str); + + qmi_message_voice_get_supported_messages_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_VOICE_GET_SUPPORTED_MESSAGES */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_voice_run (QmiDevice *device, + QmiClientVoice *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_VOICE_GET_CONFIG + if (get_config_flag) { + QmiMessageVoiceGetConfigInput *input; + + input = qmi_message_voice_get_config_input_new (); + qmi_message_voice_get_config_input_set_auto_answer (input, TRUE, NULL); + qmi_message_voice_get_config_input_set_air_timer (input, TRUE, NULL); + qmi_message_voice_get_config_input_set_roam_timer (input, TRUE, NULL); + qmi_message_voice_get_config_input_set_tty_mode (input, TRUE, NULL); + qmi_message_voice_get_config_input_set_preferred_voice_service_option (input, TRUE, NULL); + qmi_message_voice_get_config_input_set_amr_status (input, TRUE, NULL); + qmi_message_voice_get_config_input_set_preferred_voice_privacy (input, TRUE, NULL); + if (qmi_client_check_version (QMI_CLIENT (ctx->client), 2, 3)) + qmi_message_voice_get_config_input_set_nam_index (input, TRUE, NULL); + if (qmi_client_check_version (QMI_CLIENT (ctx->client), 2, 9)) + qmi_message_voice_get_config_input_set_voice_domain_preference (input, TRUE, NULL); + + g_debug ("Asynchronously getting voice configuration..."); + qmi_client_voice_get_config (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_config_ready, + NULL); + qmi_message_voice_get_config_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_VOICE_GET_SUPPORTED_MESSAGES + if (get_supported_messages_flag) { + g_debug ("Asynchronously getting supported voice messages..."); + qmi_client_voice_get_supported_messages (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_supported_messages_ready, + NULL); + return; + } +#endif + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_VOICE */ diff --git a/pkgs/qmi-nmea/qmicli-wda.c b/pkgs/qmi-nmea/qmicli-wda.c new file mode 100644 index 0000000..f512a93 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-wda.c @@ -0,0 +1,811 @@ +/* -*- 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) 2014-2017 Aleksander Morgado + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_WDA + +#define QMI_WDA_AGGREGATION_PROTOCOL_MAX_DATAGRAMS_UNDEFINED 0xFFFFFFFF +#define QMI_WDA_AGGREGATION_PROTOCOL_MAX_DATAGRAM_SIZE_UNDEFINED 0xFFFFFFFF +#define QMI_WDA_ENDPOINT_INTERFACE_NUMBER_UNDEFINED -1 + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientWda *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gchar *set_data_format_str; +static gchar *get_data_format_str; +static gboolean get_data_format_flag; +static gboolean get_supported_messages_flag; +static gboolean noop_flag; + +#if defined HAVE_QMI_MESSAGE_WDA_GET_DATA_FORMAT + +static gboolean +parse_get_data_format (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + get_data_format_flag = TRUE; + if (value && value[0]) + get_data_format_str = g_strdup (value); + return TRUE; +} + +#endif + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_WDA_SET_DATA_FORMAT + { "wda-set-data-format", 0, 0, G_OPTION_ARG_STRING, &set_data_format_str, + "Set data format (allowed keys: link-layer-protocol (802-3|raw-ip), ul-protocol (disabled|tlp|qc-ncm|mbim|rndis|qmap|qmapv5), dl-protocol (disabled|tlp|qc-ncm|mbim|rndis|qmap|qmapv5), dl-datagram-max-size, dl-max-datagrams, ep-type (undefined|hsusb|pcie|embedded), ep-iface-number, ul-datagram-max-size, ul-max-datagrams)", + "[\"key=value,...\"]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDA_GET_DATA_FORMAT + { "wda-get-data-format", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, parse_get_data_format, + "Get data format (allowed keys: ep-type (undefined|hsusb|pcie|embedded), ep-iface-number); also allows empty key list", + "[\"key=value,...\"]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDA_GET_SUPPORTED_MESSAGES + { "wda-get-supported-messages", 0, 0, G_OPTION_ARG_NONE, &get_supported_messages_flag, + "Get supported messages", + NULL + }, +#endif + { "wda-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a WDA client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_wda_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("wda", + "WDA options:", + "Show Wireless Data Administrative options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_wda_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (!!set_data_format_str + + get_data_format_flag + + get_supported_messages_flag + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many WDA actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->client) + g_object_unref (context->client); + 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); + qmicli_async_operation_done (operation_status, FALSE); +} + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +#if defined HAVE_QMI_MESSAGE_WDA_GET_DATA_FORMAT + +typedef struct { + QmiDataEndpointType endpoint_type; + gint endpoint_iface_number; +} GetDataFormatProperties; + +static gboolean +get_data_format_properties_handle (const gchar *key, + const gchar *value, + GError **error, + gpointer user_data) +{ + GetDataFormatProperties *props = (GetDataFormatProperties *)user_data; + + if (!value || !value[0]) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "key '%s' requires a value", + key); + return FALSE; + } + + if (g_ascii_strcasecmp (key, "ep-type") == 0) { + if (!qmicli_read_data_endpoint_type_from_string (value, &(props->endpoint_type))) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "Unrecognized Endpoint Type '%s'", + value); + return FALSE; + } + return TRUE; + } + + if (g_ascii_strcasecmp (key, "ep-iface-number") == 0) { + props->endpoint_iface_number = atoi (value); + return TRUE; + } + + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "Unrecognized option '%s'", + key); + return FALSE; +} + +static QmiMessageWdaGetDataFormatInput * +get_data_format_input_create (const gchar *str) +{ + g_autoptr(QmiMessageWdaGetDataFormatInput) input = NULL; + g_autoptr(GError) error = NULL; + GetDataFormatProperties props = { + .endpoint_type = QMI_DATA_ENDPOINT_TYPE_UNDEFINED, + .endpoint_iface_number = QMI_WDA_ENDPOINT_INTERFACE_NUMBER_UNDEFINED, + }; + + input = qmi_message_wda_get_data_format_input_new (); + + if (!qmicli_parse_key_value_string (str, + &error, + get_data_format_properties_handle, + &props)) { + g_printerr ("error: could not parse input string '%s'\n", error->message); + return NULL; + } + + if ((props.endpoint_type == QMI_DATA_ENDPOINT_TYPE_UNDEFINED) ^ + (props.endpoint_iface_number == QMI_WDA_ENDPOINT_INTERFACE_NUMBER_UNDEFINED)) { + g_printerr ("error: endpoint type and interface number must be both set or both unset\n"); + return NULL; + } + + if ((props.endpoint_type != QMI_DATA_ENDPOINT_TYPE_UNDEFINED) && + (props.endpoint_iface_number != QMI_WDA_ENDPOINT_INTERFACE_NUMBER_UNDEFINED) && + !qmi_message_wda_get_data_format_input_set_endpoint_info ( + input, + props.endpoint_type, + props.endpoint_iface_number, + &error)) { + g_printerr ("error: could not set peripheral endpoint id: %s\n", error->message); + return NULL; + } + + return g_steal_pointer (&input); +} + +static void +get_data_format_ready (QmiClientWda *client, + GAsyncResult *res) +{ + QmiMessageWdaGetDataFormatOutput *output; + GError *error = NULL; + gboolean qos_format; + QmiWdaLinkLayerProtocol link_layer_protocol; + QmiWdaDataAggregationProtocol data_aggregation_protocol; + guint32 ndp_signature; + guint32 data_aggregation_max_size; + guint32 data_aggregation_max_datagrams; + + output = qmi_client_wda_get_data_format_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_wda_get_data_format_output_get_result (output, &error)) { + g_printerr ("error: couldn't get data format: %s\n", error->message); + g_error_free (error); + qmi_message_wda_get_data_format_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got data format\n", + qmi_device_get_path_display (ctx->device)); + + if (qmi_message_wda_get_data_format_output_get_qos_format ( + output, + &qos_format, + NULL)) + g_print (" QoS flow header: %s\n", qos_format ? "yes" : "no"); + + if (qmi_message_wda_get_data_format_output_get_link_layer_protocol ( + output, + &link_layer_protocol, + NULL)) + g_print (" Link layer protocol: '%s'\n", + qmi_wda_link_layer_protocol_get_string (link_layer_protocol)); + + if (qmi_message_wda_get_data_format_output_get_uplink_data_aggregation_protocol ( + output, + &data_aggregation_protocol, + NULL)) + g_print (" Uplink data aggregation protocol: '%s'\n", + qmi_wda_data_aggregation_protocol_get_string (data_aggregation_protocol)); + + if (qmi_message_wda_get_data_format_output_get_downlink_data_aggregation_protocol ( + output, + &data_aggregation_protocol, + NULL)) + g_print ("Downlink data aggregation protocol: '%s'\n", + qmi_wda_data_aggregation_protocol_get_string (data_aggregation_protocol)); + + if (qmi_message_wda_get_data_format_output_get_ndp_signature ( + output, + &ndp_signature, + NULL)) + g_print (" NDP signature: '%u'\n", ndp_signature); + + if (qmi_message_wda_get_data_format_output_get_downlink_data_aggregation_max_datagrams ( + output, + &data_aggregation_max_datagrams, + NULL)) + g_print ("Downlink data aggregation max datagrams: '%u'\n", data_aggregation_max_datagrams); + + if (qmi_message_wda_get_data_format_output_get_downlink_data_aggregation_max_size ( + output, + &data_aggregation_max_size, + NULL)) + g_print ("Downlink data aggregation max size: '%u'\n", data_aggregation_max_size); + + qmi_message_wda_get_data_format_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDA_GET_DATA_FORMAT */ + +#if defined HAVE_QMI_MESSAGE_WDA_SET_DATA_FORMAT + +static void +set_data_format_ready (QmiClientWda *client, + GAsyncResult *res) +{ + QmiMessageWdaSetDataFormatOutput *output; + GError *error = NULL; + gboolean qos_format; + QmiWdaLinkLayerProtocol link_layer_protocol; + QmiWdaDataAggregationProtocol data_aggregation_protocol; + guint32 ndp_signature; + guint32 dl_data_aggregation_max_datagrams; + guint32 dl_data_aggregation_max_size; + guint32 ul_data_aggregation_max_datagrams; + guint32 ul_data_aggregation_max_size; + + output = qmi_client_wda_set_data_format_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_wda_set_data_format_output_get_result (output, &error)) { + g_printerr ("error: couldn't set data format: %s\n", error->message); + g_error_free (error); + qmi_message_wda_set_data_format_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully set data format\n", + qmi_device_get_path_display (ctx->device)); + + if (qmi_message_wda_set_data_format_output_get_qos_format ( + output, + &qos_format, + NULL)) + g_print (" QoS flow header: %s\n", qos_format ? "yes" : "no"); + + if (qmi_message_wda_set_data_format_output_get_link_layer_protocol ( + output, + &link_layer_protocol, + NULL)) + g_print (" Link layer protocol: '%s'\n", + qmi_wda_link_layer_protocol_get_string (link_layer_protocol)); + + if (qmi_message_wda_set_data_format_output_get_uplink_data_aggregation_protocol ( + output, + &data_aggregation_protocol, + NULL)) + g_print (" Uplink data aggregation protocol: '%s'\n", + qmi_wda_data_aggregation_protocol_get_string (data_aggregation_protocol)); + + if (qmi_message_wda_set_data_format_output_get_downlink_data_aggregation_protocol ( + output, + &data_aggregation_protocol, + NULL)) + g_print (" Downlink data aggregation protocol: '%s'\n", + qmi_wda_data_aggregation_protocol_get_string (data_aggregation_protocol)); + + if (qmi_message_wda_set_data_format_output_get_ndp_signature ( + output, + &ndp_signature, + NULL)) + g_print (" NDP signature: '%u'\n", ndp_signature); + + if (qmi_message_wda_set_data_format_output_get_downlink_data_aggregation_max_datagrams ( + output, + &dl_data_aggregation_max_datagrams, + NULL)) + g_print ("Downlink data aggregation max datagrams: '%u'\n", dl_data_aggregation_max_datagrams); + + if (qmi_message_wda_set_data_format_output_get_downlink_data_aggregation_max_size ( + output, + &dl_data_aggregation_max_size, + NULL)) + g_print (" Downlink data aggregation max size: '%u'\n", dl_data_aggregation_max_size); + + if (qmi_message_wda_set_data_format_output_get_uplink_data_aggregation_max_datagrams ( + output, + &ul_data_aggregation_max_datagrams, + NULL)) + g_print (" Uplink data aggregation max datagrams: '%u'\n", ul_data_aggregation_max_datagrams); + + if (qmi_message_wda_set_data_format_output_get_uplink_data_aggregation_max_size ( + output, + &ul_data_aggregation_max_size, + NULL)) + g_print (" Uplink data aggregation max size: '%u'\n", ul_data_aggregation_max_size); + + qmi_message_wda_set_data_format_output_unref (output); + operation_shutdown (TRUE); +} + +typedef struct { + QmiWdaLinkLayerProtocol link_layer_protocol; + QmiWdaDataAggregationProtocol ul_protocol; + QmiWdaDataAggregationProtocol dl_protocol; + guint32 dl_datagram_max_size; + guint32 dl_max_datagrams; + QmiDataEndpointType endpoint_type; + gint endpoint_iface_number; + guint32 ul_datagram_max_size; + guint32 ul_max_datagrams; +} SetDataFormatProperties; + + +static gboolean +set_data_format_properties_handle (const gchar *key, + const gchar *value, + GError **error, + gpointer user_data) +{ + SetDataFormatProperties *props = (SetDataFormatProperties *)user_data; + + if (!value || !value[0]) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "key '%s' requires a value", + key); + return FALSE; + } + + if (g_ascii_strcasecmp (key, "link-layer-protocol") == 0) { + if (!qmicli_read_wda_link_layer_protocol_from_string (value, &(props->link_layer_protocol))) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "Unrecognized Link Layer Protocol '%s'", + value); + return FALSE; + } + return TRUE; + } + + if (g_ascii_strcasecmp (key, "ul-protocol") == 0) { + if (!qmicli_read_wda_data_aggregation_protocol_from_string (value, &(props->ul_protocol))) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "Unrecognized Data Aggregation Protocol '%s'", + value); + return FALSE; + } + return TRUE; + } + + if (g_ascii_strcasecmp (key, "dl-protocol") == 0) { + if (!qmicli_read_wda_data_aggregation_protocol_from_string (value, &(props->dl_protocol))) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "Unrecognized Data Aggregation Protocol '%s'", + value); + return FALSE; + } + return TRUE; + } + + if (g_ascii_strcasecmp (key, "dl-datagram-max-size") == 0) { + props->dl_datagram_max_size = atoi(value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "dl-max-datagrams") == 0) { + props->dl_max_datagrams = atoi(value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "ep-type") == 0) { + if (!qmicli_read_data_endpoint_type_from_string (value, &(props->endpoint_type))) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "Unrecognized Endpoint Type '%s'", + value); + return FALSE; + } + return TRUE; + } + + if (g_ascii_strcasecmp (key, "ep-iface-number") == 0) { + props->endpoint_iface_number = atoi(value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "ul-datagram-max-size") == 0) { + props->ul_datagram_max_size = atoi(value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "ul-max-datagrams") == 0) { + props->ul_max_datagrams = atoi(value); + return TRUE; + } + + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "Unrecognized option '%s'", + key); + return FALSE; +} + +static QmiMessageWdaSetDataFormatInput * +set_data_format_input_create (const gchar *str) +{ + QmiMessageWdaSetDataFormatInput *input = NULL; + GError *error = NULL; + SetDataFormatProperties props = { + .link_layer_protocol = QMI_WDA_LINK_LAYER_PROTOCOL_UNKNOWN, + .ul_protocol = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED, + .dl_protocol = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED, + .dl_datagram_max_size = QMI_WDA_AGGREGATION_PROTOCOL_MAX_DATAGRAM_SIZE_UNDEFINED, + .dl_max_datagrams = QMI_WDA_AGGREGATION_PROTOCOL_MAX_DATAGRAMS_UNDEFINED, + .endpoint_type = QMI_DATA_ENDPOINT_TYPE_UNDEFINED, + .endpoint_iface_number = QMI_WDA_ENDPOINT_INTERFACE_NUMBER_UNDEFINED, + .ul_datagram_max_size = QMI_WDA_AGGREGATION_PROTOCOL_MAX_DATAGRAM_SIZE_UNDEFINED, + .ul_max_datagrams = QMI_WDA_AGGREGATION_PROTOCOL_MAX_DATAGRAMS_UNDEFINED, + }; + + input = qmi_message_wda_set_data_format_input_new (); + + /* New key=value format */ + if (strchr (str, '=')) { + if (!qmicli_parse_key_value_string (str, + &error, + set_data_format_properties_handle, + &props)) { + g_printerr ("error: could not parse input string '%s'\n", error->message); + g_error_free (error); + goto error_out; + } + + if (!qmi_message_wda_set_data_format_input_set_uplink_data_aggregation_protocol ( + input, + props.ul_protocol, + &error)) { + g_printerr ("error: could not set Upload data aggregation protocol '%d': %s\n", + props.ul_protocol, error->message); + g_error_free (error); + goto error_out; + } + + if (!qmi_message_wda_set_data_format_input_set_downlink_data_aggregation_protocol ( + input, + props.dl_protocol, + &error)) { + g_printerr ("error: could not set Download data aggregation protocol '%d': %s\n", + props.dl_protocol, error->message); + g_error_free (error); + goto error_out; + } + + if (props.dl_datagram_max_size != QMI_WDA_AGGREGATION_PROTOCOL_MAX_DATAGRAM_SIZE_UNDEFINED && + !qmi_message_wda_set_data_format_input_set_downlink_data_aggregation_max_size ( + input, + props.dl_datagram_max_size, + &error)) { + g_printerr ("error: could not set Download data aggregation max size %d: %s\n", + props.dl_datagram_max_size, error->message); + g_error_free (error); + goto error_out; + } + + if (props.dl_max_datagrams != QMI_WDA_AGGREGATION_PROTOCOL_MAX_DATAGRAMS_UNDEFINED && + !qmi_message_wda_set_data_format_input_set_downlink_data_aggregation_max_datagrams ( + input, + props.dl_max_datagrams, + &error)) { + g_printerr ("error: could not set Download data aggregation max datagrams %d: %s\n", + props.dl_max_datagrams, error->message); + g_error_free (error); + goto error_out; + } + + if ((props.endpoint_type == QMI_DATA_ENDPOINT_TYPE_UNDEFINED) ^ + (props.endpoint_iface_number == QMI_WDA_ENDPOINT_INTERFACE_NUMBER_UNDEFINED)) { + g_printerr ("error: endpoint type and interface number must be both set or both unset\n"); + goto error_out; + } + + if ((props.endpoint_type != QMI_DATA_ENDPOINT_TYPE_UNDEFINED) && + (props.endpoint_iface_number != QMI_WDA_ENDPOINT_INTERFACE_NUMBER_UNDEFINED) && + !qmi_message_wda_set_data_format_input_set_endpoint_info ( + input, + props.endpoint_type, + props.endpoint_iface_number, + &error)) { + g_printerr ("error: could not set peripheral endpoint id: %s\n", error->message); + g_error_free (error); + goto error_out; + } + + if (props.ul_datagram_max_size != QMI_WDA_AGGREGATION_PROTOCOL_MAX_DATAGRAM_SIZE_UNDEFINED && + !qmi_message_wda_set_data_format_input_set_uplink_data_aggregation_max_size ( + input, + props.ul_datagram_max_size, + &error)) { + g_printerr ("error: could not set Upload data aggregation max size %d: %s\n", + props.ul_datagram_max_size, error->message); + g_error_free (error); + goto error_out; + } + + if (props.ul_max_datagrams != QMI_WDA_AGGREGATION_PROTOCOL_MAX_DATAGRAMS_UNDEFINED && + !qmi_message_wda_set_data_format_input_set_uplink_data_aggregation_max_datagrams ( + input, + props.ul_max_datagrams, + &error)) { + g_printerr ("error: could not set Upload data aggregation max datagrams %d: %s\n", + props.ul_max_datagrams, error->message); + g_error_free (error); + goto error_out; + } + } + /* Old non key=value format, like this: + * "[(raw-ip|802-3)]" + */ + else { + if (!qmicli_read_wda_link_layer_protocol_from_string (str, &(props.link_layer_protocol))) { + g_printerr ("Unrecognized Link Layer Protocol '%s'\n", str); + goto error_out; + } + } + + if (props.link_layer_protocol == QMI_WDA_LINK_LAYER_PROTOCOL_UNKNOWN) { + g_printerr ("error: Link Layer Protocol value is missing\n"); + goto error_out; + } + + if (!qmi_message_wda_set_data_format_input_set_link_layer_protocol ( + input, + props.link_layer_protocol, + &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + g_error_free (error); + goto error_out; + } + + return input; + +error_out: + qmi_message_wda_set_data_format_input_unref (input); + return NULL; +} + +#endif /* HAVE_QMI_MESSAGE_WDA_SET_DATA_FORMAT */ + +#if defined HAVE_QMI_MESSAGE_WDA_GET_SUPPORTED_MESSAGES + +static void +get_supported_messages_ready (QmiClientWda *client, + GAsyncResult *res) +{ + QmiMessageWdaGetSupportedMessagesOutput *output; + GError *error = NULL; + GArray *bytearray = NULL; + gchar *str; + + output = qmi_client_wda_get_supported_messages_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_wda_get_supported_messages_output_get_result (output, &error)) { + g_printerr ("error: couldn't get supported WDA messages: %s\n", error->message); + g_error_free (error); + qmi_message_wda_get_supported_messages_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got supported WDA messages:\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_wda_get_supported_messages_output_get_list (output, &bytearray, NULL); + str = qmicli_get_supported_messages_list (bytearray ? (const guint8 *)bytearray->data : NULL, + bytearray ? bytearray->len : 0); + g_print ("%s", str); + g_free (str); + + qmi_message_wda_get_supported_messages_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDA_GET_SUPPORTED_MESSAGES */ + +void +qmicli_wda_run (QmiDevice *device, + QmiClientWda *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_WDA_SET_DATA_FORMAT + if (set_data_format_str) { + QmiMessageWdaSetDataFormatInput *input; + + input = set_data_format_input_create (set_data_format_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + g_debug ("Asynchronously setting data format..."); + qmi_client_wda_set_data_format (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_data_format_ready, + NULL); + qmi_message_wda_set_data_format_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDA_GET_DATA_FORMAT + if (get_data_format_flag) { + g_autoptr(QmiMessageWdaGetDataFormatInput) input = NULL; + + if (get_data_format_str) { + input = get_data_format_input_create (get_data_format_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + } + + g_debug ("Asynchronously getting data format..."); + qmi_client_wda_get_data_format (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_data_format_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDA_GET_SUPPORTED_MESSAGES + if (get_supported_messages_flag) { + g_debug ("Asynchronously getting supported WDA messages..."); + qmi_client_wda_get_supported_messages (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_supported_messages_ready, + NULL); + return; + } +#endif + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_WDA */ diff --git a/pkgs/qmi-nmea/qmicli-wds.c b/pkgs/qmi-nmea/qmicli-wds.c new file mode 100644 index 0000000..0bae3b4 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-wds.c @@ -0,0 +1,3751 @@ +/* -*- 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) 2015 Velocloud Inc. + * Copyright (C) 2012-2017 Aleksander Morgado + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_WDS + +#undef VALIDATE_MASK_NONE +#define VALIDATE_MASK_NONE(str) (str ? str : "none") + +#define QMI_WDS_MUX_ID_UNDEFINED 0xFF +#define QMI_WDS_ENDPOINT_INTERFACE_NUMBER_UNDEFINED -1 + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientWds *client; + GCancellable *cancellable; + + /* Helpers for the wds-start-network command */ + gulong network_started_id; + guint packet_status_timeout_id; + guint32 packet_data_handle; +} Context; +static Context *ctx; + +/* Options */ +static gchar *start_network_str; +static gboolean follow_network_flag; +static gchar *stop_network_str; +static gboolean get_current_settings_flag; +static gboolean get_packet_service_status_flag; +static gboolean get_packet_statistics_flag; +static gboolean get_data_bearer_technology_flag; +static gboolean get_current_data_bearer_technology_flag; +static gboolean go_dormant_flag; +static gboolean go_active_flag; +static gboolean get_dormancy_status_flag; +static gchar *create_profile_str; +static gchar *swi_create_profile_indexed_str; +static gchar *modify_profile_str; +static gchar *delete_profile_str; +static gchar *get_profile_list_str; +static gchar *get_default_profile_num_str; /* deprecated */ +static gchar *get_default_profile_number_str; +static gchar *set_default_profile_num_str; /* deprecated */ +static gchar *set_default_profile_number_str; +static gchar *get_default_settings_str; +static gboolean get_autoconnect_settings_flag; +static gchar *set_autoconnect_settings_str; +static gboolean get_supported_messages_flag; +static gboolean reset_flag; +static gboolean noop_flag; +static gchar *bind_data_port_str; +static gchar *bind_mux_str; +static gchar *set_ip_family_str; +static gboolean get_channel_rates_flag; +static gboolean get_lte_attach_parameters_flag; +static gboolean get_max_lte_attach_pdn_number_flag; +static gboolean get_lte_attach_pdn_list_flag; +static gchar *set_lte_attach_pdn_list_str; + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_WDS_START_NETWORK + { "wds-start-network", 0, 0, G_OPTION_ARG_STRING, &start_network_str, + "Start network (allowed keys: apn, 3gpp-profile, 3gpp2-profile, auth (PAP|CHAP|BOTH), username, password, autoconnect=yes, ip-type (4|6))", + "[\"key=value,...\"]" + }, +# if defined HAVE_QMI_MESSAGE_WDS_STOP_NETWORK && defined HAVE_QMI_MESSAGE_WDS_GET_PACKET_SERVICE_STATUS + { "wds-follow-network", 0, 0, G_OPTION_ARG_NONE, &follow_network_flag, + "Follow the network status until disconnected. Use with `--wds-start-network'", + NULL + }, +# endif +#endif +#if defined HAVE_QMI_MESSAGE_WDS_STOP_NETWORK + { "wds-stop-network", 0, 0, G_OPTION_ARG_STRING, &stop_network_str, + "Stop network", + "[Packet data handle] OR [disable-autoconnect]", + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GET_CURRENT_SETTINGS + { "wds-get-current-settings", 0, 0, G_OPTION_ARG_NONE, &get_current_settings_flag, + "Get current settings", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GET_PACKET_SERVICE_STATUS + { "wds-get-packet-service-status", 0, 0, G_OPTION_ARG_NONE, &get_packet_service_status_flag, + "Get packet service status", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GET_PACKET_STATISTICS + { "wds-get-packet-statistics", 0, 0, G_OPTION_ARG_NONE, &get_packet_statistics_flag, + "Get packet statistics", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GET_DATA_BEARER_TECHNOLOGY + { "wds-get-data-bearer-technology", 0, 0, G_OPTION_ARG_NONE, &get_data_bearer_technology_flag, + "Get data bearer technology", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GET_CURRENT_DATA_BEARER_TECHNOLOGY + { "wds-get-current-data-bearer-technology", 0, 0, G_OPTION_ARG_NONE, &get_current_data_bearer_technology_flag, + "Get current data bearer technology", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GO_DORMANT + { "wds-go-dormant", 0, 0, G_OPTION_ARG_NONE, &go_dormant_flag, + "Make the active data connection go dormant", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GO_ACTIVE + { "wds-go-active", 0, 0, G_OPTION_ARG_NONE, &go_active_flag, + "Make the active data connection go active", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GET_DORMANCY_STATUS + { "wds-get-dormancy-status", 0, 0, G_OPTION_ARG_NONE, &get_dormancy_status_flag, + "Get the dormancy status of the active data connection", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_CREATE_PROFILE + { "wds-create-profile", 0, 0, G_OPTION_ARG_STRING, &create_profile_str, + "Create new profile using first available profile index (optional keys: name, apn, pdp-type (IP|PPP|IPV6|IPV4V6), auth (NONE|PAP|CHAP|BOTH), username, password, context-num, no-roaming=yes, disabled=yes)", + "[\"(3gpp|3gpp2)[,key=value,...]\"]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_SWI_CREATE_PROFILE_INDEXED + { "wds-swi-create-profile-indexed", 0, 0, G_OPTION_ARG_STRING, &swi_create_profile_indexed_str, + "Create new profile at specified profile index [Sierra Wireless specific] (optional keys: name, apn, pdp-type (IP|PPP|IPV6|IPV4V6), auth (NONE|PAP|CHAP|BOTH), username, password, context-num, no-roaming=yes, disabled=yes)", + "[\"(3gpp|3gpp2),#[,key=value,...]\"]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_MODIFY_PROFILE + { "wds-modify-profile", 0, 0, G_OPTION_ARG_STRING, &modify_profile_str, + "Modify existing profile (optional keys: name, apn, pdp-type (IP|PPP|IPV6|IPV4V6), auth (NONE|PAP|CHAP|BOTH), username, password, context-num, no-roaming=yes, disabled=yes)", + "[\"(3gpp|3gpp2),#,key=value,...\"]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_DELETE_PROFILE + { "wds-delete-profile", 0, 0, G_OPTION_ARG_STRING, &delete_profile_str, + "Delete existing profile", + "[(3gpp|3gpp2),#]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GET_PROFILE_LIST && defined HAVE_QMI_MESSAGE_WDS_GET_PROFILE_SETTINGS + { "wds-get-profile-list", 0, 0, G_OPTION_ARG_STRING, &get_profile_list_str, + "Get profile list", + "[3gpp|3gpp2]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GET_DEFAULT_PROFILE_NUMBER + { "wds-get-default-profile-number", 0, 0, G_OPTION_ARG_STRING, &get_default_profile_number_str, + "Get default profile number", + "[3gpp|3gpp2]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_SET_DEFAULT_PROFILE_NUMBER + { "wds-set-default-profile-number", 0, 0, G_OPTION_ARG_STRING, &set_default_profile_number_str, + "Set default profile number", + "[(3gpp|3gpp2),#]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GET_DEFAULT_SETTINGS + { "wds-get-default-settings", 0, 0, G_OPTION_ARG_STRING, &get_default_settings_str, + "Get default settings", + "[3gpp|3gpp2]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GET_AUTOCONNECT_SETTINGS + { "wds-get-autoconnect-settings", 0, 0, G_OPTION_ARG_NONE, &get_autoconnect_settings_flag, + "Get autoconnect settings", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_SET_AUTOCONNECT_SETTINGS + { "wds-set-autoconnect-settings", 0, 0, G_OPTION_ARG_STRING, &set_autoconnect_settings_str, + "Set autoconnect settings (roaming settings optional)", + "[(enabled|disabled|paused)[,(roaming-allowed|home-only)]]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GET_SUPPORTED_MESSAGES + { "wds-get-supported-messages", 0, 0, G_OPTION_ARG_NONE, &get_supported_messages_flag, + "Get supported messages", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_RESET + { "wds-reset", 0, 0, G_OPTION_ARG_NONE, &reset_flag, + "Reset the service state", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_BIND_DATA_PORT + { "wds-bind-data-port", 0, 0, G_OPTION_ARG_STRING, &bind_data_port_str, + "Bind data port to controller device to be used with `--client-no-release-cid'", + "[a2-mux-rmnet0-7|#]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_BIND_MUX_DATA_PORT + { "wds-bind-mux-data-port", 0, 0, G_OPTION_ARG_STRING, &bind_mux_str, + "Bind qmux data port to controller device (allowed keys: mux-id, ep-type (undefined|hsusb|pcie|embedded|bam-dmux), ep-iface-number) to be used with `--client-no-release-cid'", + "[\"key=value,...\"]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_SET_IP_FAMILY + { "wds-set-ip-family", 0, 0, G_OPTION_ARG_STRING, &set_ip_family_str, + "Set IP family", + "[4|6]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GET_CHANNEL_RATES + { "wds-get-channel-rates", 0, 0, G_OPTION_ARG_NONE, &get_channel_rates_flag, + "Get channel data rates", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GET_LTE_ATTACH_PARAMETERS + { "wds-get-lte-attach-parameters", 0, 0, G_OPTION_ARG_NONE, &get_lte_attach_parameters_flag, + "Get LTE attach parameters", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GET_MAX_LTE_ATTACH_PDN_NUMBER + { "wds-get-max-lte-attach-pdn-num", 0, 0, G_OPTION_ARG_NONE, &get_max_lte_attach_pdn_number_flag, + "Get the maximum number of LTE attach PDN", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_GET_LTE_ATTACH_PDN_LIST + { "wds-get-lte-attach-pdn-list", 0, 0, G_OPTION_ARG_NONE, &get_lte_attach_pdn_list_flag, + "Get the list of LTE attach PDN", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_SET_LTE_ATTACH_PDN_LIST + { "wds-set-lte-attach-pdn-list", 0, 0, G_OPTION_ARG_STRING, &set_lte_attach_pdn_list_str, + "Set the list of LTE attach PDN", + "[#,#,...]" + }, +#endif + { "wds-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a WDS client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + /* deprecated entries (hidden in --help) */ +#if defined HAVE_QMI_MESSAGE_WDS_GET_DEFAULT_PROFILE_NUMBER + { "wds-get-default-profile-num", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &get_default_profile_num_str, + "Get default profile number", + "[3gpp|3gpp2]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_WDS_SET_DEFAULT_PROFILE_NUMBER + { "wds-set-default-profile-num", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &set_default_profile_num_str, + "Set default profile number", + "[(3gpp|3gpp2),#]" + }, +#endif + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_wds_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("wds", + "WDS options:", + "Show Wireless Data Service options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_wds_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (!!start_network_str + + !!stop_network_str + + !!bind_data_port_str + + !!bind_mux_str + + !!set_ip_family_str + + get_current_settings_flag + + get_packet_service_status_flag + + get_packet_statistics_flag + + get_data_bearer_technology_flag + + get_current_data_bearer_technology_flag + + go_dormant_flag + + go_active_flag + + get_dormancy_status_flag + + !!create_profile_str + + !!swi_create_profile_indexed_str + + !!modify_profile_str + + !!delete_profile_str + + !!get_profile_list_str + + !!get_default_profile_num_str + + !!get_default_profile_number_str + + !!set_default_profile_num_str + + !!set_default_profile_number_str + + !!get_default_settings_str + + get_autoconnect_settings_flag + + !!set_autoconnect_settings_str + + get_supported_messages_flag + + reset_flag + + !!get_channel_rates_flag + + get_lte_attach_parameters_flag + + get_max_lte_attach_pdn_number_flag + + get_lte_attach_pdn_list_flag + + !!set_lte_attach_pdn_list_str + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many WDS actions requested\n"); + exit (EXIT_FAILURE); + } else if (n_actions == 0 && + follow_network_flag) { + g_printerr ("error: `--wds-follow-network' must be used with `--wds-start-network'\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->client) + g_object_unref (context->client); + if (context->network_started_id) + g_cancellable_disconnect (context->cancellable, context->network_started_id); + if (context->packet_status_timeout_id) + g_source_remove (context->packet_status_timeout_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); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_WDS_START_NETWORK || defined HAVE_QMI_MESSAGE_WDS_STOP_NETWORK + +static void +stop_network_ready (QmiClientWds *client, + GAsyncResult *res) +{ + GError *error = NULL; + QmiMessageWdsStopNetworkOutput *output; + + output = qmi_client_wds_stop_network_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_wds_stop_network_output_get_result (output, &error)) { + g_printerr ("error: couldn't stop network: %s\n", error->message); + g_error_free (error); + qmi_message_wds_stop_network_output_unref (output); + operation_shutdown (FALSE); + return; + } + +#undef VALIDATE_UNKNOWN +#define VALIDATE_UNKNOWN(str) (str ? str : "unknown") + + g_print ("[%s] Network stopped\n", + qmi_device_get_path_display (ctx->device)); + qmi_message_wds_stop_network_output_unref (output); + operation_shutdown (TRUE); +} + +static void +internal_stop_network (GCancellable *cancellable, + guint32 packet_data_handle, + gboolean disable_autoconnect) +{ + QmiMessageWdsStopNetworkInput *input; + + input = qmi_message_wds_stop_network_input_new (); + qmi_message_wds_stop_network_input_set_packet_data_handle (input, packet_data_handle, NULL); + if (disable_autoconnect) + qmi_message_wds_stop_network_input_set_disable_autoconnect (input, TRUE, NULL); + + g_print ("Network cancelled... releasing resources\n"); + qmi_client_wds_stop_network (ctx->client, + input, + 120, + ctx->cancellable, + (GAsyncReadyCallback)stop_network_ready, + NULL); + qmi_message_wds_stop_network_input_unref (input); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_START_NETWORK + * HAVE_QMI_MESSAGE_WDS_STOP_NETWORK */ + +#if defined HAVE_QMI_MESSAGE_WDS_START_NETWORK + +#if defined HAVE_QMI_MESSAGE_WDS_STOP_NETWORK && defined HAVE_QMI_MESSAGE_WDS_GET_PACKET_SERVICE_STATUS + +static void +network_cancelled (GCancellable *cancellable) +{ + ctx->network_started_id = 0; + + /* Remove the timeout right away */ + if (ctx->packet_status_timeout_id) { + g_source_remove (ctx->packet_status_timeout_id); + ctx->packet_status_timeout_id = 0; + } + + g_print ("Network cancelled... releasing resources\n"); + internal_stop_network (cancellable, ctx->packet_data_handle, FALSE); +} + +static void +timeout_get_packet_service_status_ready (QmiClientWds *client, + GAsyncResult *res) +{ + GError *error = NULL; + QmiMessageWdsGetPacketServiceStatusOutput *output; + QmiWdsConnectionStatus status; + + output = qmi_client_wds_get_packet_service_status_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", + error->message); + g_error_free (error); + return; + } + + if (!qmi_message_wds_get_packet_service_status_output_get_result (output, &error)) { + g_printerr ("error: couldn't get packet service status: %s\n", error->message); + g_error_free (error); + qmi_message_wds_get_packet_service_status_output_unref (output); + return; + } + + qmi_message_wds_get_packet_service_status_output_get_connection_status ( + output, + &status, + NULL); + + g_print ("[%s] Connection status: '%s'\n", + qmi_device_get_path_display (ctx->device), + qmi_wds_connection_status_get_string (status)); + qmi_message_wds_get_packet_service_status_output_unref (output); + + /* If packet service checks detect disconnection, halt --wds-follow-network */ + if (status != QMI_WDS_CONNECTION_STATUS_CONNECTED) { + g_print ("[%s] Stopping after detecting disconnection\n", + qmi_device_get_path_display (ctx->device)); + internal_stop_network (NULL, ctx->packet_data_handle, FALSE); + } +} + +static gboolean +packet_status_timeout (void) +{ + qmi_client_wds_get_packet_service_status (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)timeout_get_packet_service_status_ready, + NULL); + + return TRUE; +} + +#endif /* HAVE_QMI_MESSAGE_WDS_STOP_NETWORK + * HAVE_QMI_MESSAGE_WDS_GET_PACKET_SERVICE_STATUS */ + +typedef struct { + gchar *apn; + guint8 profile_index_3gpp; + guint8 profile_index_3gpp2; + QmiWdsAuthentication auth; + gboolean auth_set; + QmiWdsIpFamily ip_type; + gchar *username; + gchar *password; + gboolean autoconnect; + gboolean autoconnect_set; +} StartNetworkProperties; + +static gboolean +start_network_properties_handle (const gchar *key, + const gchar *value, + GError **error, + gpointer user_data) +{ + StartNetworkProperties *props = user_data; + + if (!value || !value[0]) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "key '%s' required a value", + key); + return FALSE; + } + + if (g_ascii_strcasecmp (key, "apn") == 0 && !props->apn) { + props->apn = g_strdup (value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "3gpp-profile") == 0 && !props->profile_index_3gpp) { + props->profile_index_3gpp = atoi (value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "3gpp2-profile") == 0 && !props->profile_index_3gpp2) { + props->profile_index_3gpp2 = atoi (value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "auth") == 0 && !props->auth_set) { + if (!qmicli_read_authentication_from_string (value, &(props->auth))) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unknown auth protocol '%s'", + value); + return FALSE; + } + props->auth_set = TRUE; + return TRUE; + } + + if (g_ascii_strcasecmp (key, "username") == 0 && !props->username) { + props->username = g_strdup (value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "password") == 0 && !props->password) { + props->password = g_strdup (value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "autoconnect") == 0 && !props->autoconnect_set) { + if (!qmicli_read_yes_no_from_string (value, &(props->autoconnect))) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unknown autoconnect setup '%s'", + value); + return FALSE; + } + props->autoconnect_set = TRUE; + return TRUE; + } + + if (g_ascii_strcasecmp (key, "ip-type") == 0 && props->ip_type == QMI_WDS_IP_FAMILY_UNSPECIFIED) { + switch (atoi (value)) { + case 4: + props->ip_type = QMI_WDS_IP_FAMILY_IPV4; + break; + case 6: + props->ip_type = QMI_WDS_IP_FAMILY_IPV6; + break; + default: + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unknown IP type '%s' (not 4 or 6)", + value); + return FALSE; + } + return TRUE; + } + + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unrecognized or duplicate option '%s'", + key); + return FALSE; +} + +static gboolean +start_network_input_create (const gchar *str, + QmiMessageWdsStartNetworkInput **input, + GError **error) +{ + g_autofree gchar *aux_auth_str = NULL; + const gchar *ip_type_str = NULL; + gchar **split = NULL; + StartNetworkProperties props = { + .auth = QMI_WDS_AUTHENTICATION_NONE, + .ip_type = QMI_WDS_IP_FAMILY_UNSPECIFIED, + }; + gboolean success = FALSE; + + g_assert (input && !*input); + + /* An empty string is totally valid (i.e. no TLVs) */ + if (!str[0]) + return TRUE; + + /* New key=value format */ + if (strchr (str, '=')) { + GError *parse_error = NULL; + + if (!qmicli_parse_key_value_string (str, + &parse_error, + start_network_properties_handle, + &props)) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "couldn't parse input string: %s", + parse_error->message); + g_error_free (parse_error); + goto out; + } + } + /* Old non key=value format, like this: + * "[(APN),(PAP|CHAP|BOTH),(Username),(Password)]" + */ + else { + /* Parse input string into the expected fields */ + split = g_strsplit (str, ",", 0); + + props.apn = g_strdup (split[0]); + + if (props.apn && split[1]) { + if (!qmicli_read_authentication_from_string (split[1], &(props.auth))) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unknown auth protocol '%s'", + split[1]); + goto out; + } + props.auth_set = TRUE; + } + + props.username = (props.auth_set ? g_strdup (split[2]) : NULL); + props.password = (props.username ? g_strdup (split[3]) : NULL); + } + + /* Create input bundle */ + *input = qmi_message_wds_start_network_input_new (); + + /* Set APN */ + if (props.apn) + qmi_message_wds_start_network_input_set_apn (*input, props.apn, NULL); + + /* Set 3GPP profile */ + if (props.profile_index_3gpp > 0) + qmi_message_wds_start_network_input_set_profile_index_3gpp (*input, props.profile_index_3gpp, NULL); + + /* Set 3GPP2 profile */ + if (props.profile_index_3gpp2 > 0) + qmi_message_wds_start_network_input_set_profile_index_3gpp2 (*input, props.profile_index_3gpp2, NULL); + + /* Set IP Type */ + if (props.ip_type != 0) { + qmi_message_wds_start_network_input_set_ip_family_preference (*input, props.ip_type, NULL); + if (props.ip_type == QMI_WDS_IP_FAMILY_IPV4) + ip_type_str = "4"; + else if (props.ip_type == QMI_WDS_IP_FAMILY_IPV6) + ip_type_str = "6"; + } + + /* Set authentication method */ + if (props.auth_set) { + aux_auth_str = qmi_wds_authentication_build_string_from_mask (props.auth); + qmi_message_wds_start_network_input_set_authentication_preference (*input, props.auth, NULL); + } + + /* Set username, avoid empty strings */ + if (props.username && props.username[0]) + qmi_message_wds_start_network_input_set_username (*input, props.username, NULL); + + /* Set password, avoid empty strings */ + if (props.password && props.password[0]) + qmi_message_wds_start_network_input_set_password (*input, props.password, NULL); + + /* Set autoconnect */ + if (props.autoconnect_set) + qmi_message_wds_start_network_input_set_enable_autoconnect (*input, props.autoconnect, NULL); + + success = TRUE; + + g_debug ("Network start parameters set (apn: '%s', 3gpp_profile: '%u', 3gpp2_profile: '%u', auth: '%s', ip-type: '%s', username: '%s', password: '%s', autoconnect: '%s')", + props.apn ? props.apn : "unspecified", + props.profile_index_3gpp, + props.profile_index_3gpp2, + aux_auth_str ? aux_auth_str : "unspecified", + ip_type_str ? ip_type_str : "unspecified", + (props.username && props.username[0]) ? props.username : "unspecified", + (props.password && props.password[0]) ? props.password : "unspecified", + props.autoconnect_set ? (props.autoconnect ? "yes" : "no") : "unspecified"); + +out: + g_strfreev (split); + g_free (props.apn); + g_free (props.username); + g_free (props.password); + + return success; +} + +static void +start_network_ready (QmiClientWds *client, + GAsyncResult *res) +{ + GError *error = NULL; + QmiMessageWdsStartNetworkOutput *output; + + output = qmi_client_wds_start_network_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_wds_start_network_output_get_result (output, &error)) { + g_printerr ("error: couldn't start network: %s\n", error->message); + if (g_error_matches (error, + QMI_PROTOCOL_ERROR, + QMI_PROTOCOL_ERROR_CALL_FAILED)) { + QmiWdsCallEndReason cer; + QmiWdsVerboseCallEndReasonType verbose_cer_type; + gint16 verbose_cer_reason; + + if (qmi_message_wds_start_network_output_get_call_end_reason ( + output, + &cer, + NULL)) + g_printerr ("call end reason (%u): %s\n", + cer, + qmi_wds_call_end_reason_get_string (cer)); + + if (qmi_message_wds_start_network_output_get_verbose_call_end_reason ( + output, + &verbose_cer_type, + &verbose_cer_reason, + NULL)) + g_printerr ("verbose call end reason (%u,%d): [%s] %s\n", + verbose_cer_type, + verbose_cer_reason, + qmi_wds_verbose_call_end_reason_type_get_string (verbose_cer_type), + qmi_wds_verbose_call_end_reason_get_string (verbose_cer_type, verbose_cer_reason)); + } + + g_error_free (error); + qmi_message_wds_start_network_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_wds_start_network_output_get_packet_data_handle (output, &ctx->packet_data_handle, NULL); + qmi_message_wds_start_network_output_unref (output); + +#undef VALIDATE_UNKNOWN +#define VALIDATE_UNKNOWN(str) (str ? str : "unknown") + + g_print ("[%s] Network started\n" + "\tPacket data handle: '%u'\n", + qmi_device_get_path_display (ctx->device), + (guint)ctx->packet_data_handle); + +#if defined HAVE_QMI_MESSAGE_WDS_STOP_NETWORK && defined HAVE_QMI_MESSAGE_WDS_GET_PACKET_SERVICE_STATUS + if (follow_network_flag) { + g_print ("\nCtrl+C will stop the network\n"); + ctx->network_started_id = g_cancellable_connect (ctx->cancellable, + G_CALLBACK (network_cancelled), + NULL, + NULL); + + ctx->packet_status_timeout_id = g_timeout_add_seconds (20, + (GSourceFunc)packet_status_timeout, + NULL); + return; + } +#endif + + /* Nothing else to do */ + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_START_NETWORK */ + +#if defined HAVE_QMI_MESSAGE_WDS_GET_CURRENT_SETTINGS + +static void +get_current_settings_ready (QmiClientWds *client, + GAsyncResult *res) +{ + GError *error = NULL; + QmiMessageWdsGetCurrentSettingsOutput *output; + QmiWdsIpFamily ip_family = QMI_WDS_IP_FAMILY_UNSPECIFIED; + guint32 mtu = 0; + GArray *array; + guint32 addr = 0; + struct in_addr in_addr_val; + struct in6_addr in6_addr_val; + gchar buf4[INET_ADDRSTRLEN]; + gchar buf6[INET6_ADDRSTRLEN]; + guint8 prefix = 0; + guint i; + + output = qmi_client_wds_get_current_settings_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_wds_get_current_settings_output_get_result (output, &error)) { + g_printerr ("error: couldn't get current settings: %s\n", error->message); + g_error_free (error); + qmi_message_wds_get_current_settings_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Current settings retrieved:\n", + qmi_device_get_path_display (ctx->device)); + + if (qmi_message_wds_get_current_settings_output_get_ip_family (output, &ip_family, NULL)) + g_print (" IP Family: %s\n", + ((ip_family == QMI_WDS_IP_FAMILY_IPV4) ? "IPv4" : + ((ip_family == QMI_WDS_IP_FAMILY_IPV6) ? "IPv6" : + "unknown"))); + + /* IPv4... */ + + if (qmi_message_wds_get_current_settings_output_get_ipv4_address (output, &addr, NULL)) { + in_addr_val.s_addr = GUINT32_TO_BE (addr); + memset (buf4, 0, sizeof (buf4)); + inet_ntop (AF_INET, &in_addr_val, buf4, sizeof (buf4)); + g_print (" IPv4 address: %s\n", buf4); + } + + if (qmi_message_wds_get_current_settings_output_get_ipv4_gateway_subnet_mask (output, &addr, NULL)) { + in_addr_val.s_addr = GUINT32_TO_BE (addr); + memset (buf4, 0, sizeof (buf4)); + inet_ntop (AF_INET, &in_addr_val, buf4, sizeof (buf4)); + g_print (" IPv4 subnet mask: %s\n", buf4); + } + + if (qmi_message_wds_get_current_settings_output_get_ipv4_gateway_address (output, &addr, NULL)) { + in_addr_val.s_addr = GUINT32_TO_BE (addr); + memset (buf4, 0, sizeof (buf4)); + inet_ntop (AF_INET, &in_addr_val, buf4, sizeof (buf4)); + g_print ("IPv4 gateway address: %s\n", buf4); + } + + if (qmi_message_wds_get_current_settings_output_get_primary_ipv4_dns_address (output, &addr, NULL)) { + in_addr_val.s_addr = GUINT32_TO_BE (addr); + memset (buf4, 0, sizeof (buf4)); + inet_ntop (AF_INET, &in_addr_val, buf4, sizeof (buf4)); + g_print (" IPv4 primary DNS: %s\n", buf4); + } + + if (qmi_message_wds_get_current_settings_output_get_secondary_ipv4_dns_address (output, &addr, NULL)) { + in_addr_val.s_addr = GUINT32_TO_BE (addr); + memset (buf4, 0, sizeof (buf4)); + inet_ntop (AF_INET, &in_addr_val, buf4, sizeof (buf4)); + g_print (" IPv4 secondary DNS: %s\n", buf4); + } + + /* IPv6... */ + + if (qmi_message_wds_get_current_settings_output_get_ipv6_address (output, &array, &prefix, NULL)) { + for (i = 0; i < array->len; i++) + in6_addr_val.s6_addr16[i] = GUINT16_TO_BE (g_array_index (array, guint16, i)); + memset (buf6, 0, sizeof (buf6)); + inet_ntop (AF_INET6, &in6_addr_val, buf6, sizeof (buf6)); + g_print (" IPv6 address: %s/%d\n", buf6, prefix); + } + + if (qmi_message_wds_get_current_settings_output_get_ipv6_gateway_address (output, &array, &prefix, NULL)) { + for (i = 0; i < array->len; i++) + in6_addr_val.s6_addr16[i] = GUINT16_TO_BE (g_array_index (array, guint16, i)); + memset (buf6, 0, sizeof (buf6)); + inet_ntop (AF_INET6, &in6_addr_val, buf6, sizeof (buf6)); + g_print ("IPv6 gateway address: %s/%d\n", buf6, prefix); + } + + if (qmi_message_wds_get_current_settings_output_get_ipv6_primary_dns_address (output, &array, NULL)) { + for (i = 0; i < array->len; i++) + in6_addr_val.s6_addr16[i] = GUINT16_TO_BE (g_array_index (array, guint16, i)); + memset (buf6, 0, sizeof (buf6)); + inet_ntop (AF_INET6, &in6_addr_val, buf6, sizeof (buf6)); + g_print (" IPv6 primary DNS: %s\n", buf6); + } + + if (qmi_message_wds_get_current_settings_output_get_ipv6_secondary_dns_address (output, &array, NULL)) { + for (i = 0; i < array->len; i++) + in6_addr_val.s6_addr16[i] = GUINT16_TO_BE (g_array_index (array, guint16, i)); + memset (buf6, 0, sizeof (buf6)); + inet_ntop (AF_INET6, &in6_addr_val, buf6, sizeof (buf6)); + g_print (" IPv6 secondary DNS: %s\n", buf6); + } + + /* Other... */ + + if (qmi_message_wds_get_current_settings_output_get_mtu (output, &mtu, NULL)) + g_print (" MTU: %u\n", mtu); + + if (qmi_message_wds_get_current_settings_output_get_domain_name_list (output, &array, &error)) { + GString *s = NULL; + + if (array) { + for (i = 0; i < array->len; i++) { + if (!s) + s = g_string_new (""); + else + g_string_append (s, ", "); + g_string_append (s, g_array_index (array, const gchar *, i)); + } + } + if (s) { + g_print (" Domains: %s\n", s->str); + g_string_free (s, TRUE); + } else + g_print (" Domains: none\n"); + } + + qmi_message_wds_get_current_settings_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GET_CURRENT_SETTINGS */ + +#if defined HAVE_QMI_MESSAGE_WDS_GET_PACKET_SERVICE_STATUS + +static void +get_packet_service_status_ready (QmiClientWds *client, + GAsyncResult *res) +{ + GError *error = NULL; + QmiMessageWdsGetPacketServiceStatusOutput *output; + QmiWdsConnectionStatus status; + + output = qmi_client_wds_get_packet_service_status_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_wds_get_packet_service_status_output_get_result (output, &error)) { + g_printerr ("error: couldn't get packet service status: %s\n", error->message); + g_error_free (error); + qmi_message_wds_get_packet_service_status_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_wds_get_packet_service_status_output_get_connection_status ( + output, + &status, + NULL); + + g_print ("[%s] Connection status: '%s'\n", + qmi_device_get_path_display (ctx->device), + qmi_wds_connection_status_get_string (status)); + + qmi_message_wds_get_packet_service_status_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GET_PACKET_SERVICE_STATUS */ + +#if defined HAVE_QMI_MESSAGE_WDS_GET_PACKET_STATISTICS + +static void +get_packet_statistics_ready (QmiClientWds *client, + GAsyncResult *res) +{ + GError *error = NULL; + QmiMessageWdsGetPacketStatisticsOutput *output; + guint32 val32; + guint64 val64; + + output = qmi_client_wds_get_packet_statistics_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_wds_get_packet_statistics_output_get_result (output, &error)) { + g_printerr ("error: couldn't get packet statistics: %s\n", error->message); + g_error_free (error); + qmi_message_wds_get_packet_statistics_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Connection statistics:\n", + qmi_device_get_path_display (ctx->device)); + + if (qmi_message_wds_get_packet_statistics_output_get_tx_packets_ok (output, &val32, NULL) && + val32 != 0xFFFFFFFF) + g_print ("\tTX packets OK: %u\n", val32); + if (qmi_message_wds_get_packet_statistics_output_get_rx_packets_ok (output, &val32, NULL) && + val32 != 0xFFFFFFFF) + g_print ("\tRX packets OK: %u\n", val32); + if (qmi_message_wds_get_packet_statistics_output_get_tx_packets_error (output, &val32, NULL) && + val32 != 0xFFFFFFFF) + g_print ("\tTX packets error: %u\n", val32); + if (qmi_message_wds_get_packet_statistics_output_get_rx_packets_error (output, &val32, NULL) && + val32 != 0xFFFFFFFF) + g_print ("\tRX packets error: %u\n", val32); + if (qmi_message_wds_get_packet_statistics_output_get_tx_overflows (output, &val32, NULL) && + val32 != 0xFFFFFFFF) + g_print ("\tTX overflows: %u\n", val32); + if (qmi_message_wds_get_packet_statistics_output_get_rx_overflows (output, &val32, NULL) && + val32 != 0xFFFFFFFF) + g_print ("\tRX overflows: %u\n", val32); + if (qmi_message_wds_get_packet_statistics_output_get_tx_packets_dropped (output, &val32, NULL) && + val32 != 0xFFFFFFFF) + g_print ("\tTX packets dropped: %u\n", val32); + if (qmi_message_wds_get_packet_statistics_output_get_rx_packets_dropped (output, &val32, NULL) && + val32 != 0xFFFFFFFF) + g_print ("\tRX packets dropped: %u\n", val32); + + if (qmi_message_wds_get_packet_statistics_output_get_tx_bytes_ok (output, &val64, NULL)) + g_print ("\tTX bytes OK: %" G_GUINT64_FORMAT "\n", val64); + if (qmi_message_wds_get_packet_statistics_output_get_rx_bytes_ok (output, &val64, NULL)) + g_print ("\tRX bytes OK: %" G_GUINT64_FORMAT "\n", val64); + if (qmi_message_wds_get_packet_statistics_output_get_last_call_tx_bytes_ok (output, &val64, NULL)) + g_print ("\tTX bytes OK (last): %" G_GUINT64_FORMAT "\n", val64); + if (qmi_message_wds_get_packet_statistics_output_get_last_call_rx_bytes_ok (output, &val64, NULL)) + g_print ("\tRX bytes OK (last): %" G_GUINT64_FORMAT "\n", val64); + + qmi_message_wds_get_packet_statistics_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GET_PACKET_STATISTICS */ + +#if defined HAVE_QMI_MESSAGE_WDS_GET_DATA_BEARER_TECHNOLOGY + +static void +get_data_bearer_technology_ready (QmiClientWds *client, + GAsyncResult *res) +{ + GError *error = NULL; + QmiMessageWdsGetDataBearerTechnologyOutput *output; + QmiWdsDataBearerTechnology current; + + output = qmi_client_wds_get_data_bearer_technology_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_wds_get_data_bearer_technology_output_get_result (output, &error)) { + g_printerr ("error: couldn't get data bearer technology: %s\n", error->message); + + if (g_error_matches (error, + QMI_PROTOCOL_ERROR, + QMI_PROTOCOL_ERROR_OUT_OF_CALL)) { + QmiWdsDataBearerTechnology last = QMI_WDS_DATA_BEARER_TECHNOLOGY_UNKNOWN; + + if (qmi_message_wds_get_data_bearer_technology_output_get_last ( + output, + &last, + NULL)) + g_print ("[%s] Data bearer technology (last): '%s'(%d)\n", + qmi_device_get_path_display (ctx->device), + qmi_wds_data_bearer_technology_get_string (last), last); + } + + g_error_free (error); + qmi_message_wds_get_data_bearer_technology_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_wds_get_data_bearer_technology_output_get_current ( + output, + ¤t, + NULL); + g_print ("[%s] Data bearer technology (current): '%s'\n", + qmi_device_get_path_display (ctx->device), + qmi_wds_data_bearer_technology_get_string (current)); + qmi_message_wds_get_data_bearer_technology_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GET_DATA_BEARER_TECHNOLOGY */ + +#if defined HAVE_QMI_MESSAGE_WDS_GET_CURRENT_DATA_BEARER_TECHNOLOGY + +static void +print_current_data_bearer_technology_results (const gchar *which, + QmiWdsNetworkType network_type, + guint32 rat_mask, + guint32 so_mask) +{ + g_autofree gchar *rat_string = NULL; + g_autofree gchar *so_string = NULL; + + if (network_type == QMI_WDS_NETWORK_TYPE_3GPP2) { + rat_string = qmi_wds_rat_3gpp2_build_string_from_mask (rat_mask); + if (rat_mask & QMI_WDS_RAT_3GPP2_CDMA1X) + so_string = qmi_wds_so_cdma1x_build_string_from_mask (so_mask); + else if (rat_mask & QMI_WDS_RAT_3GPP2_EVDO_REVA) + so_string = qmi_wds_so_evdo_reva_build_string_from_mask (so_mask); + } else if (network_type == QMI_WDS_NETWORK_TYPE_3GPP) { + rat_string = qmi_wds_rat_3gpp_build_string_from_mask (rat_mask); + } + + g_print ("[%s] Data bearer technology (%s):\n" + " Network type: '%s'\n" + " Radio Access Technology: '%s'\n", + qmi_device_get_path_display (ctx->device), + which, + qmi_wds_network_type_get_string (network_type), + VALIDATE_MASK_NONE (rat_string)); + + if (network_type == QMI_WDS_NETWORK_TYPE_3GPP2) + g_print (" Service Option: '%s'\n", + VALIDATE_MASK_NONE (so_string)); +} + +static void +get_current_data_bearer_technology_ready (QmiClientWds *client, + GAsyncResult *res) +{ + GError *error = NULL; + QmiMessageWdsGetCurrentDataBearerTechnologyOutput *output; + QmiWdsNetworkType network_type; + guint32 rat_mask; + guint32 so_mask; + + output = qmi_client_wds_get_current_data_bearer_technology_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", + error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + +#undef VALIDATE_UNKNOWN +#define VALIDATE_UNKNOWN(str) (str ? str : "unknown") + + if (!qmi_message_wds_get_current_data_bearer_technology_output_get_result (output, &error)) { + g_printerr ("error: couldn't get current data bearer technology: %s\n", error->message); + + if (qmi_message_wds_get_current_data_bearer_technology_output_get_last ( + output, + &network_type, + &rat_mask, + &so_mask, + NULL)) { + print_current_data_bearer_technology_results ( + "last", + network_type, + rat_mask, + so_mask); + } + + g_error_free (error); + qmi_message_wds_get_current_data_bearer_technology_output_unref (output); + operation_shutdown (FALSE); + return; + } + + /* Retrieve CURRENT */ + if (qmi_message_wds_get_current_data_bearer_technology_output_get_current ( + output, + &network_type, + &rat_mask, + &so_mask, + NULL)) { + print_current_data_bearer_technology_results ( + "current", + network_type, + rat_mask, + so_mask); + } + + qmi_message_wds_get_current_data_bearer_technology_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GET_CURRENT_DATA_BEARER_TECHNOLOGY */ + +#if defined HAVE_QMI_MESSAGE_WDS_GO_DORMANT + +static void +go_dormant_ready (QmiClientWds *client, + GAsyncResult *res) +{ + GError *error = NULL; + QmiMessageWdsGoDormantOutput *output; + + output = qmi_client_wds_go_dormant_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_wds_go_dormant_output_get_result (output, &error)) { + g_printerr ("error: couldn't go dormant: %s\n", error->message); + g_error_free (error); + qmi_message_wds_go_dormant_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_wds_go_dormant_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GO_DORMANT */ + +#if defined HAVE_QMI_MESSAGE_WDS_GO_ACTIVE + +static void +go_active_ready (QmiClientWds *client, + GAsyncResult *res) +{ + GError *error = NULL; + QmiMessageWdsGoActiveOutput *output; + + output = qmi_client_wds_go_active_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_wds_go_active_output_get_result (output, &error)) { + g_printerr ("error: couldn't go active: %s\n", error->message); + g_error_free (error); + qmi_message_wds_go_active_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_wds_go_active_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GO_ACTIVE */ + +#if defined HAVE_QMI_MESSAGE_WDS_GET_DORMANCY_STATUS + +static void +get_dormancy_status_ready (QmiClientWds *client, + GAsyncResult *res) +{ + GError *error = NULL; + QmiMessageWdsGetDormancyStatusOutput *output; + QmiWdsDormancyStatus status; + + output = qmi_client_wds_get_dormancy_status_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_wds_get_dormancy_status_output_get_result (output, &error)) { + g_printerr ("error: couldn't get dormancy status: %s\n", error->message); + g_error_free (error); + qmi_message_wds_get_dormancy_status_output_unref (output); + operation_shutdown (FALSE); + return; + } + + if (qmi_message_wds_get_dormancy_status_output_get_dormancy_status ( + output, + &status, + NULL)) { + g_print ("[%s] Dormancy Status: '%s'\n", + qmi_device_get_path_display (ctx->device), + qmi_wds_dormancy_status_get_string (status)); + } + + qmi_message_wds_get_dormancy_status_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GET_DORMANCY_STATUS */ + +#if defined HAVE_QMI_MESSAGE_WDS_CREATE_PROFILE || \ + defined HAVE_QMI_MESSAGE_WDS_SWI_CREATE_PROFILE_INDEXED || \ + defined HAVE_QMI_MESSAGE_WDS_MODIFY_PROFILE + +typedef struct { + QmiWdsProfileType profile_type; + guint profile_index; + guint context_number; + gchar *name; + QmiWdsPdpType pdp_type; + gboolean pdp_type_set; + QmiWdsApnTypeMask apn_type; + gboolean apn_type_set; + gchar *apn; + gchar *username; + gchar *password; + QmiWdsAuthentication auth; + gboolean auth_set; + gboolean no_roaming; + gboolean no_roaming_set; + gboolean disabled; + gboolean disabled_set; +} CreateModifyProfileProperties; + +static gboolean +create_modify_profile_properties_handle (const gchar *key, + const gchar *value, + GError **error, + gpointer user_data) +{ + CreateModifyProfileProperties *props = user_data; + + /* Allow empty values for string parameters */ + + if (g_ascii_strcasecmp (key, "name") == 0 && !props->name) { + props->name = g_strdup (value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "apn") == 0 && !props->apn) { + props->apn = g_strdup (value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "username") == 0 && !props->username) { + props->username = g_strdup (value); + return TRUE; + } + + if (g_ascii_strcasecmp (key, "password") == 0 && !props->password) { + props->password = g_strdup (value); + return TRUE; + } + + /* all other TLVs do require a value... */ + if (!value || !value[0]) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "key '%s' required a value", + key); + return FALSE; + } + + if (g_ascii_strcasecmp (key, "context-num") == 0 && !props->context_number) { + props->context_number = atoi (value); + if (props->context_number <= 0 || props->context_number > G_MAXUINT8) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "invalid or out of range context number [1,%u]: '%s'", + G_MAXUINT8, + value); + return FALSE; + } + return TRUE; + } + + if (g_ascii_strcasecmp (key, "auth") == 0 && !props->auth_set) { + if (!qmicli_read_authentication_from_string (value, &(props->auth))) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unknown auth protocol '%s'", + value); + return FALSE; + } + props->auth_set = TRUE; + return TRUE; + } + + if (g_ascii_strcasecmp (key, "pdp-type") == 0 && !props->pdp_type_set) { + if (!qmicli_read_pdp_type_from_string (value, &(props->pdp_type))) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unknown pdp type '%s'", + value); + return FALSE; + } + props->pdp_type_set = TRUE; + return TRUE; + } + + if (g_ascii_strcasecmp (key, "apn-type-mask") == 0 && !props->apn_type_set) { + if (!qmicli_read_wds_apn_type_mask_from_string (value, &(props->apn_type))) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unknown apn type '%s'", + value); + return FALSE; + } + props->apn_type_set = TRUE; + return TRUE; + } + + if (g_ascii_strcasecmp (key, "no-roaming") == 0 && !props->no_roaming_set) { + if (!qmicli_read_yes_no_from_string (value, &(props->no_roaming))) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unknown 'no-roaming' value '%s'", + value); + return FALSE; + } + props->no_roaming_set = TRUE; + return TRUE; + } + + if (g_ascii_strcasecmp (key, "disabled") == 0 && !props->disabled_set) { + if (!qmicli_read_yes_no_from_string (value, &(props->disabled))) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unknown 'disabled' value '%s'", + value); + return FALSE; + } + props->disabled_set = TRUE; + return TRUE; + } + + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unrecognized or duplicate option '%s'", + key); + return FALSE; +} + +#endif /* HAVE_QMI_MESSAGE_WDS_CREATE_PROFILE + * HAVE_QMI_MESSAGE_WDS_SWI_CREATE_PROFILE_INDEXED + * HAVE_QMI_MESSAGE_WDS_MODIFY_PROFILE */ + +#if defined HAVE_QMI_MESSAGE_WDS_CREATE_PROFILE + +static gboolean +create_profile_input_create (const gchar *str, + QmiMessageWdsCreateProfileInput **input, + GError **error) +{ + GError *parse_error = NULL; + CreateModifyProfileProperties props = {}; + gboolean success = FALSE; + gchar **split; + + g_assert (input && !*input); + + split = g_strsplit (str, ",", 2); + if (g_strv_length (split) < 1) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "expected at least 1 arguments for 'wds create profile' command"); + goto out; + } + + g_strstrip (split[0]); + if (g_str_equal (split[0], "3gpp")) + props.profile_type = QMI_WDS_PROFILE_TYPE_3GPP; + else if (g_str_equal (split[0], "3gpp2")) + props.profile_type = QMI_WDS_PROFILE_TYPE_3GPP2; + else { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "invalid profile type. Expected '3gpp' or '3gpp2'.'"); + goto out; + } + + if (split[1]) { + if (!qmicli_parse_key_value_string (split[1], + &parse_error, + create_modify_profile_properties_handle, + &props)) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "couldn't parse input string: %s", + parse_error->message); + g_error_free (parse_error); + goto out; + } + } + + /* Create input bundle */ + *input = qmi_message_wds_create_profile_input_new (); + + /* Profile type is required */ + qmi_message_wds_create_profile_input_set_profile_type (*input, props.profile_type, NULL); + + if (props.context_number) + qmi_message_wds_create_profile_input_set_pdp_context_number (*input, props.context_number, NULL); + + if (props.pdp_type_set) + qmi_message_wds_create_profile_input_set_pdp_type (*input, props.pdp_type, NULL); + + if (props.apn_type_set) + qmi_message_wds_create_profile_input_set_apn_type_mask (*input, props.apn_type, NULL); + + if (props.name) + qmi_message_wds_create_profile_input_set_profile_name (*input, props.name, NULL); + + if (props.apn) + qmi_message_wds_create_profile_input_set_apn_name (*input, props.apn, NULL); + + if (props.auth_set) + qmi_message_wds_create_profile_input_set_authentication (*input, props.auth, NULL); + + if (props.username) + qmi_message_wds_create_profile_input_set_username (*input, props.username, NULL); + + if (props.password) + qmi_message_wds_create_profile_input_set_password (*input, props.password, NULL); + + if (props.no_roaming_set) + qmi_message_wds_create_profile_input_set_roaming_disallowed_flag (*input, props.no_roaming, NULL); + + if (props.disabled_set) + qmi_message_wds_create_profile_input_set_apn_disabled_flag (*input, props.disabled, NULL); + + success = TRUE; + +out: + g_strfreev (split); + g_free (props.name); + g_free (props.apn); + g_free (props.username); + g_free (props.password); + + return success; +} + +static void +create_profile_ready (QmiClientWds *client, + GAsyncResult *res) +{ + QmiMessageWdsCreateProfileOutput *output; + GError *error = NULL; + QmiWdsProfileType profile_type; + guint8 profile_index; + + output = qmi_client_wds_create_profile_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_wds_create_profile_output_get_result (output, &error)) { + QmiWdsDsProfileError ds_profile_error; + + if (g_error_matches (error, + QMI_PROTOCOL_ERROR, + QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) && + qmi_message_wds_create_profile_output_get_extended_error_code ( + output, + &ds_profile_error, + NULL)) { + g_printerr ("error: couldn't create profile: ds profile error: %s\n", + qmi_wds_ds_profile_error_get_string (ds_profile_error)); + } else { + g_printerr ("error: couldn't create profile: %s\n", + error->message); + } + g_error_free (error); + qmi_message_wds_create_profile_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("New profile created:\n"); + if (qmi_message_wds_create_profile_output_get_profile_identifier (output, + &profile_type, + &profile_index, + NULL)) { + g_print ("\tProfile type: '%s'\n", qmi_wds_profile_type_get_string(profile_type)); + g_print ("\tProfile index: '%d'\n", profile_index); + } + qmi_message_wds_create_profile_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_CREATE_PROFILE */ + +#if defined HAVE_QMI_MESSAGE_WDS_SWI_CREATE_PROFILE_INDEXED + +static gboolean +swi_create_profile_indexed_input_create (const gchar *str, + QmiMessageWdsSwiCreateProfileIndexedInput **input, + GError **error) +{ + GError *parse_error = NULL; + CreateModifyProfileProperties props = {}; + gchar **split; + gboolean success = FALSE; + + g_assert (input && !*input); + + /* Expect max 3 tokens: the first two give us the mandatory parameters of the command, + * the 3rd one will contain the key/value pair list */ + split = g_strsplit (str, ",", 3); + if (g_strv_length (split) < 2) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "expected at least 2 arguments for 'wds swi create profile indexed' command"); + goto out; + } + + g_strstrip (split[0]); + if (g_str_equal (split[0], "3gpp")) + props.profile_type = QMI_WDS_PROFILE_TYPE_3GPP; + else if (g_str_equal (split[0], "3gpp2")) + props.profile_type = QMI_WDS_PROFILE_TYPE_3GPP2; + else { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "invalid profile type '%s'. Expected '3gpp' or '3gpp2'.'", + split[0]); + goto out; + } + + g_strstrip (split[1]); + props.profile_index = atoi (split[1]); + if (props.profile_index <= 0 || props.profile_index > G_MAXUINT8) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "invalid or out of range profile index [1,%u]: '%s'\n", + G_MAXUINT8, + split[1]); + goto out; + } + + if (split[2]) { + if (!qmicli_parse_key_value_string (split[2], + &parse_error, + create_modify_profile_properties_handle, + &props)) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "couldn't parse input string: %s", + parse_error->message); + g_error_free (parse_error); + goto out; + } + } + + /* Create input bundle */ + *input = qmi_message_wds_swi_create_profile_indexed_input_new (); + + /* Profile identifier is required */ + qmi_message_wds_swi_create_profile_indexed_input_set_profile_identifier (*input, props.profile_type, props.profile_index, NULL); + + if (props.context_number) + qmi_message_wds_swi_create_profile_indexed_input_set_pdp_context_number (*input, props.context_number, NULL); + + if (props.pdp_type_set) + qmi_message_wds_swi_create_profile_indexed_input_set_pdp_type (*input, props.pdp_type, NULL); + + if (props.name) + qmi_message_wds_swi_create_profile_indexed_input_set_profile_name (*input, props.name, NULL); + + if (props.apn) + qmi_message_wds_swi_create_profile_indexed_input_set_apn_name (*input, props.apn, NULL); + + if (props.auth_set) + qmi_message_wds_swi_create_profile_indexed_input_set_authentication (*input, props.auth, NULL); + + if (props.username) + qmi_message_wds_swi_create_profile_indexed_input_set_username (*input, props.username, NULL); + + if (props.password) + qmi_message_wds_swi_create_profile_indexed_input_set_password (*input, props.password, NULL); + + if (props.no_roaming_set) + qmi_message_wds_swi_create_profile_indexed_input_set_roaming_disallowed_flag (*input, props.no_roaming, NULL); + + if (props.disabled_set) + qmi_message_wds_swi_create_profile_indexed_input_set_apn_disabled_flag (*input, props.disabled, NULL); + + success = TRUE; + +out: + g_strfreev (split); + g_free (props.name); + g_free (props.apn); + g_free (props.username); + g_free (props.password); + + return success; +} + +static void +swi_create_profile_indexed_ready (QmiClientWds *client, + GAsyncResult *res) +{ + QmiMessageWdsSwiCreateProfileIndexedOutput *output; + GError *error = NULL; + QmiWdsProfileType profile_type; + guint8 profile_index; + + output = qmi_client_wds_swi_create_profile_indexed_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_wds_swi_create_profile_indexed_output_get_result (output, &error)) { + g_printerr ("error: couldn't create indexed profile: %s\n", error->message); + g_error_free (error); + qmi_message_wds_swi_create_profile_indexed_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("New profile created:\n"); + if (qmi_message_wds_swi_create_profile_indexed_output_get_profile_identifier (output, + &profile_type, + &profile_index, + NULL)) { + g_print ("\tProfile type: '%s'\n", qmi_wds_profile_type_get_string(profile_type)); + g_print ("\tProfile index: '%d'\n", profile_index); + } + qmi_message_wds_swi_create_profile_indexed_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_SWI_CREATE_PROFILE_INDEXED */ + +#if defined HAVE_QMI_MESSAGE_WDS_MODIFY_PROFILE + +static gboolean +modify_profile_input_create (const gchar *str, + QmiMessageWdsModifyProfileInput **input, + GError **error) +{ + GError *parse_error = NULL; + CreateModifyProfileProperties props = {}; + gchar **split; + gboolean success = FALSE; + + g_assert (input && !*input); + + /* Expect max 3 tokens: the first two give us the mandatory parameters of the command, + * the 3rd one will contain the key/value pair list */ + split = g_strsplit (str, ",", 3); + if (g_strv_length (split) < 3) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "expected at least 3 arguments for 'wds modify profile' command"); + goto out; + } + + g_strstrip (split[0]); + if (g_str_equal (split[0], "3gpp")) + props.profile_type = QMI_WDS_PROFILE_TYPE_3GPP; + else if (g_str_equal (split[0], "3gpp2")) + props.profile_type = QMI_WDS_PROFILE_TYPE_3GPP2; + else { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "invalid profile type '%s'. Expected '3gpp' or '3gpp2'.'", + split[0]); + goto out; + } + + g_strstrip (split[1]); + props.profile_index = atoi (split[1]); + if (props.profile_index <= 0 || props.profile_index > G_MAXUINT8) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "invalid or out of range profile index [1,%u]: '%s'\n", + G_MAXUINT8, + split[1]); + goto out; + } + + /* advance to third token, that's where key/value pairs start */ + if (!qmicli_parse_key_value_string (split[2], + &parse_error, + create_modify_profile_properties_handle, + &props)) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "couldn't parse input string: %s", + parse_error->message); + g_error_free (parse_error); + goto out; + } + + /* Create input bundle */ + *input = qmi_message_wds_modify_profile_input_new (); + + /* Profile identifier is required */ + qmi_message_wds_modify_profile_input_set_profile_identifier (*input, props.profile_type, props.profile_index, NULL); + + if (props.context_number) + qmi_message_wds_modify_profile_input_set_pdp_context_number (*input, props.context_number, NULL); + + if (props.pdp_type_set) + qmi_message_wds_modify_profile_input_set_pdp_type (*input, props.pdp_type, NULL); + + if (props.apn_type_set) + qmi_message_wds_modify_profile_input_set_apn_type_mask (*input, props.apn_type, NULL); + + if (props.name) + qmi_message_wds_modify_profile_input_set_profile_name (*input, props.name, NULL); + + if (props.apn) + qmi_message_wds_modify_profile_input_set_apn_name (*input, props.apn, NULL); + + if (props.auth_set) + qmi_message_wds_modify_profile_input_set_authentication (*input, props.auth, NULL); + + if (props.username) + qmi_message_wds_modify_profile_input_set_username (*input, props.username, NULL); + + if (props.password) + qmi_message_wds_modify_profile_input_set_password (*input, props.password, NULL); + + if (props.no_roaming_set) + qmi_message_wds_modify_profile_input_set_roaming_disallowed_flag (*input, props.no_roaming, NULL); + + if (props.disabled_set) + qmi_message_wds_modify_profile_input_set_apn_disabled_flag (*input, props.disabled, NULL); + + success = TRUE; + +out: + g_strfreev (split); + g_free (props.name); + g_free (props.apn); + g_free (props.username); + g_free (props.password); + + return success; +} + +static void +modify_profile_ready (QmiClientWds *client, + GAsyncResult *res) +{ + QmiMessageWdsModifyProfileOutput *output; + GError *error = NULL; + + output = qmi_client_wds_modify_profile_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_wds_modify_profile_output_get_result (output, &error)) { + QmiWdsDsProfileError ds_profile_error; + + if (g_error_matches (error, + QMI_PROTOCOL_ERROR, + QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) && + qmi_message_wds_modify_profile_output_get_extended_error_code ( + output, + &ds_profile_error, + NULL)) { + g_printerr ("error: couldn't modify profile: ds profile error: %s\n", + qmi_wds_ds_profile_error_get_string (ds_profile_error)); + } else { + g_printerr ("error: couldn't modify profile: %s\n", + error->message); + } + g_error_free (error); + qmi_message_wds_modify_profile_output_unref (output); + operation_shutdown (FALSE); + return; + } + qmi_message_wds_modify_profile_output_unref (output); + g_print ("Profile successfully modified.\n"); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_MODIFY_PROFILE */ + +#if defined HAVE_QMI_MESSAGE_WDS_DELETE_PROFILE + +static void +delete_profile_ready (QmiClientWds *client, + GAsyncResult *res) +{ + QmiMessageWdsDeleteProfileOutput *output; + GError *error = NULL; + + output = qmi_client_wds_delete_profile_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_wds_delete_profile_output_get_result (output, &error)) { + QmiWdsDsProfileError ds_profile_error; + + if (g_error_matches (error, + QMI_PROTOCOL_ERROR, + QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) && + qmi_message_wds_delete_profile_output_get_extended_error_code ( + output, + &ds_profile_error, + NULL)) { + g_printerr ("error: couldn't delete profile: ds profile error: %s\n", + qmi_wds_ds_profile_error_get_string (ds_profile_error)); + } else { + g_printerr ("error: couldn't delete profile: %s\n", + error->message); + } + g_error_free (error); + qmi_message_wds_delete_profile_output_unref (output); + operation_shutdown (FALSE); + return; + } + qmi_message_wds_delete_profile_output_unref (output); + g_print ("Profile successfully deleted.\n"); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_DELETE_PROFILE */ + +#if defined HAVE_QMI_MESSAGE_WDS_GET_PROFILE_LIST && defined HAVE_QMI_MESSAGE_WDS_GET_PROFILE_SETTINGS + +typedef struct { + guint i; + GArray *profile_list; +} GetProfileListContext; + +static void get_next_profile_settings (GetProfileListContext *inner_ctx); + +static void +get_profile_settings_ready (QmiClientWds *client, + GAsyncResult *res, + GetProfileListContext *inner_ctx) +{ + QmiMessageWdsGetProfileSettingsOutput *output; + GError *error = NULL; + + output = qmi_client_wds_get_profile_settings_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + g_error_free (error); + } else if (!qmi_message_wds_get_profile_settings_output_get_result (output, &error)) { + QmiWdsDsProfileError ds_profile_error; + + if (g_error_matches (error, + QMI_PROTOCOL_ERROR, + QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) && + qmi_message_wds_get_profile_settings_output_get_extended_error_code ( + output, + &ds_profile_error, + NULL)) { + g_printerr ("error: couldn't get profile settings: ds profile error: %s\n", + qmi_wds_ds_profile_error_get_string (ds_profile_error)); + } else { + g_printerr ("error: couldn't get profile settings: %s\n", + error->message); + } + g_error_free (error); + qmi_message_wds_get_profile_settings_output_unref (output); + } else { + const gchar *str; + guint8 context_number; + QmiWdsPdpType pdp_type; + QmiWdsAuthentication auth; + QmiWdsApnTypeMask apn_type; + gboolean flag; + + if (qmi_message_wds_get_profile_settings_output_get_apn_name (output, &str, NULL)) + g_print ("\t\tAPN: '%s'\n", str); + if (qmi_message_wds_get_profile_settings_output_get_apn_type_mask (output, &apn_type, NULL)) { + g_autofree gchar *aux = NULL; + + aux = qmi_wds_apn_type_mask_build_string_from_mask (apn_type); + g_print ("\t\tAPN type: '%s'\n", VALIDATE_MASK_NONE (aux)); + } + if (qmi_message_wds_get_profile_settings_output_get_pdp_type (output, &pdp_type, NULL)) + g_print ("\t\tPDP type: '%s'\n", qmi_wds_pdp_type_get_string (pdp_type)); + if (qmi_message_wds_get_profile_settings_output_get_pdp_context_number (output, &context_number, NULL)) + g_print ("\t\tPDP context number: '%d'\n", context_number); + if (qmi_message_wds_get_profile_settings_output_get_username (output, &str, NULL)) + g_print ("\t\tUsername: '%s'\n", str); + if (qmi_message_wds_get_profile_settings_output_get_password (output, &str, NULL)) + g_print ("\t\tPassword: '%s'\n", str); + if (qmi_message_wds_get_profile_settings_output_get_authentication (output, &auth, NULL)) { + g_autofree gchar *aux = NULL; + + aux = qmi_wds_authentication_build_string_from_mask (auth); + g_print ("\t\tAuth: '%s'\n", VALIDATE_MASK_NONE (aux)); + } + if (qmi_message_wds_get_profile_settings_output_get_roaming_disallowed_flag (output, &flag, NULL)) + g_print ("\t\tNo roaming: '%s'\n", flag ? "yes" : "no"); + if (qmi_message_wds_get_profile_settings_output_get_apn_disabled_flag (output, &flag, NULL)) + g_print ("\t\tAPN disabled: '%s'\n", flag ? "yes" : "no"); + qmi_message_wds_get_profile_settings_output_unref (output); + } + + /* Keep on */ + inner_ctx->i++; + get_next_profile_settings (inner_ctx); +} + +static void +get_next_profile_settings (GetProfileListContext *inner_ctx) +{ + QmiMessageWdsGetProfileListOutputProfileListProfile *profile; + QmiMessageWdsGetProfileSettingsInput *input; + + if (inner_ctx->i >= inner_ctx->profile_list->len) { + /* All done */ + g_array_unref (inner_ctx->profile_list); + g_slice_free (GetProfileListContext, inner_ctx); + operation_shutdown (TRUE); + return; + } + + profile = &g_array_index (inner_ctx->profile_list, QmiMessageWdsGetProfileListOutputProfileListProfile, inner_ctx->i); + g_print ("\t[%u] %s - %s\n", + profile->profile_index, + qmi_wds_profile_type_get_string (profile->profile_type), + profile->profile_name); + + input = qmi_message_wds_get_profile_settings_input_new (); + qmi_message_wds_get_profile_settings_input_set_profile_id ( + input, + profile->profile_type, + profile->profile_index, + NULL); + qmi_client_wds_get_profile_settings (ctx->client, + input, + 3, + NULL, + (GAsyncReadyCallback)get_profile_settings_ready, + inner_ctx); + qmi_message_wds_get_profile_settings_input_unref (input); +} + +static void +get_profile_list_ready (QmiClientWds *client, + GAsyncResult *res) +{ + GError *error = NULL; + QmiMessageWdsGetProfileListOutput *output; + GetProfileListContext *inner_ctx; + GArray *profile_list = NULL; + + output = qmi_client_wds_get_profile_list_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_wds_get_profile_list_output_get_result (output, &error)) { + QmiWdsDsProfileError ds_profile_error; + + if (g_error_matches (error, + QMI_PROTOCOL_ERROR, + QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) && + qmi_message_wds_get_profile_list_output_get_extended_error_code ( + output, + &ds_profile_error, + NULL)) { + g_printerr ("error: couldn't get profile list: ds profile error: %s\n", + qmi_wds_ds_profile_error_get_string (ds_profile_error)); + } else { + g_printerr ("error: couldn't get profile list: %s\n", + error->message); + } + + g_error_free (error); + qmi_message_wds_get_profile_list_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_wds_get_profile_list_output_get_profile_list (output, &profile_list, NULL); + + if (!profile_list || !profile_list->len) { + g_print ("Profile list empty\n"); + qmi_message_wds_get_profile_list_output_unref (output); + operation_shutdown (TRUE); + return; + } + + g_print ("Profile list retrieved:\n"); + + inner_ctx = g_slice_new (GetProfileListContext); + inner_ctx->profile_list = g_array_ref (profile_list); + inner_ctx->i = 0; + get_next_profile_settings (inner_ctx); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GET_PROFILE_LIST + * HAVE_QMI_MESSAGE_WDS_GET_PROFILE_SETTINGS */ + +#if defined HAVE_QMI_MESSAGE_WDS_GET_DEFAULT_SETTINGS + +static void +get_default_settings_ready (QmiClientWds *client, + GAsyncResult *res) +{ + QmiMessageWdsGetDefaultSettingsOutput *output; + GError *error = NULL; + const gchar *str; + QmiWdsPdpType pdp_type; + QmiWdsAuthentication auth; + + output = qmi_client_wds_get_default_settings_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_wds_get_default_settings_output_get_result (output, &error)) { + QmiWdsDsProfileError ds_profile_error; + + if (g_error_matches (error, + QMI_PROTOCOL_ERROR, + QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) && + qmi_message_wds_get_default_settings_output_get_extended_error_code ( + output, + &ds_profile_error, + NULL)) { + g_printerr ("error: couldn't get default settings: ds default error: %s\n", + qmi_wds_ds_profile_error_get_string (ds_profile_error)); + } else { + g_printerr ("error: couldn't get default settings: %s\n", + error->message); + } + g_error_free (error); + qmi_message_wds_get_default_settings_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("Default settings retrieved:\n"); + + if (qmi_message_wds_get_default_settings_output_get_apn_name (output, &str, NULL)) + g_print ("\tAPN: '%s'\n", str); + if (qmi_message_wds_get_default_settings_output_get_pdp_type (output, &pdp_type, NULL)) + g_print ("\tPDP type: '%s'\n", qmi_wds_pdp_type_get_string (pdp_type)); + if (qmi_message_wds_get_default_settings_output_get_username (output, &str, NULL)) + g_print ("\tUsername: '%s'\n", str); + if (qmi_message_wds_get_default_settings_output_get_password (output, &str, NULL)) + g_print ("\tPassword: '%s'\n", str); + if (qmi_message_wds_get_default_settings_output_get_authentication (output, &auth, NULL)) { + g_autofree gchar *aux = NULL; + + aux = qmi_wds_authentication_build_string_from_mask (auth); + g_print ("\tAuth: '%s'\n", VALIDATE_MASK_NONE (aux)); + } + + qmi_message_wds_get_default_settings_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GET_DEFAULT_SETTINGS */ + +#if defined HAVE_QMI_MESSAGE_WDS_GET_DEFAULT_PROFILE_NUMBER + +static void +get_default_profile_number_ready (QmiClientWds *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageWdsGetDefaultProfileNumberOutput) output = NULL; + g_autoptr(GError) error = NULL; + guint8 profile_num; + + output = qmi_client_wds_get_default_profile_number_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_wds_get_default_profile_number_output_get_result (output, &error)) { + QmiWdsDsProfileError ds_profile_error; + + if (g_error_matches (error, + QMI_PROTOCOL_ERROR, + QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) && + qmi_message_wds_get_default_profile_number_output_get_extended_error_code ( + output, + &ds_profile_error, + NULL)) { + g_printerr ("error: couldn't get default profile number: ds profile error: %s\n", + qmi_wds_ds_profile_error_get_string (ds_profile_error)); + } else { + g_printerr ("error: couldn't get default profile number: %s\n", + error->message); + } + operation_shutdown (FALSE); + return; + } + + g_print ("Default profile number retrieved:\n"); + if (qmi_message_wds_get_default_profile_number_output_get_index (output, &profile_num, NULL)) + g_print ("\tDefault profile number: '%d'\n", profile_num); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GET_DEFAULT_PROFILE_NUMBER */ + +#if defined HAVE_QMI_MESSAGE_WDS_SET_DEFAULT_PROFILE_NUMBER + +static void +set_default_profile_number_ready (QmiClientWds *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageWdsSetDefaultProfileNumberOutput) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_wds_set_default_profile_number_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_wds_set_default_profile_number_output_get_result (output, &error)) { + QmiWdsDsProfileError ds_profile_error; + + if (g_error_matches (error, + QMI_PROTOCOL_ERROR, + QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) && + qmi_message_wds_set_default_profile_number_output_get_extended_error_code ( + output, + &ds_profile_error, + NULL)) { + g_printerr ("error: couldn't set default settings: ds profile error: %s\n", + qmi_wds_ds_profile_error_get_string (ds_profile_error)); + } else { + g_printerr ("error: couldn't set default settings: %s\n", + error->message); + } + operation_shutdown (FALSE); + return; + } + + g_print ("Default profile number updated\n"); + operation_shutdown (TRUE); +} + +static QmiMessageWdsSetDefaultProfileNumberInput * +set_default_profile_number_input_create (const gchar *str) +{ + g_autoptr(QmiMessageWdsSetDefaultProfileNumberInput) input = NULL; + g_autoptr(GError) error = NULL; + g_auto(GStrv) split = NULL; + QmiWdsProfileType profile_type; + guint profile_num; + + split = g_strsplit (str, ",", -1); + input = qmi_message_wds_set_default_profile_number_input_new (); + + if (g_strv_length (split) != 2) { + g_printerr ("error: expected 2 options in default profile number settings\n"); + return NULL; + } + + g_strstrip (split[0]); + if (g_str_equal (split[0], "3gpp")) + profile_type = QMI_WDS_PROFILE_TYPE_3GPP; + else if (g_str_equal (split[0], "3gpp2")) + profile_type = QMI_WDS_PROFILE_TYPE_3GPP2; + else { + g_printerr ("error: invalid profile type '%s'. Expected '3gpp' or '3gpp2'.'\n", split[0]); + return NULL; + } + + g_strstrip (split[1]); + profile_num = atoi (split[1]); + if (profile_num <= 0 || profile_num > G_MAXUINT8) { + g_printerr ("error: invalid or out of range profile number [1,%u]: '%s'\n", + G_MAXUINT8, + split[1]); + return NULL; + } + + if (!qmi_message_wds_set_default_profile_number_input_set_profile_identifier ( + input, + profile_type, + QMI_WDS_PROFILE_FAMILY_TETHERED, + (guint8)profile_num, + &error)) { + g_printerr ("error: couldn't create input bundle: '%s'\n", error->message); + return NULL; + } + + return g_steal_pointer (&input); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_SET_DEFAULT_PROFILE_NUMBER */ + +#if defined HAVE_QMI_MESSAGE_WDS_GET_AUTOCONNECT_SETTINGS + +static void +get_autoconnect_settings_ready (QmiClientWds *client, + GAsyncResult *res) +{ + QmiMessageWdsGetAutoconnectSettingsOutput *output; + GError *error = NULL; + QmiWdsAutoconnectSetting status; + QmiWdsAutoconnectSettingRoaming roaming; + + output = qmi_client_wds_get_autoconnect_settings_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_wds_get_autoconnect_settings_output_get_result (output, &error)) { + g_printerr ("error: couldn't get autoconnect settings: %s\n", + error->message); + g_error_free (error); + qmi_message_wds_get_autoconnect_settings_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("Autoconnect settings retrieved:\n"); + + qmi_message_wds_get_autoconnect_settings_output_get_status (output, &status, NULL); + g_print ("\tStatus: '%s'\n", qmi_wds_autoconnect_setting_get_string (status)); + + if (qmi_message_wds_get_autoconnect_settings_output_get_roaming (output, &roaming, NULL)) + g_print ("\tRoaming: '%s'\n", qmi_wds_autoconnect_setting_roaming_get_string (roaming)); + + qmi_message_wds_get_autoconnect_settings_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GET_AUTOCONNECT_SETTINGS */ + +#if defined HAVE_QMI_MESSAGE_WDS_SET_AUTOCONNECT_SETTINGS + +static QmiMessageWdsSetAutoconnectSettingsInput * +set_autoconnect_settings_input_create (const gchar *str) +{ + QmiMessageWdsSetAutoconnectSettingsInput *input = NULL; + gchar **split; + QmiWdsAutoconnectSetting status; + QmiWdsAutoconnectSettingRoaming roaming; + GError *error = NULL; + + split = g_strsplit (str, ",", -1); + input = qmi_message_wds_set_autoconnect_settings_input_new (); + + if (g_strv_length (split) != 2 && g_strv_length (split) != 1) { + g_printerr ("error: expected 1 or 2 options in autoconnect settings\n"); + goto error_out; + } + + g_strstrip (split[0]); + if (!qmicli_read_wds_autoconnect_setting_from_string (split[0], &status)) { + g_printerr ("error: failed to parse autoconnect setting\n"); + goto error_out; + } + if (!qmi_message_wds_set_autoconnect_settings_input_set_status (input, status, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + goto error_out; + } + + if (g_strv_length (split) == 2) { + g_strstrip (split[1]); + if (!qmicli_read_wds_autoconnect_setting_roaming_from_string (g_str_equal (split[1], "roaming-allowed") ? "allowed" : split[1], &roaming)) { + g_printerr ("error: failed to parse autoconnect roaming setting\n"); + goto error_out; + } + if (!qmi_message_wds_set_autoconnect_settings_input_set_roaming (input, roaming, &error)) { + g_printerr ("error: couldn't create input data bundle: '%s'\n", + error->message); + goto error_out; + } + } + + g_strfreev (split); + return input; + +error_out: + if (error) + g_error_free (error); + g_strfreev (split); + qmi_message_wds_set_autoconnect_settings_input_unref (input); + return NULL; +} + +static void +set_autoconnect_settings_ready (QmiClientWds *client, + GAsyncResult *res) +{ + QmiMessageWdsSetAutoconnectSettingsOutput *output; + GError *error = NULL; + + output = qmi_client_wds_set_autoconnect_settings_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_wds_set_autoconnect_settings_output_get_result (output, &error)) { + g_printerr ("error: couldn't set autoconnect settings: %s\n", + error->message); + g_error_free (error); + qmi_message_wds_set_autoconnect_settings_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("Autoconnect settings updated\n"); + qmi_message_wds_set_autoconnect_settings_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_SET_AUTOCONNECT_SETTINGS */ + +#if defined HAVE_QMI_MESSAGE_WDS_GET_SUPPORTED_MESSAGES + +static void +get_supported_messages_ready (QmiClientWds *client, + GAsyncResult *res) +{ + QmiMessageWdsGetSupportedMessagesOutput *output; + GError *error = NULL; + GArray *bytearray = NULL; + gchar *str; + + output = qmi_client_wds_get_supported_messages_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_wds_get_supported_messages_output_get_result (output, &error)) { + g_printerr ("error: couldn't get supported WDS messages: %s\n", error->message); + g_error_free (error); + qmi_message_wds_get_supported_messages_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got supported WDS messages:\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_wds_get_supported_messages_output_get_list (output, &bytearray, NULL); + str = qmicli_get_supported_messages_list (bytearray ? (const guint8 *)bytearray->data : NULL, + bytearray ? bytearray->len : 0); + g_print ("%s", str); + g_free (str); + + qmi_message_wds_get_supported_messages_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GET_SUPPORTED_MESSAGES */ + +#if defined HAVE_QMI_MESSAGE_WDS_RESET + +static void +reset_ready (QmiClientWds *client, + GAsyncResult *res) +{ + QmiMessageWdsResetOutput *output; + GError *error = NULL; + + output = qmi_client_wds_reset_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_wds_reset_output_get_result (output, &error)) { + g_printerr ("error: couldn't reset the WDS service: %s\n", error->message); + g_error_free (error); + qmi_message_wds_reset_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully performed WDS service reset\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_wds_reset_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_RESET */ + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +#if defined HAVE_QMI_MESSAGE_WDS_BIND_DATA_PORT + +static QmiMessageWdsBindDataPortInput * +bind_data_port_input_create (const gchar *str) +{ + g_autoptr(QmiMessageWdsBindDataPortInput) input = NULL; + g_autoptr(GError) error = NULL; + QmiSioPort sio_port; + + sio_port = strtoul(str, NULL, 0); + if (!sio_port && !qmicli_read_sio_port_from_string (str, &sio_port)) + return NULL; + + input = qmi_message_wds_bind_data_port_input_new (); + if (!qmi_message_wds_bind_data_port_input_set_data_port (input, sio_port, &error)) { + g_printerr ("error: couldn't set data port: '%s'\n", error->message); + return NULL; + } + + return g_steal_pointer (&input); +} + +static void +bind_data_port_ready (QmiClientWds *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageWdsBindDataPortOutput) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_wds_bind_data_port_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_wds_bind_data_port_output_get_result (output, &error)) { + g_printerr ("error: couldn't bind data port: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_BIND_DATA_PORT */ + +#if defined HAVE_QMI_MESSAGE_WDS_BIND_MUX_DATA_PORT + +typedef struct { + guint8 mux_id; + QmiDataEndpointType ep_type; + gint ep_iface_number; + QmiWdsClientType client_type; +} BindMuxDataPortProperties; + +static gboolean +bind_mux_data_port_properties_handle (const gchar *key, + const gchar *value, + GError **error, + gpointer user_data) +{ + BindMuxDataPortProperties *props = user_data; + + if (!value || !value[0]) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "key '%s' requires a value", key); + return FALSE; + } + + if (g_ascii_strcasecmp (key, "mux-id") == 0) { + guint aux; + + /* QMI_WDS_MUX_ID_UNDEFINED is G_MAXUINT8 (0xff) */ + if (!qmicli_read_uint_from_string (value, &aux) || (aux >= G_MAXUINT8)) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "failed reading key 'mux-id' value in range [0,254]: '%s'", value); + return FALSE; + } + props->mux_id = (guint8) aux; + return TRUE; + } + + if (g_ascii_strcasecmp (key, "ep-type") == 0) { + if (!qmicli_read_data_endpoint_type_from_string (value, &(props->ep_type))) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "failed reading key 'ep-type' value: '%s'", value); + return FALSE; + } + return TRUE; + } + + if (g_ascii_strcasecmp (key, "ep-iface-number") == 0) { + guint aux; + + if (!qmicli_read_uint_from_string (value, &aux)) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "failed reading key 'ep-iface-number' value: '%s'", value); + return FALSE; + } + + props->ep_iface_number = (gint) aux; + return TRUE; + } + + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED, + "unrecognized key: '%s'", key); + return FALSE; +} + +static QmiMessageWdsBindMuxDataPortInput * +bind_mux_data_port_input_create (const gchar *str) +{ + QmiMessageWdsBindMuxDataPortInput *input = NULL; + GError *error = NULL; + BindMuxDataPortProperties props = { + .mux_id = QMI_WDS_MUX_ID_UNDEFINED, + .ep_type = QMI_DATA_ENDPOINT_TYPE_HSUSB, + .ep_iface_number = QMI_WDS_ENDPOINT_INTERFACE_NUMBER_UNDEFINED, + .client_type = QMI_WDS_CLIENT_TYPE_TETHERED, + }; + + if (!str[0]) + return NULL; + + if (strchr (str, '=')) { + if (!qmicli_parse_key_value_string (str, + &error, + bind_mux_data_port_properties_handle, + &props)) { + g_printerr ("error: could not parse input string '%s'\n", error->message); + g_error_free (error); + return NULL; + } + } else { + g_printerr ("error: malformed input string, key=value format expected.\n"); + goto error_out; + } + + + if ((props.mux_id == QMI_WDS_MUX_ID_UNDEFINED) || + (props.ep_iface_number == QMI_WDS_ENDPOINT_INTERFACE_NUMBER_UNDEFINED)) { + g_printerr ("error: Mux ID and Endpoint Iface Number are both needed\n"); + return NULL; + } + + input = qmi_message_wds_bind_mux_data_port_input_new (); + + if (!qmi_message_wds_bind_mux_data_port_input_set_endpoint_info (input, props.ep_type, props.ep_iface_number, &error)) { + g_printerr ("error: couldn't set endpoint info: '%s'\n", error->message); + goto error_out; + } + + if (!qmi_message_wds_bind_mux_data_port_input_set_mux_id (input, props.mux_id, &error)) { + g_printerr ("error: couldn't set mux ID %d: '%s'\n", props.mux_id, error->message); + goto error_out; + } + + if (!qmi_message_wds_bind_mux_data_port_input_set_client_type (input, props.client_type , &error)) { + g_printerr ("error: couldn't set client type: '%s'\n", error->message); + goto error_out; + } + + return input; + +error_out: + if (error) + g_error_free (error); + qmi_message_wds_bind_mux_data_port_input_unref (input); + return NULL; +} + +static void +bind_mux_data_port_ready (QmiClientWds *client, + GAsyncResult *res) { + QmiMessageWdsBindMuxDataPortOutput *output; + GError *error = NULL; + + output = qmi_client_wds_bind_mux_data_port_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_wds_bind_mux_data_port_output_get_result (output, &error)) { + g_printerr ("error: couldn't bind mux data port: %s\n", error->message); + g_error_free (error); + qmi_message_wds_bind_mux_data_port_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_wds_bind_mux_data_port_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_BIND_MUX_DATA_PORT */ + +#if defined HAVE_QMI_MESSAGE_WDS_SET_IP_FAMILY + +static void +set_ip_family_ready (QmiClientWds *client, + GAsyncResult *res) +{ + QmiMessageWdsSetIpFamilyOutput *output; + GError *error = NULL; + + output = qmi_client_wds_set_ip_family_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_wds_set_ip_family_output_get_result (output, &error)) { + g_printerr ("error: couldn't set IP family: %s\n", error->message); + g_error_free (error); + qmi_message_wds_set_ip_family_output_unref (output); + operation_shutdown (FALSE); + return; + } + + qmi_message_wds_set_ip_family_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_SET_IP_FAMILY */ + +#if defined HAVE_QMI_MESSAGE_WDS_GET_CHANNEL_RATES + +static void +get_channel_rates_ready (QmiClientWds *client, + GAsyncResult *res) +{ + QmiMessageWdsGetChannelRatesOutput *output; + guint32 txrate = 0, rxrate = 0, maxtxrate = 0, maxrxrate = 0; + GError *error = NULL; + + output = qmi_client_wds_get_channel_rates_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_wds_get_channel_rates_output_get_result (output, &error)) { + g_printerr ("error: couldn't get channel rates: %s\n", + error->message); + g_error_free (error); + qmi_message_wds_get_channel_rates_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("Channel data rates:\n"); + + qmi_message_wds_get_channel_rates_output_get_channel_rates (output, + &txrate, + &rxrate, + &maxtxrate, + &maxrxrate, + NULL); + + /* Current TX/RX rates may not be available if device is disconnected */ + + g_print ("\tCurrent TX rate: "); + if (txrate != QMI_WDS_RATE_UNAVAILABLE) + g_print ("%ubps\n", txrate); + else + g_print ("n/a\n"); + + g_print ("\tCurrent RX rate: "); + if (rxrate != QMI_WDS_RATE_UNAVAILABLE) + g_print ("%ubps\n", rxrate); + else + g_print ("n/a\n"); + + g_print ("\tMax TX rate: %ubps\n" + "\tMax RX rate: %ubps\n", + maxtxrate, + maxrxrate); + + qmi_message_wds_get_channel_rates_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GET_CHANNEL_RATES */ + +#if defined HAVE_QMI_MESSAGE_WDS_GET_LTE_ATTACH_PARAMETERS + +static void +get_lte_attach_parameters_ready (QmiClientWds *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageWdsGetLteAttachParametersOutput) output = NULL; + g_autoptr(GError) error = NULL; + const gchar *apn; + gboolean ota_attach_performed; + QmiWdsIpSupportType ip_support_type; + + output = qmi_client_wds_get_lte_attach_parameters_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_wds_get_lte_attach_parameters_output_get_result (output, &error)) { + g_printerr ("error: couldn't get LTE attach parameters: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("LTE attach parameters successfully retrieved:\n"); + if (qmi_message_wds_get_lte_attach_parameters_output_get_apn (output, &apn, NULL)) + g_print ("\tAPN: %s\n", apn); + if (qmi_message_wds_get_lte_attach_parameters_output_get_ip_support_type (output, &ip_support_type, NULL)) + g_print ("\tIP support type: %s\n", qmi_wds_ip_support_type_get_string (ip_support_type)); + if (qmi_message_wds_get_lte_attach_parameters_output_get_ota_attach_performed (output, &ota_attach_performed, NULL)) + g_print ("\tOTA attach performed: %s\n", ota_attach_performed ? "yes" : "no"); + + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GET_LTE_ATTACH_PARAMETERS */ + +#if defined HAVE_QMI_MESSAGE_WDS_GET_MAX_LTE_ATTACH_PDN_NUMBER + +static void +get_max_lte_attach_pdn_number_ready (QmiClientWds *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageWdsGetMaxLteAttachPdnNumberOutput) output = NULL; + g_autoptr(GError) error = NULL; + guint8 maxnum = 0; + + output = qmi_client_wds_get_max_lte_attach_pdn_number_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_wds_get_max_lte_attach_pdn_number_output_get_result (output, &error)) { + g_printerr ("error: couldn't get maximum number of attach PDN: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + qmi_message_wds_get_max_lte_attach_pdn_number_output_get_info (output, &maxnum, NULL); + g_print ("Maximum number of LTE attach PDN: %u\n", maxnum); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GET_MAX_LTE_ATTACH_PDN_NUMBER */ + +#if defined HAVE_QMI_MESSAGE_WDS_GET_LTE_ATTACH_PDN_LIST + +static void +get_lte_attach_pdn_list_ready (QmiClientWds *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageWdsGetLteAttachPdnListOutput) output = NULL; + g_autoptr(GError) error = NULL; + GArray *current_list = NULL; + GArray *pending_list = NULL; + guint i; + + output = qmi_client_wds_get_lte_attach_pdn_list_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_wds_get_lte_attach_pdn_list_output_get_result (output, &error)) { + g_printerr ("error: couldn't get the list of LTE attach PDN: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("Attach PDN list retrieved:\n"); + + qmi_message_wds_get_lte_attach_pdn_list_output_get_current_list (output, ¤t_list, NULL); + if (!current_list || !current_list->len) { + g_print ("\tCurrent list: n/a\n"); + } else { + g_print ("\tCurrent list: '"); + for (i = 0; i < current_list->len; i++) + g_print ("%s%u", i > 0 ? ", " : "", g_array_index (current_list, guint16, i)); + g_print ("'\n"); + } + + qmi_message_wds_get_lte_attach_pdn_list_output_get_pending_list (output, &pending_list, NULL); + if (!pending_list || !pending_list->len) { + g_print ("\tPending list: n/a\n"); + } else { + g_print ("\tPending list: '"); + for (i = 0; i < pending_list->len; i++) + g_print ("%s%u", i > 0 ? ", " : "", g_array_index (pending_list, guint16, i)); + g_print ("'\n"); + } + + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_GET_LTE_ATTACH_PDN_LIST */ + +#if defined HAVE_QMI_MESSAGE_WDS_SET_LTE_ATTACH_PDN_LIST + +static QmiMessageWdsSetLteAttachPdnListInput * +set_lte_attach_pdn_list_input_create (const gchar *str) +{ + g_autoptr(QmiMessageWdsSetLteAttachPdnListInput) input = NULL; + g_autoptr(GError) error = NULL; + g_auto(GStrv) split = NULL; + g_autoptr(GArray) pdn_list = NULL; + guint i; + + pdn_list = g_array_new (FALSE, FALSE, sizeof (guint16)); + + split = g_strsplit (str, ",", -1); + + for (i = 0; i < g_strv_length (split); i++) { + guint val = 0; + guint16 profile_index; + gboolean success; + + g_strstrip (split[i]); + success = qmicli_read_uint_from_string (split[i], &val); + if (!success || val == 0 || val > G_MAXUINT16) { + g_printerr ("error: invalid or out of range profile number [1,%u]: '%s'\n", G_MAXUINT16, split[i]); + operation_shutdown (FALSE); + return NULL; + } + profile_index = (guint16) val; + g_array_append_val (pdn_list, profile_index); + } + + input = qmi_message_wds_set_lte_attach_pdn_list_input_new (); + if (!qmi_message_wds_set_lte_attach_pdn_list_input_set_list (input, pdn_list, &error)) { + g_printerr ("error: couldn't set attach PDN list: '%s'\n", error->message); + return NULL; + } + if (!qmi_message_wds_set_lte_attach_pdn_list_input_set_action (input, QMI_WDS_ATTACH_PDN_LIST_ACTION_DETACH_OR_PDN_DISCONNECT, &error)) { + g_printerr ("error: couldn't set attach PDN list action: '%s'\n", error->message); + return NULL; + } + + return g_steal_pointer (&input); +} + +static void +set_lte_attach_pdn_list_ready (QmiClientWds *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageWdsSetLteAttachPdnListOutput) output = NULL; + g_autoptr(GError) error = NULL; + + output = qmi_client_wds_set_lte_attach_pdn_list_finish (client, res, &error); + if (!output) { + g_printerr ("error: operation failed: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_wds_set_lte_attach_pdn_list_output_get_result (output, &error)) { + g_printerr ("error: couldn't set attach PDN list: %s\n", error->message); + operation_shutdown (FALSE); + return; + } + + g_print ("Attach PDN list update successfully requested\n"); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WDS_SET_LTE_ATTACH_PDN_LIST */ + +void +qmicli_wds_run (QmiDevice *device, + QmiClientWds *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + ctx->network_started_id = 0; + ctx->packet_status_timeout_id = 0; + +#if defined HAVE_QMI_MESSAGE_WDS_START_NETWORK + if (start_network_str) { + QmiMessageWdsStartNetworkInput *input = NULL; + GError *error = NULL; + + if (!start_network_input_create (start_network_str, &input, &error)) { + g_printerr ("error: %s\n", error->message); + g_error_free (error); + return; + } + + g_debug ("Asynchronously starting network..."); + qmi_client_wds_start_network (ctx->client, + input, + 180, + ctx->cancellable, + (GAsyncReadyCallback)start_network_ready, + NULL); + if (input) + qmi_message_wds_start_network_input_unref (input); + return; + } +#endif /* HAVE_QMI_MESSAGE_WDS_START_NETWORK */ + +#if defined HAVE_QMI_MESSAGE_WDS_STOP_NETWORK + if (stop_network_str) { + gulong packet_data_handle; + gboolean disable_autoconnect; + + if (g_str_equal (stop_network_str, "disable-autoconnect")) { + packet_data_handle = 0xFFFFFFFF; + disable_autoconnect = TRUE; + } else { + disable_autoconnect = FALSE; + if (g_str_has_prefix (stop_network_str, "0x")) + packet_data_handle = strtoul (stop_network_str, NULL, 16); + else + packet_data_handle = strtoul (stop_network_str, NULL, 10); + if (!packet_data_handle || packet_data_handle > G_MAXUINT32) { + g_printerr ("error: invalid packet data handle given '%s'\n", + stop_network_str); + operation_shutdown (FALSE); + return; + } + } + + g_debug ("Asynchronously stopping network (%lu)...", packet_data_handle); + internal_stop_network (ctx->cancellable, (guint32)packet_data_handle, disable_autoconnect); + return; + } +#endif /* HAVE_QMI_MESSAGE_WDS_STOP_NETWORK */ + +#if defined HAVE_QMI_MESSAGE_WDS_BIND_DATA_PORT + if (bind_data_port_str) { + g_autoptr(QmiMessageWdsBindDataPortInput) input = NULL; + + g_debug ("Binding data port..."); + input = bind_data_port_input_create (bind_data_port_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + qmi_client_wds_bind_data_port (client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) bind_data_port_ready, + NULL); + return; + } +#endif /* HAVE_QMI_MESSAGE_WDS_BIND_MUX_DATA_PORT */ + +#if defined HAVE_QMI_MESSAGE_WDS_BIND_MUX_DATA_PORT + if (bind_mux_str) { + QmiMessageWdsBindMuxDataPortInput *input; + + g_debug ("Binding mux data port.."); + input = bind_mux_data_port_input_create (bind_mux_str); + qmi_client_wds_bind_mux_data_port (client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) bind_mux_data_port_ready, + NULL); + qmi_message_wds_bind_mux_data_port_input_unref (input); + return; + } +#endif /* HAVE_QMI_MESSAGE_WDS_BIND_MUX_DATA_PORT */ + +#if defined HAVE_QMI_MESSAGE_WDS_SET_IP_FAMILY + if (set_ip_family_str) { + QmiMessageWdsSetIpFamilyInput *input; + QmiWdsIpFamily preference; + + switch (atoi (set_ip_family_str)) { + case 4: + preference = QMI_WDS_IP_FAMILY_IPV4; + break; + case 6: + preference = QMI_WDS_IP_FAMILY_IPV6; + break; + default: + g_printerr ("error: unknown IP type '%s' (not 4 or 6)\n", + set_ip_family_str); + operation_shutdown (FALSE); + return; + } + + input = qmi_message_wds_set_ip_family_input_new (); + qmi_message_wds_set_ip_family_input_set_preference (input, preference, NULL); + + g_debug ("Asynchronously set IP family..."); + qmi_client_wds_set_ip_family (client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) set_ip_family_ready, + NULL); + qmi_message_wds_set_ip_family_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GET_CURRENT_SETTINGS + if (get_current_settings_flag) { + QmiMessageWdsGetCurrentSettingsInput *input; + + input = qmi_message_wds_get_current_settings_input_new (); + qmi_message_wds_get_current_settings_input_set_requested_settings ( + input, + (QMI_WDS_REQUESTED_SETTINGS_DNS_ADDRESS | + QMI_WDS_REQUESTED_SETTINGS_GRANTED_QOS | + QMI_WDS_REQUESTED_SETTINGS_IP_ADDRESS | + QMI_WDS_REQUESTED_SETTINGS_GATEWAY_INFO | + QMI_WDS_REQUESTED_SETTINGS_MTU | + QMI_WDS_REQUESTED_SETTINGS_DOMAIN_NAME_LIST | + QMI_WDS_REQUESTED_SETTINGS_IP_FAMILY), + NULL); + + g_debug ("Asynchronously getting current settings..."); + qmi_client_wds_get_current_settings (client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_current_settings_ready, + NULL); + qmi_message_wds_get_current_settings_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GET_PACKET_SERVICE_STATUS + if (get_packet_service_status_flag) { + g_debug ("Asynchronously getting packet service status..."); + qmi_client_wds_get_packet_service_status (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_packet_service_status_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GET_PACKET_STATISTICS + if (get_packet_statistics_flag) { + QmiMessageWdsGetPacketStatisticsInput *input; + + input = qmi_message_wds_get_packet_statistics_input_new (); + qmi_message_wds_get_packet_statistics_input_set_mask ( + input, + (QMI_WDS_PACKET_STATISTICS_MASK_FLAG_TX_PACKETS_OK | + QMI_WDS_PACKET_STATISTICS_MASK_FLAG_RX_PACKETS_OK | + QMI_WDS_PACKET_STATISTICS_MASK_FLAG_TX_PACKETS_ERROR | + QMI_WDS_PACKET_STATISTICS_MASK_FLAG_RX_PACKETS_ERROR | + QMI_WDS_PACKET_STATISTICS_MASK_FLAG_TX_OVERFLOWS | + QMI_WDS_PACKET_STATISTICS_MASK_FLAG_RX_OVERFLOWS | + QMI_WDS_PACKET_STATISTICS_MASK_FLAG_TX_BYTES_OK | + QMI_WDS_PACKET_STATISTICS_MASK_FLAG_RX_BYTES_OK | + QMI_WDS_PACKET_STATISTICS_MASK_FLAG_TX_PACKETS_DROPPED | + QMI_WDS_PACKET_STATISTICS_MASK_FLAG_RX_PACKETS_DROPPED), + NULL); + + g_debug ("Asynchronously getting packet statistics..."); + qmi_client_wds_get_packet_statistics (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_packet_statistics_ready, + NULL); + qmi_message_wds_get_packet_statistics_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GET_DATA_BEARER_TECHNOLOGY + if (get_data_bearer_technology_flag) { + g_debug ("Asynchronously getting data bearer technology..."); + qmi_client_wds_get_data_bearer_technology (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_data_bearer_technology_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GET_CURRENT_DATA_BEARER_TECHNOLOGY + if (get_current_data_bearer_technology_flag) { + g_debug ("Asynchronously getting current data bearer technology..."); + qmi_client_wds_get_current_data_bearer_technology (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_current_data_bearer_technology_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GO_DORMANT + if (go_dormant_flag) { + g_debug ("Asynchronously going dormant..."); + qmi_client_wds_go_dormant (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)go_dormant_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GO_ACTIVE + if (go_active_flag) { + g_debug ("Asynchronously going active..."); + qmi_client_wds_go_active (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)go_active_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GET_DORMANCY_STATUS + if (get_dormancy_status_flag) { + g_debug ("Asynchronously getting dormancy status..."); + qmi_client_wds_get_dormancy_status (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_dormancy_status_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_CREATE_PROFILE + if (create_profile_str) { + QmiMessageWdsCreateProfileInput *input = NULL; + GError *error = NULL; + + if (!create_profile_input_create (create_profile_str, &input, &error)) { + g_printerr ("error: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + g_debug ("Asynchronously creating new profile..."); + qmi_client_wds_create_profile (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)create_profile_ready, + NULL); + qmi_message_wds_create_profile_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_SWI_CREATE_PROFILE_INDEXED + if (swi_create_profile_indexed_str) { + QmiMessageWdsSwiCreateProfileIndexedInput *input = NULL; + GError *error = NULL; + + if (!swi_create_profile_indexed_input_create (swi_create_profile_indexed_str, &input, &error)) { + g_printerr ("error: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + g_debug ("Asynchronously creating new indexed profile..."); + qmi_client_wds_swi_create_profile_indexed (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)swi_create_profile_indexed_ready, + NULL); + qmi_message_wds_swi_create_profile_indexed_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_MODIFY_PROFILE + if (modify_profile_str) { + QmiMessageWdsModifyProfileInput *input = NULL; + GError *error = NULL; + + if (!modify_profile_input_create (modify_profile_str, &input, &error)) { + g_printerr ("error: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + g_debug ("Asynchronously modifying profile..."); + qmi_client_wds_modify_profile (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)modify_profile_ready, + NULL); + qmi_message_wds_modify_profile_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_DELETE_PROFILE + if (delete_profile_str) { + QmiMessageWdsDeleteProfileInput *input; + gchar **split; + QmiWdsProfileType profile_type; + guint profile_index; + + split = g_strsplit (delete_profile_str, ",", -1); + + if (g_strv_length (split) != 2) { + g_printerr ("error: expected 2 arguments for delete profile command\n"); + operation_shutdown (FALSE); + return; + } + + g_strstrip (split[0]); + if (g_str_equal (split[0], "3gpp")) + profile_type = QMI_WDS_PROFILE_TYPE_3GPP; + else if (g_str_equal (split[0], "3gpp2")) + profile_type = QMI_WDS_PROFILE_TYPE_3GPP2; + else { + g_printerr ("error: invalid profile type '%s'. Expected '3gpp' or '3gpp2'.'\n", + split[0]); + operation_shutdown (FALSE); + return; + } + + g_strstrip (split[1]); + profile_index = atoi (split[1]); + if (profile_index <= 0 || profile_index > G_MAXUINT8) { + g_printerr ("error: invalid or out of range profile number [1,%u]: '%s'\n", + G_MAXUINT8, + split[1]); + operation_shutdown (FALSE); + return; + } + + input = qmi_message_wds_delete_profile_input_new (); + + qmi_message_wds_delete_profile_input_set_profile_identifier (input, profile_type, (guint8)profile_index, NULL); + + g_strfreev (split); + + g_debug ("Asynchronously deleting new profile..."); + qmi_client_wds_delete_profile (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)delete_profile_ready, + NULL); + qmi_message_wds_delete_profile_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GET_PROFILE_LIST && defined HAVE_QMI_MESSAGE_WDS_GET_PROFILE_SETTINGS + if (get_profile_list_str) { + QmiMessageWdsGetProfileListInput *input; + QmiWdsProfileType profile_type; + + if (g_str_equal (get_profile_list_str, "3gpp")) + profile_type = QMI_WDS_PROFILE_TYPE_3GPP; + else if (g_str_equal (get_profile_list_str, "3gpp2")) + profile_type = QMI_WDS_PROFILE_TYPE_3GPP2; + else { + g_printerr ("error: invalid profile type '%s'. Expected '3gpp' or '3gpp2'.'\n", + get_profile_list_str); + operation_shutdown (FALSE); + return; + } + + input = qmi_message_wds_get_profile_list_input_new (); + qmi_message_wds_get_profile_list_input_set_profile_type (input, profile_type, NULL); + + g_debug ("Asynchronously get profile list..."); + qmi_client_wds_get_profile_list (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_profile_list_ready, + NULL); + qmi_message_wds_get_profile_list_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GET_DEFAULT_PROFILE_NUMBER + if (get_default_profile_number_str || get_default_profile_num_str) { + g_autoptr(QmiMessageWdsGetDefaultProfileNumberInput) input = NULL; + QmiWdsProfileType profile_type; + const gchar *str; + + str = get_default_profile_number_str ? get_default_profile_number_str : get_default_profile_num_str; + if (g_str_equal (str, "3gpp")) + profile_type = QMI_WDS_PROFILE_TYPE_3GPP; + else if (g_str_equal (str, "3gpp2")) + profile_type = QMI_WDS_PROFILE_TYPE_3GPP2; + else { + g_printerr ("error: invalid profile type '%s'. Expected '3gpp' or '3gpp2'.'\n", str); + operation_shutdown (FALSE); + return; + } + + input = qmi_message_wds_get_default_profile_number_input_new (); + /* always use profile family 'tethered', we don't really know what it means */ + qmi_message_wds_get_default_profile_number_input_set_profile_type (input, + profile_type, + QMI_WDS_PROFILE_FAMILY_TETHERED, + NULL); + + g_debug ("Asynchronously getting default profile number..."); + qmi_client_wds_get_default_profile_number (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_default_profile_number_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_SET_DEFAULT_PROFILE_NUMBER + if (set_default_profile_number_str || set_default_profile_num_str) { + g_autoptr(QmiMessageWdsSetDefaultProfileNumberInput) input = NULL; + + input = set_default_profile_number_input_create (set_default_profile_number_str ? set_default_profile_number_str : set_default_profile_num_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + g_debug ("Asynchronously setting default profile number..."); + qmi_client_wds_set_default_profile_number (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_default_profile_number_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GET_DEFAULT_SETTINGS + if (get_default_settings_str) { + QmiMessageWdsGetDefaultSettingsInput *input; + QmiWdsProfileType profile_type; + + if (g_str_equal (get_default_settings_str, "3gpp")) + profile_type = QMI_WDS_PROFILE_TYPE_3GPP; + else if (g_str_equal (get_default_settings_str, "3gpp2")) + profile_type = QMI_WDS_PROFILE_TYPE_3GPP2; + else { + g_printerr ("error: invalid default type '%s'. Expected '3gpp' or '3gpp2'.'\n", + get_default_settings_str); + operation_shutdown (FALSE); + return; + } + + input = qmi_message_wds_get_default_settings_input_new (); + qmi_message_wds_get_default_settings_input_set_profile_type (input, profile_type, NULL); + + g_debug ("Asynchronously get default settings..."); + qmi_client_wds_get_default_settings (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_default_settings_ready, + NULL); + qmi_message_wds_get_default_settings_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GET_AUTOCONNECT_SETTINGS + if (get_autoconnect_settings_flag) { + g_debug ("Asynchronously getting autoconnect settings..."); + qmi_client_wds_get_autoconnect_settings (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_autoconnect_settings_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_SET_AUTOCONNECT_SETTINGS + if (set_autoconnect_settings_str) { + QmiMessageWdsSetAutoconnectSettingsInput *input; + + input = set_autoconnect_settings_input_create (set_autoconnect_settings_str); + if (!input) { + operation_shutdown (FALSE); + return; + } + + g_debug ("Asynchronously set autoconnect settings..."); + qmi_client_wds_set_autoconnect_settings (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_autoconnect_settings_ready, + NULL); + qmi_message_wds_set_autoconnect_settings_input_unref (input); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GET_SUPPORTED_MESSAGES + if (get_supported_messages_flag) { + g_debug ("Asynchronously getting supported WDS messages..."); + qmi_client_wds_get_supported_messages (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_supported_messages_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_RESET + if (reset_flag) { + g_debug ("Asynchronously resetting WDS service..."); + qmi_client_wds_reset (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)reset_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GET_CHANNEL_RATES + if (get_channel_rates_flag) { + g_debug ("Asynchronously getting channel data rates..."); + qmi_client_wds_get_channel_rates (client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_channel_rates_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GET_LTE_ATTACH_PARAMETERS + if (get_lte_attach_parameters_flag) { + g_debug ("Asynchronously getting LTE attach parameters..."); + qmi_client_wds_get_lte_attach_parameters (client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_lte_attach_parameters_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GET_MAX_LTE_ATTACH_PDN_NUMBER + if (get_max_lte_attach_pdn_number_flag) { + g_debug ("Asynchronously getting max LTE attach PDN number..."); + qmi_client_wds_get_max_lte_attach_pdn_number (client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_max_lte_attach_pdn_number_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_GET_LTE_ATTACH_PDN_LIST + if (get_lte_attach_pdn_list_flag) { + g_debug ("Asynchronously getting LTE Attach PDN list..."); + qmi_client_wds_get_lte_attach_pdn_list (client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_lte_attach_pdn_list_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WDS_SET_LTE_ATTACH_PDN_LIST + if (set_lte_attach_pdn_list_str) { + g_autoptr(QmiMessageWdsSetLteAttachPdnListInput) input = NULL; + + input = set_lte_attach_pdn_list_input_create (set_lte_attach_pdn_list_str); + g_debug ("Asynchronously setting LTE Attach PDN list..."); + qmi_client_wds_set_lte_attach_pdn_list (client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback) set_lte_attach_pdn_list_ready, + NULL); + return; + } +#endif + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_WDS */ diff --git a/pkgs/qmi-nmea/qmicli-wms.c b/pkgs/qmi-nmea/qmicli-wms.c new file mode 100644 index 0000000..c0f9c19 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli-wms.c @@ -0,0 +1,566 @@ +/* -*- 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) 2015-2017 Aleksander Morgado + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#if defined HAVE_QMI_SERVICE_WMS + +#define VALIDATE_UNKNOWN(str) (str ? str : "unknown") + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientWms *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gboolean get_supported_messages_flag; +static gboolean get_routes_flag; +static gchar *set_routes_str; +static gboolean reset_flag; +static gboolean noop_flag; + +static GOptionEntry entries[] = { +#if defined HAVE_QMI_MESSAGE_WMS_GET_SUPPORTED_MESSAGES + { "wms-get-supported-messages", 0, 0, G_OPTION_ARG_NONE, &get_supported_messages_flag, + "Get supported messages", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WMS_GET_ROUTES + { "wms-get-routes", 0, 0, G_OPTION_ARG_NONE, &get_routes_flag, + "Get SMS route information", + NULL + }, +#endif +#if defined HAVE_QMI_MESSAGE_WMS_SET_ROUTES + { "wms-set-routes", 0, 0, G_OPTION_ARG_STRING, &set_routes_str, + "Set SMS route information (keys: type, class, storage, receipt-action)", + "[\"key=value,...\"]" + }, +#endif +#if defined HAVE_QMI_MESSAGE_WMS_RESET + { "wms-reset", 0, 0, G_OPTION_ARG_NONE, &reset_flag, + "Reset the service state", + NULL + }, +#endif + { "wms-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag, + "Just allocate or release a WMS client. Use with `--client-no-release-cid' and/or `--client-cid'", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +GOptionGroup * +qmicli_wms_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("wms", + "WMS options:", + "Show Wireless Messaging Service options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_wms_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (get_supported_messages_flag + + get_routes_flag + + !!set_routes_str + + reset_flag + + noop_flag); + + if (n_actions > 1) { + g_printerr ("error: too many WMS actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *context) +{ + if (!context) + return; + + if (context->client) + g_object_unref (context->client); + 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); + qmicli_async_operation_done (operation_status, FALSE); +} + +#if defined HAVE_QMI_MESSAGE_WMS_GET_SUPPORTED_MESSAGES + +static void +get_supported_messages_ready (QmiClientWms *client, + GAsyncResult *res) +{ + QmiMessageWmsGetSupportedMessagesOutput *output; + GError *error = NULL; + GArray *bytearray = NULL; + gchar *str; + + output = qmi_client_wms_get_supported_messages_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_wms_get_supported_messages_output_get_result (output, &error)) { + g_printerr ("error: couldn't get supported WMS messages: %s\n", error->message); + g_error_free (error); + qmi_message_wms_get_supported_messages_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully got supported WMS messages:\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_wms_get_supported_messages_output_get_list (output, &bytearray, NULL); + str = qmicli_get_supported_messages_list (bytearray ? (const guint8 *)bytearray->data : NULL, + bytearray ? bytearray->len : 0); + g_print ("%s", str); + g_free (str); + + qmi_message_wms_get_supported_messages_output_unref (output); + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WMS_GET_SUPPORTED_MESSAGES */ + +#if defined HAVE_QMI_MESSAGE_WMS_GET_ROUTES + +static void +get_routes_ready (QmiClientWms *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageWmsGetRoutesOutput) output = NULL; + GError *error = NULL; + GArray *route_list; + guint i; + + output = qmi_client_wms_get_routes_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_wms_get_routes_output_get_result (output, &error)) { + g_printerr ("error: couldn't get SMS routes: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + if (!qmi_message_wms_get_routes_output_get_route_list (output, &route_list, &error)) { + g_printerr ("error: got invalid SMS routes: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Got %u SMS routes:\n", qmi_device_get_path_display (ctx->device), + route_list->len); + + for (i = 0; i < route_list->len; i++) { + QmiMessageWmsGetRoutesOutputRouteListElement *route; + + route = &g_array_index (route_list, QmiMessageWmsGetRoutesOutputRouteListElement, i); + g_print (" Route #%u:\n", i + 1); + g_print (" Message Type: %s\n", VALIDATE_UNKNOWN (qmi_wms_message_type_get_string (route->message_type))); + g_print (" Message Class: %s\n", VALIDATE_UNKNOWN (qmi_wms_message_class_get_string (route->message_class))); + g_print (" Storage Type: %s\n", VALIDATE_UNKNOWN (qmi_wms_storage_type_get_string (route->storage))); + g_print (" Receipt Action: %s\n", VALIDATE_UNKNOWN (qmi_wms_receipt_action_get_string (route->receipt_action))); + } + + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WMS_GET_ROUTES */ + +#if defined HAVE_QMI_MESSAGE_WMS_SET_ROUTES + +typedef struct { + GArray *route_list; + + gboolean message_type_set; + gboolean message_class_set; + gboolean storage_set; + gboolean receipt_action_set; +} SetRoutesContext; + +static void +set_routes_context_init (SetRoutesContext *routes_ctx) +{ + memset (routes_ctx, 0, sizeof(SetRoutesContext)); + routes_ctx->route_list = g_array_new (FALSE, TRUE, sizeof (QmiMessageWmsSetRoutesInputRouteListElement)); +} + +static void +set_routes_context_destroy (SetRoutesContext *routes_ctx) +{ + g_array_unref (routes_ctx->route_list); +} + +static gboolean +set_route_properties_handle (const gchar *key, + const gchar *value, + GError **error, + gpointer user_data) +{ + SetRoutesContext *routes_ctx = user_data; + QmiMessageWmsSetRoutesInputRouteListElement *cur_route; + gboolean ret = FALSE; + + if (!value || !value[0]) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "key '%s' required a value", + key); + return FALSE; + } + + if (!routes_ctx->message_type_set && !routes_ctx->message_class_set && + !routes_ctx->storage_set && !routes_ctx->receipt_action_set) { + QmiMessageWmsSetRoutesInputRouteListElement new_elt; + + memset (&new_elt, 0, sizeof (QmiMessageWmsSetRoutesInputRouteListElement)); + g_array_append_val (routes_ctx->route_list, new_elt); + } + cur_route = &g_array_index (routes_ctx->route_list, + QmiMessageWmsSetRoutesInputRouteListElement, + routes_ctx->route_list->len - 1); + + if (g_ascii_strcasecmp (key, "type") == 0 && !routes_ctx->message_type_set) { + if (!qmicli_read_wms_message_type_from_string (value, &cur_route->message_type)) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unknown message type '%s'", + value); + return FALSE; + } + routes_ctx->message_type_set = TRUE; + ret = TRUE; + } else if (g_ascii_strcasecmp (key, "class") == 0 && !routes_ctx->message_class_set) { + if (!qmicli_read_wms_message_class_from_string (value, &cur_route->message_class)) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unknown message class '%s'", + value); + return FALSE; + } + routes_ctx->message_class_set = TRUE; + ret = TRUE; + } else if (g_ascii_strcasecmp (key, "storage") == 0 && !routes_ctx->storage_set) { + if (!qmicli_read_wms_storage_type_from_string (value, &cur_route->storage)) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unknown storage type '%s'", + value); + return FALSE; + } + routes_ctx->storage_set = TRUE; + ret = TRUE; + } else if (g_ascii_strcasecmp (key, "receipt-action") == 0 && !routes_ctx->receipt_action_set) { + if (!qmicli_read_wms_receipt_action_from_string (value, &cur_route->receipt_action)) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unknown receipt action '%s'", + value); + return FALSE; + } + routes_ctx->receipt_action_set = TRUE; + ret = TRUE; + } + + if (routes_ctx->message_type_set && routes_ctx->message_class_set && + routes_ctx->storage_set && routes_ctx->receipt_action_set) { + /* We have a complete set of details for this route. Reset the context state. */ + routes_ctx->message_type_set = FALSE; + routes_ctx->message_class_set = FALSE; + routes_ctx->storage_set = FALSE; + routes_ctx->receipt_action_set = FALSE; + } + + if (!ret) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "unrecognized or duplicate option '%s'", + key); + } + return ret; +} + +static QmiMessageWmsSetRoutesInput * +set_routes_input_create (const gchar *str, + GError **error) +{ + g_autoptr(QmiMessageWmsSetRoutesInput) input = NULL; + SetRoutesContext routes_ctx; + GError *inner_error = NULL; + + set_routes_context_init (&routes_ctx); + + if (!qmicli_parse_key_value_string (str, + &inner_error, + set_route_properties_handle, + &routes_ctx)) { + g_propagate_prefixed_error (error, + inner_error, + "couldn't parse input string: "); + set_routes_context_destroy (&routes_ctx); + return NULL; + } + + if (routes_ctx.route_list->len == 0) { + g_set_error_literal (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "route list was empty"); + set_routes_context_destroy (&routes_ctx); + return NULL; + } + + if (routes_ctx.message_type_set || routes_ctx.message_class_set || + routes_ctx.storage_set || routes_ctx.receipt_action_set) { + g_set_error_literal (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "final route was missing one or more options"); + set_routes_context_destroy (&routes_ctx); + return NULL; + } + + /* Create input */ + input = qmi_message_wms_set_routes_input_new (); + + if (!qmi_message_wms_set_routes_input_set_route_list (input, routes_ctx.route_list, &inner_error)) { + g_propagate_error (error, inner_error); + set_routes_context_destroy (&routes_ctx); + return NULL; + } + + set_routes_context_destroy (&routes_ctx); + return g_steal_pointer (&input); +} + +static void +set_routes_ready (QmiClientWms *client, + GAsyncResult *res) +{ + g_autoptr(QmiMessageWmsSetRoutesOutput) output = NULL; + GError *error = NULL; + + output = qmi_client_wms_set_routes_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_wms_set_routes_output_get_result (output, &error)) { + g_printerr ("error: couldn't set SMS routes: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully set SMS routes\n", + qmi_device_get_path_display (ctx->device)); + + operation_shutdown (TRUE); +} + +#endif /* HAVE_QMI_MESSAGE_WMS_SET_ROUTES */ + +#if defined HAVE_QMI_MESSAGE_WMS_RESET + +static void +reset_ready (QmiClientWms *client, + GAsyncResult *res) +{ + QmiMessageWmsResetOutput *output; + GError *error = NULL; + + output = qmi_client_wms_reset_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_wms_reset_output_get_result (output, &error)) { + g_printerr ("error: couldn't reset the WMS service: %s\n", error->message); + g_error_free (error); + qmi_message_wms_reset_output_unref (output); + operation_shutdown (FALSE); + return; + } + + g_print ("[%s] Successfully performed WMS service reset\n", + qmi_device_get_path_display (ctx->device)); + + qmi_message_wms_reset_output_unref (output); + operation_shutdown (TRUE); +} + +#endif + +static gboolean +noop_cb (gpointer unused) +{ + operation_shutdown (TRUE); + return FALSE; +} + +void +qmicli_wms_run (QmiDevice *device, + QmiClientWms *client, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + ctx->client = g_object_ref (client); + ctx->cancellable = g_object_ref (cancellable); + +#if defined HAVE_QMI_MESSAGE_WMS_GET_SUPPORTED_MESSAGES + if (get_supported_messages_flag) { + g_debug ("Asynchronously getting supported WMS messages..."); + qmi_client_wms_get_supported_messages (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_supported_messages_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WMS_GET_ROUTES + if (get_routes_flag) { + g_debug ("Asynchronously getting SMS routes..."); + qmi_client_wms_get_routes (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_routes_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WMS_SET_ROUTES + if (set_routes_str) { + g_autoptr(QmiMessageWmsSetRoutesInput) input = NULL; + GError *error = NULL; + + input = set_routes_input_create (set_routes_str, &error); + if (!input) { + g_printerr ("Failed to set route: %s\n", error->message); + g_error_free (error); + operation_shutdown (FALSE); + return; + } + g_debug ("Asynchronously setting SMS routes..."); + qmi_client_wms_set_routes (ctx->client, + input, + 10, + ctx->cancellable, + (GAsyncReadyCallback)set_routes_ready, + NULL); + return; + } +#endif + +#if defined HAVE_QMI_MESSAGE_WMS_RESET + if (reset_flag) { + g_debug ("Asynchronously resetting WMS service..."); + qmi_client_wms_reset (ctx->client, + NULL, + 10, + ctx->cancellable, + (GAsyncReadyCallback)reset_ready, + NULL); + return; + } +#endif + + /* Just client allocate/release? */ + if (noop_flag) { + g_idle_add (noop_cb, NULL); + return; + } + + g_warn_if_reached (); +} + +#endif /* HAVE_QMI_SERVICE_WMS */ diff --git a/pkgs/qmi-nmea/qmicli.c b/pkgs/qmi-nmea/qmicli.c new file mode 100644 index 0000000..ea694f9 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli.c @@ -0,0 +1,1169 @@ +/* -*- 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) 2012-2023 Aleksander Morgado + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#if QMI_MBIM_QMUX_SUPPORTED +#include +#endif + +#include "qmicli.h" +#include "qmicli-helpers.h" + +#define PROGRAM_NAME "qmicli" +#define PROGRAM_VERSION PACKAGE_VERSION + +/* Globals */ +static GMainLoop *loop; +static GCancellable *cancellable; +static QmiDevice *device; +static QmiClient *client; +static QmiService service; +static gboolean operation_status; +static gboolean expect_indications; +#if QMI_QRTR_SUPPORTED +static QrtrBus *qrtr_bus; +#endif + +/* Main options */ +static gchar *device_str; +static gboolean get_service_version_info_flag; +static gchar *device_set_instance_id_str; +static gboolean device_open_version_info_flag; +static gboolean device_open_sync_flag; +static gchar *device_open_net_str; +static gboolean device_open_proxy_flag; +#if QMI_MBIM_QMUX_SUPPORTED +static gboolean device_open_qmi_flag; +static gboolean device_open_mbim_flag; +static gboolean device_open_auto_flag; +#endif +static gchar *client_cid_str; +static gboolean client_no_release_cid_flag; +static gboolean verbose_flag; +static gboolean verbose_full_flag; +static gboolean silent_flag; +static gboolean version_flag; + +static GOptionEntry main_entries[] = { + { "device", 'd', 0, G_OPTION_ARG_STRING, &device_str, +#if QMI_QRTR_SUPPORTED + "Specify device path or QRTR URI (e.g. qrtr://0)", + "[PATH|URI]" +#else + "Specify device path", + "[PATH]" +#endif + }, + { "get-service-version-info", 0, 0, G_OPTION_ARG_NONE, &get_service_version_info_flag, + "Get service version info", + NULL + }, + { "device-set-instance-id", 0, 0, G_OPTION_ARG_STRING, &device_set_instance_id_str, + "Set instance ID", + "[Instance ID]" + }, + { "device-open-version-info", 0, 0, G_OPTION_ARG_NONE, &device_open_version_info_flag, + "Run version info check when opening device", + NULL + }, + { "device-open-sync", 0, 0, G_OPTION_ARG_NONE, &device_open_sync_flag, + "Run sync operation when opening device", + NULL + }, + { "device-open-proxy", 'p', 0, G_OPTION_ARG_NONE, &device_open_proxy_flag, + "Request to use the 'qmi-proxy' proxy", + NULL + }, +#if QMI_MBIM_QMUX_SUPPORTED + { "device-open-qmi", 0, 0, G_OPTION_ARG_NONE, &device_open_qmi_flag, + "Open a cdc-wdm device explicitly in QMI mode", + NULL + }, + { "device-open-mbim", 0, 0, G_OPTION_ARG_NONE, &device_open_mbim_flag, + "Open a cdc-wdm device explicitly in MBIM mode", + NULL + }, + { "device-open-auto", 0, 0, G_OPTION_ARG_NONE, &device_open_auto_flag, + "Open a cdc-wdm device in either QMI or MBIM mode (default)", + NULL + }, +#endif + { "device-open-net", 0, 0, G_OPTION_ARG_STRING, &device_open_net_str, + "Open device with specific link protocol and QoS flags", + "[net-802-3|net-raw-ip|net-qos-header|net-no-qos-header]" + }, + { "client-cid", 0, 0, G_OPTION_ARG_STRING, &client_cid_str, + "Use the given CID, don't allocate a new one", + "[CID]" + }, + { "client-no-release-cid", 0, 0, G_OPTION_ARG_NONE, &client_no_release_cid_flag, + "Do not release the CID when exiting", + NULL + }, + { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag, + "Run action with verbose logs, including the debug ones", + NULL + }, + { "verbose-full", 0, 0, G_OPTION_ARG_NONE, &verbose_full_flag, + "Run action with verbose logs, including the debug ones and personal info", + NULL + }, + { "silent", 0, 0, G_OPTION_ARG_NONE, &silent_flag, + "Run action with no logs; not even the error/warning ones", + NULL + }, + { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag, + "Print version", + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +static gboolean +signals_handler (void) +{ + if (cancellable) { + /* Ignore consecutive requests of cancellation */ + if (!g_cancellable_is_cancelled (cancellable)) { + g_printerr ("cancelling the operation...\n"); + g_cancellable_cancel (cancellable); + /* Re-set the signal handler to allow main loop cancellation on + * second signal */ + return G_SOURCE_CONTINUE; + } + } + + if (loop && g_main_loop_is_running (loop)) { + g_printerr ("cancelling the main loop...\n"); + g_idle_add ((GSourceFunc) g_main_loop_quit, loop); + } + return G_SOURCE_REMOVE; +} + +static void +log_handler (const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer user_data) +{ + const gchar *log_level_str; + time_t now; + gchar time_str[64]; + struct tm *local_time; + gboolean err; + + /* Nothing to do if we're silent */ + if (silent_flag) + return; + + now = time ((time_t *) NULL); + local_time = localtime (&now); + strftime (time_str, 64, "%d %b %Y, %H:%M:%S", local_time); + err = FALSE; + + switch (log_level) { + case G_LOG_LEVEL_WARNING: + log_level_str = "-Warning **"; + err = TRUE; + break; + + case G_LOG_LEVEL_CRITICAL: + case G_LOG_LEVEL_ERROR: + log_level_str = "-Error **"; + err = TRUE; + break; + + case G_LOG_LEVEL_DEBUG: + log_level_str = "[Debug]"; + break; + + case G_LOG_LEVEL_MESSAGE: + case G_LOG_LEVEL_INFO: + log_level_str = ""; + break; + + case G_LOG_FLAG_FATAL: + case G_LOG_LEVEL_MASK: + case G_LOG_FLAG_RECURSION: + default: + g_assert_not_reached (); + } + + if (!verbose_flag && !verbose_full_flag && !err) + return; + + g_fprintf (err ? stderr : stdout, + "[%s] %s %s\n", + time_str, + log_level_str, + message); +} + +G_GNUC_NORETURN +static void +print_version_and_exit (void) +{ + g_print ("Copyright (C) 2012-2023 Aleksander Morgado, 2024 Daniel Barlow\n" + "License GPLv2+: GNU GPL version 2 or later \n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n" + "\n"); + exit (EXIT_SUCCESS); +} + +static gboolean +generic_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (!!device_set_instance_id_str + + get_service_version_info_flag); + + if (n_actions > 1) { + g_printerr ("error: too many generic actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +/*****************************************************************************/ +/* Report that indications are expected */ + +void +qmicli_expect_indications (void) +{ + expect_indications = TRUE; +} + +/*****************************************************************************/ +/* Running asynchronously */ + +static void +close_ready (QmiDevice *dev, + GAsyncResult *res) +{ + GError *error = NULL; + + if (!qmi_device_close_finish (dev, res, &error)) { + g_printerr ("error: couldn't close: %s\n", error->message); + g_error_free (error); + } else + g_debug ("Closed"); + + g_main_loop_quit (loop); +} + +static void +release_client_ready (QmiDevice *dev, + GAsyncResult *res) +{ + GError *error = NULL; + + if (!qmi_device_release_client_finish (dev, res, &error)) { + g_printerr ("error: couldn't release client: %s\n", error->message); + g_error_free (error); + } else + g_debug ("Client released"); + + qmi_device_close_async (dev, 10, NULL, (GAsyncReadyCallback) close_ready, NULL); +} + +void +qmicli_async_operation_done (gboolean reported_operation_status, + gboolean skip_cid_release) +{ + QmiDeviceReleaseClientFlags flags = QMI_DEVICE_RELEASE_CLIENT_FLAGS_NONE; + + /* Keep the result of the operation */ + operation_status = reported_operation_status; + + /* Cleanup cancellation */ + g_clear_object (&cancellable); + + /* If no client was allocated (e.g. generic action), just quit */ + if (!client) { + g_main_loop_quit (loop); + return; + } + + if (skip_cid_release) + g_debug ("Skipped CID release"); + else if (!client_no_release_cid_flag) + flags |= QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID; + else + g_print ("[%s] Client ID not released:\n" + "\tService: '%s'\n" + "\t CID: '%u'\n", + qmi_device_get_path_display (device), + qmi_service_get_string (service), + qmi_client_get_cid (client)); + + qmi_device_release_client (device, + client, + flags, + 10, + NULL, + (GAsyncReadyCallback)release_client_ready, + NULL); +} + +static void +allocate_client_ready (QmiDevice *dev, + GAsyncResult *res) +{ + GError *error = NULL; + + client = qmi_device_allocate_client_finish (dev, res, &error); + if (!client) { + g_printerr ("error: couldn't create client for the '%s' service: %s\n", + qmi_service_get_string (service), + error->message); + exit (EXIT_FAILURE); + } + + /* Run the service-specific action */ + switch (service) { + case QMI_SERVICE_DMS: +#if defined HAVE_QMI_SERVICE_DMS + qmicli_dms_run (dev, QMI_CLIENT_DMS (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_NAS: +#if defined HAVE_QMI_SERVICE_NAS + qmicli_nas_run (dev, QMI_CLIENT_NAS (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_WDS: +#if defined HAVE_QMI_SERVICE_WDS + qmicli_wds_run (dev, QMI_CLIENT_WDS (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_PBM: +#if defined HAVE_QMI_SERVICE_PBM + qmicli_pbm_run (dev, QMI_CLIENT_PBM (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_PDC: +#if defined HAVE_QMI_SERVICE_PDC + qmicli_pdc_run (dev, QMI_CLIENT_PDC (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_UIM: +#if defined HAVE_QMI_SERVICE_UIM + qmicli_uim_run (dev, QMI_CLIENT_UIM (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_WMS: +#if defined HAVE_QMI_SERVICE_WMS + qmicli_wms_run (dev, QMI_CLIENT_WMS (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_WDA: +#if defined HAVE_QMI_SERVICE_WDA + qmicli_wda_run (dev, QMI_CLIENT_WDA (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_VOICE: +#if defined HAVE_QMI_SERVICE_VOICE + qmicli_voice_run (dev, QMI_CLIENT_VOICE (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_LOC: +#if defined HAVE_QMI_SERVICE_LOC + qmicli_loc_run (dev, QMI_CLIENT_LOC (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_QOS: +#if defined HAVE_QMI_SERVICE_QOS + qmicli_qos_run (dev, QMI_CLIENT_QOS (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_GAS: +#if defined HAVE_QMI_SERVICE_GAS + qmicli_gas_run (dev, QMI_CLIENT_GAS (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_GMS: +#if defined HAVE_QMI_SERVICE_GMS + qmicli_gms_run (dev, QMI_CLIENT_GMS (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_DSD: +#if defined HAVE_QMI_SERVICE_DSD + qmicli_dsd_run (dev, QMI_CLIENT_DSD (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_SAR: +#if defined HAVE_QMI_SERVICE_SAR + qmicli_sar_run (dev, QMI_CLIENT_SAR (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_DPM: +#if defined HAVE_QMI_SERVICE_DPM + qmicli_dpm_run (dev, QMI_CLIENT_DPM (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_FOX: +#if defined HAVE_QMI_SERVICE_FOX + qmicli_fox_run (dev, QMI_CLIENT_FOX (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_ATR: +#if defined HAVE_QMI_SERVICE_ATR + qmicli_atr_run (dev, QMI_CLIENT_ATR (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_IMSP: +#if defined HAVE_QMI_SERVICE_IMSP + qmicli_imsp_run (dev, QMI_CLIENT_IMSP (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_IMSA: +#if defined HAVE_QMI_SERVICE_IMSA + qmicli_imsa_run (dev, QMI_CLIENT_IMSA (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_IMS: +#if defined HAVE_QMI_SERVICE_IMS + qmicli_ims_run (dev, QMI_CLIENT_IMS (client), cancellable); + return; +#else + break; +#endif + case QMI_SERVICE_UNKNOWN: + case QMI_SERVICE_CTL: + case QMI_SERVICE_AUTH: + case QMI_SERVICE_AT: + case QMI_SERVICE_CAT2: + case QMI_SERVICE_QCHAT: + case QMI_SERVICE_RMTFS: + case QMI_SERVICE_TEST: + case QMI_SERVICE_ADC: + case QMI_SERVICE_CSD: + case QMI_SERVICE_MFS: + case QMI_SERVICE_TIME: + case QMI_SERVICE_TS: + case QMI_SERVICE_TMD: + case QMI_SERVICE_SAP: + case QMI_SERVICE_TSYNC: + case QMI_SERVICE_RFSA: + case QMI_SERVICE_CSVT: + case QMI_SERVICE_QCMAP: + case QMI_SERVICE_IMSVT: + case QMI_SERVICE_COEX: + case QMI_SERVICE_STX: + case QMI_SERVICE_BIT: + case QMI_SERVICE_IMSRTP: + case QMI_SERVICE_RFRPE: + case QMI_SERVICE_SSCTL: + case QMI_SERVICE_CAT: + case QMI_SERVICE_RMS: + case QMI_SERVICE_FOTA: + case QMI_SERVICE_PDS: + case QMI_SERVICE_OMA: + case QMI_SERVICE_SSC: + default: + break; + } + g_assert_not_reached (); +} + +static void +device_allocate_client (QmiDevice *dev) +{ + guint8 cid = QMI_CID_NONE; + + if (client_cid_str) { + guint32 cid32; + + cid32 = atoi (client_cid_str); + if (!cid32 || cid32 > G_MAXUINT8) { + g_printerr ("error: invalid CID given '%s'\n", + client_cid_str); + exit (EXIT_FAILURE); + } + + cid = (guint8)cid32; + g_debug ("Reusing CID '%u'", cid); + } + + /* As soon as we get the QmiDevice, create a client for the requested + * service */ + qmi_device_allocate_client (dev, + service, + cid, + 10, + cancellable, + (GAsyncReadyCallback)allocate_client_ready, + NULL); +} + +static void +set_instance_id_ready (QmiDevice *dev, + GAsyncResult *res) +{ + GError *error = NULL; + guint16 link_id; + + if (!qmi_device_set_instance_id_finish (dev, res, &link_id, &error)) { + g_printerr ("error: couldn't set instance ID: %s\n", + error->message); + exit (EXIT_FAILURE); + } + + g_print ("[%s] Instance ID set:\n" + "\tLink ID: '%" G_GUINT16_FORMAT "'\n", + qmi_device_get_path_display (dev), + link_id); + + /* We're done now */ + qmicli_async_operation_done (TRUE, FALSE); +} + +static void +device_set_instance_id (QmiDevice *dev) +{ + gint instance_id; + + if (g_str_equal (device_set_instance_id_str, "0")) + instance_id = 0; + else { + instance_id = atoi (device_set_instance_id_str); + if (instance_id == 0) { + g_printerr ("error: invalid instance ID given: '%s'\n", device_set_instance_id_str); + exit (EXIT_FAILURE); + } else if (instance_id < 0 || instance_id > G_MAXUINT8) { + g_printerr ("error: given instance ID is out of range [0,%u]: '%s'\n", + G_MAXUINT8, + device_set_instance_id_str); + exit (EXIT_FAILURE); + } + } + + g_debug ("Setting instance ID '%d'...", instance_id); + qmi_device_set_instance_id (dev, + (guint8)instance_id, + 10, + cancellable, + (GAsyncReadyCallback)set_instance_id_ready, + NULL); +} + +static void +get_service_version_info_ready (QmiDevice *dev, + GAsyncResult *res) +{ + GError *error = NULL; + GArray *services; + guint i; + + services = qmi_device_get_service_version_info_finish (dev, res, &error); + if (!services) { + g_printerr ("error: couldn't get service version info: %s\n", + error->message); + exit (EXIT_FAILURE); + } + + g_print ("[%s] Supported versions:\n", + qmi_device_get_path_display (dev)); + for (i = 0; i < services->len; i++) { + QmiDeviceServiceVersionInfo *info; + const gchar *service_str; + + info = &g_array_index (services, QmiDeviceServiceVersionInfo, i); + service_str = qmi_service_get_string (info->service); + if (service_str) + g_print ("\t%s (%u.%u)\n", + service_str, + info->major_version, + info->minor_version); + else + g_print ("\tunknown [0x%02x] (%u.%u)\n", + info->service, + info->major_version, + info->minor_version); + } + g_array_unref (services); + + /* We're done now */ + qmicli_async_operation_done (TRUE, FALSE); +} + +static void +device_get_service_version_info (QmiDevice *dev) +{ + g_debug ("Getting service version info..."); + qmi_device_get_service_version_info (dev, + 10, + cancellable, + (GAsyncReadyCallback)get_service_version_info_ready, + NULL); +} + +static void +device_open_ready (QmiDevice *dev, + GAsyncResult *res) +{ + GError *error = NULL; + + if (!qmi_device_open_finish (dev, res, &error)) { + g_printerr ("error: couldn't open the QmiDevice: %s\n", + error->message); + exit (EXIT_FAILURE); + } + + g_debug ("QMI Device at '%s' ready", + qmi_device_get_path_display (dev)); + + if (device_set_instance_id_str) + device_set_instance_id (dev); + else if (get_service_version_info_flag) + device_get_service_version_info (dev); + else if (qmicli_link_management_options_enabled ()) + qmicli_link_management_run (dev, cancellable); + else if (qmicli_qmiwwan_options_enabled ()) + qmicli_qmiwwan_run (dev, cancellable); + else + device_allocate_client (dev); +} + +static void +device_new_ready (GObject *unused, + GAsyncResult *res) +{ + QmiDeviceOpenFlags open_flags = QMI_DEVICE_OPEN_FLAGS_NONE; + GError *error = NULL; + + device = qmi_device_new_finish (res, &error); + if (!device) { + g_printerr ("error: couldn't create QmiDevice: %s\n", + error->message); + exit (EXIT_FAILURE); + } + +#if QMI_MBIM_QMUX_SUPPORTED + if (device_open_mbim_flag + device_open_qmi_flag + device_open_auto_flag > 1) { + g_printerr ("error: cannot specify multiple mode flags to open device\n"); + exit (EXIT_FAILURE); + } +#endif + + /* Setup device open flags */ + if (device_open_version_info_flag) + open_flags |= QMI_DEVICE_OPEN_FLAGS_VERSION_INFO; + if (device_open_sync_flag) + open_flags |= QMI_DEVICE_OPEN_FLAGS_SYNC; + if (device_open_proxy_flag) + open_flags |= QMI_DEVICE_OPEN_FLAGS_PROXY; +#if QMI_MBIM_QMUX_SUPPORTED + if (device_open_mbim_flag) + open_flags |= QMI_DEVICE_OPEN_FLAGS_MBIM; + if (device_open_auto_flag || (!device_open_qmi_flag && !device_open_mbim_flag)) + open_flags |= QMI_DEVICE_OPEN_FLAGS_AUTO; +#endif + if (expect_indications) + open_flags |= QMI_DEVICE_OPEN_FLAGS_EXPECT_INDICATIONS; + if (device_open_net_str) { + if (!qmicli_read_device_open_flags_from_string (device_open_net_str, &open_flags) || + !qmicli_validate_device_open_flags (open_flags)) + exit (EXIT_FAILURE); + } + + /* Open the device */ + qmi_device_open (device, + open_flags, + 15, + cancellable, + (GAsyncReadyCallback)device_open_ready, + NULL); +} + +#if QMI_QRTR_SUPPORTED + +static void +bus_new_ready (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GError) error = NULL; + guint node_id; + QrtrNode *node; + + node_id = GPOINTER_TO_UINT (user_data); + + qrtr_bus = qrtr_bus_new_finish (res, &error); + if (!qrtr_bus) { + g_printerr ("error: couldn't access QRTR bus: %s\n", error->message); + exit (EXIT_FAILURE); + } + + node = qrtr_bus_peek_node (qrtr_bus, node_id); + if (!node) { + g_printerr ("error: node with id %u not found in QRTR bus\n", node_id); + exit (EXIT_FAILURE); + } + + qmi_device_new_from_node (node, + cancellable, + (GAsyncReadyCallback)device_new_ready, + NULL); +} + +#endif + +static gboolean +make_device (GFile *file) +{ + g_autofree gchar *id = NULL; + + id = g_file_get_path (file); + if (id) { + /* Describes a local device file. */ + qmi_device_new (file, + cancellable, + (GAsyncReadyCallback)device_new_ready, + NULL); + return TRUE; + } + +#if QMI_QRTR_SUPPORTED + { + guint32 node_id; + + id = g_file_get_uri (file); + if (qrtr_get_node_for_uri (id, &node_id)) { + qrtr_bus_new (1000, /* ms */ + cancellable, + (GAsyncReadyCallback)bus_new_ready, + GUINT_TO_POINTER (node_id)); + return TRUE; + } + + g_printerr ("error: URI is neither a local file path nor a QRTR node: %s\n", id); + return FALSE; + } +#else + g_printerr ("error: URI is not a local file path: %s\n", id); + return FALSE; +#endif +} + +/*****************************************************************************/ + +static void +parse_actions (void) +{ + guint actions_enabled = 0; + + if (generic_options_enabled ()) { + service = QMI_SERVICE_CTL; + actions_enabled++; + } + + if (qmicli_link_management_options_enabled ()) { + service = QMI_SERVICE_UNKNOWN; + actions_enabled++; + } + + if (qmicli_qmiwwan_options_enabled ()) { + service = QMI_SERVICE_UNKNOWN; + actions_enabled++; + } + +#if defined HAVE_QMI_SERVICE_DMS + if (qmicli_dms_options_enabled ()) { + service = QMI_SERVICE_DMS; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_NAS + if (qmicli_nas_options_enabled ()) { + service = QMI_SERVICE_NAS; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_WDS + if (qmicli_wds_options_enabled ()) { + service = QMI_SERVICE_WDS; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_PBM + if (qmicli_pbm_options_enabled ()) { + service = QMI_SERVICE_PBM; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_PDC + if (qmicli_pdc_options_enabled ()) { + service = QMI_SERVICE_PDC; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_UIM + if (qmicli_uim_options_enabled ()) { + service = QMI_SERVICE_UIM; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_SAR + if (qmicli_sar_options_enabled ()) { + service = QMI_SERVICE_SAR; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_WMS + if (qmicli_wms_options_enabled ()) { + service = QMI_SERVICE_WMS; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_WDA + if (qmicli_wda_options_enabled ()) { + service = QMI_SERVICE_WDA; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_VOICE + if (qmicli_voice_options_enabled ()) { + service = QMI_SERVICE_VOICE; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_LOC + if (qmicli_loc_options_enabled ()) { + service = QMI_SERVICE_LOC; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_QOS + if (qmicli_qos_options_enabled ()) { + service = QMI_SERVICE_QOS; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_GAS + if (qmicli_gas_options_enabled ()) { + service = QMI_SERVICE_GAS; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_GMS + if (qmicli_gms_options_enabled ()) { + service = QMI_SERVICE_GMS; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_DSD + if (qmicli_dsd_options_enabled ()) { + service = QMI_SERVICE_DSD; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_DPM + if (qmicli_dpm_options_enabled ()) { + service = QMI_SERVICE_DPM; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_FOX + if (qmicli_fox_options_enabled ()) { + service = QMI_SERVICE_FOX; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_ATR + if (qmicli_atr_options_enabled ()) { + service = QMI_SERVICE_ATR; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_IMSP + if (qmicli_imsp_options_enabled ()) { + service = QMI_SERVICE_IMSP; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_IMSA + if (qmicli_imsa_options_enabled ()) { + service = QMI_SERVICE_IMSA; + actions_enabled++; + } +#endif + +#if defined HAVE_QMI_SERVICE_IMS + if (qmicli_ims_options_enabled ()) { + service = QMI_SERVICE_IMS; + actions_enabled++; + } +#endif + + /* Cannot mix actions from different services */ + if (actions_enabled > 1) { + g_printerr ("error: cannot execute multiple actions of different services\n"); + exit (EXIT_FAILURE); + } + + /* No options? */ + if (actions_enabled == 0) { + g_printerr ("error: no actions specified\n"); + exit (EXIT_FAILURE); + } + + /* Go on! */ +} + +int main (int argc, char **argv) +{ + GError *error = NULL; + GFile *file; + GOptionContext *context; + + setlocale (LC_ALL, ""); + + /* Setup option context, process it and destroy it */ + context = g_option_context_new ("- Control QMI devices"); +#if defined HAVE_QMI_SERVICE_DMS + g_option_context_add_group (context, qmicli_dms_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_NAS + g_option_context_add_group (context, qmicli_nas_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_WDS + g_option_context_add_group (context, qmicli_wds_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_PBM + g_option_context_add_group (context, qmicli_pbm_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_PDC + g_option_context_add_group (context, qmicli_pdc_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_UIM + g_option_context_add_group (context, qmicli_uim_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_SAR + g_option_context_add_group (context, qmicli_sar_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_WMS + g_option_context_add_group (context, qmicli_wms_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_WDA + g_option_context_add_group (context, qmicli_wda_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_VOICE + g_option_context_add_group (context, qmicli_voice_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_LOC + g_option_context_add_group (context, qmicli_loc_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_QOS + g_option_context_add_group (context, qmicli_qos_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_GAS + g_option_context_add_group (context, qmicli_gas_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_GMS + g_option_context_add_group (context, qmicli_gms_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_DSD + g_option_context_add_group (context, qmicli_dsd_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_DPM + g_option_context_add_group (context, qmicli_dpm_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_FOX + g_option_context_add_group (context, qmicli_fox_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_ATR + g_option_context_add_group (context, qmicli_atr_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_IMSP + g_option_context_add_group (context, qmicli_imsp_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_IMSA + g_option_context_add_group (context, qmicli_imsa_get_option_group ()); +#endif +#if defined HAVE_QMI_SERVICE_IMS + g_option_context_add_group (context, qmicli_ims_get_option_group ()); +#endif + g_option_context_add_group (context, qmicli_link_management_get_option_group ()); + g_option_context_add_group (context, qmicli_qmiwwan_get_option_group ()); + g_option_context_add_main_entries (context, main_entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) { + g_printerr ("error: %s\n", + error->message); + exit (EXIT_FAILURE); + } + g_option_context_free (context); + + if (version_flag) + print_version_and_exit (); + + g_log_set_handler (NULL, G_LOG_LEVEL_MASK, log_handler, NULL); + g_log_set_handler ("Qmi", G_LOG_LEVEL_MASK, log_handler, NULL); + + if (verbose_flag && verbose_full_flag) { + g_printerr ("error: cannot specify --verbose and --verbose-full at the same time\n"); + exit (EXIT_FAILURE); + } else if (verbose_flag) { + qmi_utils_set_traces_enabled (TRUE); + qmi_utils_set_show_personal_info (FALSE); + } else if (verbose_full_flag) { + qmi_utils_set_traces_enabled (TRUE); + qmi_utils_set_show_personal_info (TRUE); + } + +#if QMI_MBIM_QMUX_SUPPORTED + /* libmbim logging */ + g_log_set_handler ("Mbim", G_LOG_LEVEL_MASK, log_handler, NULL); + if (verbose_flag) { + mbim_utils_set_traces_enabled (TRUE); +#if MBIM_CHECK_VERSION(1,27,6) + mbim_utils_set_show_personal_info (FALSE); +#endif + } else if (verbose_full_flag) { + mbim_utils_set_traces_enabled (TRUE); +#if MBIM_CHECK_VERSION(1,27,6) + mbim_utils_set_show_personal_info (TRUE); +#endif + } +#endif + +#if QMI_QRTR_SUPPORTED + /* libqrtr-glib logging */ + g_log_set_handler ("Qrtr", G_LOG_LEVEL_MASK, log_handler, NULL); +#endif + + /* No device path given? */ + if (!device_str) { + g_printerr ("error: no device path specified\n"); + exit (EXIT_FAILURE); + } + + /* Build new GFile from the commandline arg */ + file = g_file_new_for_commandline_arg (device_str); + + parse_actions (); + + /* Create requirements for async options */ + cancellable = g_cancellable_new (); + loop = g_main_loop_new (NULL, FALSE); + + /* Setup signals */ + g_unix_signal_add (SIGINT, (GSourceFunc) signals_handler, NULL); + g_unix_signal_add (SIGHUP, (GSourceFunc) signals_handler, NULL); + g_unix_signal_add (SIGTERM, (GSourceFunc) signals_handler, NULL); + + /* Launch QmiDevice creation */ + if (!make_device (file)) + return EXIT_FAILURE; + + g_main_loop_run (loop); + + if (cancellable) + g_object_unref (cancellable); + if (client) + g_object_unref (client); + if (device) + g_object_unref (device); + g_main_loop_unref (loop); + g_object_unref (file); + + return (operation_status ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/pkgs/qmi-nmea/qmicli.h b/pkgs/qmi-nmea/qmicli.h new file mode 100644 index 0000000..0a5fa97 --- /dev/null +++ b/pkgs/qmi-nmea/qmicli.h @@ -0,0 +1,212 @@ +/* -*- 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) 2012-2017 Aleksander Morgado + */ + +#include +#include + +#ifndef __QMICLI_H__ +#define __QMICLI_H__ + +/* Common */ +void qmicli_async_operation_done (gboolean reported_operation_status, + gboolean skip_cid_release); +void qmicli_expect_indications (void); + +/* qmi_wwan specific */ +GOptionGroup *qmicli_qmiwwan_get_option_group (void); +gboolean qmicli_qmiwwan_options_enabled (void); +void qmicli_qmiwwan_run (QmiDevice *device, + GCancellable *cancellable); + +/* link management */ +GOptionGroup *qmicli_link_management_get_option_group (void); +gboolean qmicli_link_management_options_enabled (void); +void qmicli_link_management_run (QmiDevice *device, + GCancellable *cancellable); + +#if defined HAVE_QMI_SERVICE_DMS +GOptionGroup *qmicli_dms_get_option_group (void); +gboolean qmicli_dms_options_enabled (void); +void qmicli_dms_run (QmiDevice *device, + QmiClientDms *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_WDS +GOptionGroup *qmicli_wds_get_option_group (void); +gboolean qmicli_wds_options_enabled (void); +void qmicli_wds_run (QmiDevice *device, + QmiClientWds *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_NAS +GOptionGroup *qmicli_nas_get_option_group (void); +gboolean qmicli_nas_options_enabled (void); +void qmicli_nas_run (QmiDevice *device, + QmiClientNas *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_PBM +GOptionGroup *qmicli_pbm_get_option_group (void); +gboolean qmicli_pbm_options_enabled (void); +void qmicli_pbm_run (QmiDevice *device, + QmiClientPbm *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_PDC +GOptionGroup *qmicli_pdc_get_option_group (void); +gboolean qmicli_pdc_options_enabled (void); +void qmicli_pdc_run (QmiDevice *device, + QmiClientPdc *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_UIM +GOptionGroup *qmicli_uim_get_option_group (void); +gboolean qmicli_uim_options_enabled (void); +void qmicli_uim_run (QmiDevice *device, + QmiClientUim *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_WMS +GOptionGroup *qmicli_wms_get_option_group (void); +gboolean qmicli_wms_options_enabled (void); +void qmicli_wms_run (QmiDevice *device, + QmiClientWms *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_WDA +GOptionGroup *qmicli_wda_get_option_group (void); +gboolean qmicli_wda_options_enabled (void); +void qmicli_wda_run (QmiDevice *device, + QmiClientWda *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_VOICE +GOptionGroup *qmicli_voice_get_option_group (void); +gboolean qmicli_voice_options_enabled (void); +void qmicli_voice_run (QmiDevice *device, + QmiClientVoice *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_LOC +GOptionGroup *qmicli_loc_get_option_group (void); +gboolean qmicli_loc_options_enabled (void); +void qmicli_loc_run (QmiDevice *device, + QmiClientLoc *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_QOS +GOptionGroup *qmicli_qos_get_option_group (void); +gboolean qmicli_qos_options_enabled (void); +void qmicli_qos_run (QmiDevice *device, + QmiClientQos *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_GAS +GOptionGroup *qmicli_gas_get_option_group (void); +gboolean qmicli_gas_options_enabled (void); +void qmicli_gas_run (QmiDevice *device, + QmiClientGas *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_GMS +GOptionGroup *qmicli_gms_get_option_group (void); +gboolean qmicli_gms_options_enabled (void); +void qmicli_gms_run (QmiDevice *device, + QmiClientGms *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_DSD +GOptionGroup *qmicli_dsd_get_option_group (void); +gboolean qmicli_dsd_options_enabled (void); +void qmicli_dsd_run (QmiDevice *device, + QmiClientDsd *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_SAR +GOptionGroup *qmicli_sar_get_option_group (void); +gboolean qmicli_sar_options_enabled (void); +void qmicli_sar_run (QmiDevice *device, + QmiClientSar *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_DPM +GOptionGroup *qmicli_dpm_get_option_group (void); +gboolean qmicli_dpm_options_enabled (void); +void qmicli_dpm_run (QmiDevice *device, + QmiClientDpm *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_FOX +GOptionGroup *qmicli_fox_get_option_group (void); +gboolean qmicli_fox_options_enabled (void); +void qmicli_fox_run (QmiDevice *device, + QmiClientFox *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_ATR +GOptionGroup *qmicli_atr_get_option_group (void); +gboolean qmicli_atr_options_enabled (void); +void qmicli_atr_run (QmiDevice *device, + QmiClientAtr *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_IMSP +GOptionGroup *qmicli_imsp_get_option_group (void); +gboolean qmicli_imsp_options_enabled (void); +void qmicli_imsp_run (QmiDevice *device, + QmiClientImsp *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_IMSA +GOptionGroup *qmicli_imsa_get_option_group (void); +gboolean qmicli_imsa_options_enabled (void); +void qmicli_imsa_run (QmiDevice *device, + QmiClientImsa *client, + GCancellable *cancellable); +#endif + +#if defined HAVE_QMI_SERVICE_IMS +GOptionGroup *qmicli_ims_get_option_group (void); +gboolean qmicli_ims_options_enabled (void); +void qmicli_ims_run (QmiDevice *device, + QmiClientIms *client, + GCancellable *cancellable); +#endif + +#endif /* __QMICLI_H__ */