commit 1a0e19908aefde8048f062773f0a6075cdf2ea97
parent b8aa0ae9b16c4da6037db746567f9756fd41b288
Author: Daniel Barlow <dan@telent.net>
Date: Tue, 23 Jul 2024 20:56:05 +0100
add qmi-nmea placeholder (copy of qmicli)
Diffstat:
35 files changed, 31110 insertions(+), 0 deletions(-)
diff --git 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
@@ -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
@@ -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
@@ -0,0 +1 @@
+
diff --git a/pkgs/qmi-nmea/default.nix 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
@@ -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 <fabio.porcedda@gmail.com>
+ */
+
+#ifndef _COMMON_QMI_COMMON_H_
+#define _COMMON_QMI_COMMON_H_
+
+#include <glib.h>
+
+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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2012-2023 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <gio/gio.h>
+#include <glib-unix.h>
+
+#include <libqmi-glib.h>
+
+#if QMI_MBIM_QMUX_SUPPORTED
+#include <libmbim-glib.h>
+#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 <http://gnu.org/licenses/gpl-2.0.html>\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
@@ -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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2023 Daniele Palmas <dnlplm@gmail.com>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+#include <assert.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2012-2017 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2022 Freedom Liu <lk@linuxdev.top>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+#include <assert.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2019 Andreas Kling <awesomekling@gmail.com>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+#include <qmi-common.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2020 Vladimir Podshivalov <vladimir.podshivalov@outlook.com>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+#include <assert.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2015 Velocloud Inc.
+ * Copyright (C) 2012-2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2015 Velocloud Inc.
+ * Copyright (C) 2012-2017 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <glib.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2023 Dylan Van Assche <me@dylanvanassche.be>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+#include <assert.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2023 Dylan Van Assche <me@dylanvanassche.be>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+#include <assert.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2023 Dylan Van Assche <me@dylanvanassche.be>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+#include <assert.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#include "qmicli.h"
+#include "qmicli-helpers.h"
+
+/* 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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2018 Thomas Weißschuh <thomas@weissschuh.net>
+ * Copyright (C) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2012-2017 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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<empty>\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<empty>\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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013-2017 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013-2022 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#include "qmicli.h"
+#include "qmicli-helpers.h"
+
+#if defined HAVE_QMI_SERVICE_PDC
+
+#define LIST_CONFIGS_TIMEOUT_SECS 2
+#define LOAD_CONFIG_CHUNK_SIZE 0x400
+
+/* Info about config */
+typedef struct {
+ GArray *id;
+ QmiPdcConfigurationType config_type;
+ guint32 token;
+ guint32 version;
+ gchar *description;
+ guint32 total_size;
+} ConfigInfo;
+
+/* Info about loading config */
+typedef struct {
+ GMappedFile *mapped_file;
+ GArray *checksum;
+ gsize offset;
+} LoadConfigFileData;
+
+/* Context */
+typedef struct {
+ QmiDevice *device;
+ QmiClientPdc *client;
+ GCancellable *cancellable;
+ gboolean skip_cid_release;
+
+ /* local data */
+ guint timeout_id;
+ GArray *config_list;
+ guint configs_loaded;
+ GArray *active_config_id;
+ GArray *pending_config_id;
+ gboolean ids_loaded;
+ guint list_configs_indication_id;
+ guint get_selected_config_indication_id;
+
+ LoadConfigFileData *load_config_file_data;
+ guint load_config_indication_id;
+ guint get_config_info_indication_id;
+
+ guint set_selected_config_indication_id;
+ guint activate_config_indication_id;
+ guint device_removed_indication_id;
+
+ guint deactivate_config_indication_id;
+
+ guint refresh_indication_id;
+
+ guint token;
+} Context;
+static Context *ctx;
+
+/* Options */
+static gchar *list_configs_str;
+static gchar *delete_config_str;
+static gchar *activate_config_str;
+static gchar *deactivate_config_str;
+static gchar *load_config_str;
+static gboolean monitor_refresh_flag;
+static gboolean noop_flag;
+
+#if defined HAVE_QMI_MESSAGE_PDC_LIST_CONFIGS && \
+ defined HAVE_QMI_INDICATION_PDC_LIST_CONFIGS && \
+ defined HAVE_QMI_MESSAGE_PDC_GET_SELECTED_CONFIG && \
+ defined HAVE_QMI_INDICATION_PDC_GET_SELECTED_CONFIG && \
+ defined HAVE_QMI_MESSAGE_PDC_GET_CONFIG_INFO && \
+ defined HAVE_QMI_INDICATION_PDC_GET_CONFIG_INFO
+# define HAVE_QMI_ACTION_PDC_LIST_CONFIGS
+#endif
+
+#if defined HAVE_QMI_MESSAGE_PDC_ACTIVATE_CONFIG && \
+ defined HAVE_QMI_INDICATION_PDC_ACTIVATE_CONFIG && \
+ defined HAVE_QMI_MESSAGE_PDC_SET_SELECTED_CONFIG && \
+ defined HAVE_QMI_INDICATION_PDC_SET_SELECTED_CONFIG
+# define HAVE_QMI_ACTION_PDC_ACTIVATE_CONFIG
+#endif
+
+#if defined HAVE_QMI_MESSAGE_PDC_DEACTIVATE_CONFIG && \
+ defined HAVE_QMI_INDICATION_PDC_DEACTIVATE_CONFIG
+# define HAVE_QMI_ACTION_PDC_DEACTIVATE_CONFIG
+#endif
+
+#if defined HAVE_QMI_MESSAGE_PDC_LOAD_CONFIG && \
+ defined HAVE_QMI_INDICATION_PDC_LOAD_CONFIG
+# define HAVE_QMI_ACTION_PDC_LOAD_CONFIG
+#endif
+
+#if defined HAVE_QMI_INDICATION_PDC_REFRESH && \
+ defined HAVE_QMI_MESSAGE_PDC_REGISTER
+# define HAVE_QMI_ACTION_PDC_REFRESH
+#endif
+
+static GOptionEntry entries[] = {
+#if defined HAVE_QMI_ACTION_PDC_LIST_CONFIGS
+ {
+ "pdc-list-configs", 0, 0, G_OPTION_ARG_STRING, &list_configs_str,
+ "List all configs",
+ "[(platform|software)]"
+ },
+#endif
+#if defined HAVE_QMI_MESSAGE_PDC_DELETE_CONFIG
+ {
+ "pdc-delete-config", 0, 0, G_OPTION_ARG_STRING, &delete_config_str,
+ "Delete config",
+ "[(platform|software),ConfigId]"
+ },
+#endif
+#if defined HAVE_QMI_ACTION_PDC_ACTIVATE_CONFIG
+ {
+ "pdc-activate-config", 0, 0, G_OPTION_ARG_STRING, &activate_config_str,
+ "Activate config",
+ "[(platform|software),ConfigId]"
+ },
+#endif
+#if defined HAVE_QMI_ACTION_PDC_DEACTIVATE_CONFIG
+ {
+ "pdc-deactivate-config", 0, 0, G_OPTION_ARG_STRING, &deactivate_config_str,
+ "Deactivate config",
+ "[(platform|software),ConfigId]"
+ },
+#endif
+#if defined HAVE_QMI_ACTION_PDC_LOAD_CONFIG
+ {
+ "pdc-load-config", 0, 0, G_OPTION_ARG_STRING, &load_config_str,
+ "Load config to device",
+ "[Path to config]"
+ },
+#endif
+#if defined HAVE_QMI_ACTION_PDC_REFRESH
+ {
+ "pdc-monitor-refresh", 0, 0, G_OPTION_ARG_NONE, &monitor_refresh_flag,
+ "Watch for refresh indications",
+ NULL
+ },
+#endif
+ {
+ "pdc-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag,
+ "Just allocate or release a PDC client. Use with `--client-no-release-cid' and/or `--client-cid'",
+ NULL
+ },
+ { NULL, 0, 0, 0, NULL, NULL, NULL }
+};
+
+GOptionGroup *
+qmicli_pdc_get_option_group (void)
+{
+ GOptionGroup *group;
+
+ group = g_option_group_new ("pdc",
+ "PDC options:",
+ "Show platform device configurations options", NULL, NULL);
+ g_option_group_add_entries (group, entries);
+
+ return group;
+}
+
+gboolean
+qmicli_pdc_options_enabled (void)
+{
+ static guint n_actions = 0;
+ static gboolean checked = FALSE;
+
+ if (checked)
+ return !!n_actions;
+
+ n_actions = (!!list_configs_str +
+ !!delete_config_str +
+ !!activate_config_str +
+ !!deactivate_config_str +
+ !!load_config_str +
+ monitor_refresh_flag +
+ noop_flag);
+
+ if (n_actions > 1) {
+ g_printerr ("error: too many PDC actions requested\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Actions that require receiving QMI indication messages must specify that
+ * indications are expected. */
+ if (list_configs_str || activate_config_str || deactivate_config_str || load_config_str || monitor_refresh_flag)
+ qmicli_expect_indications ();
+
+ checked = TRUE;
+ return !!n_actions;
+}
+
+static Context *
+context_new (QmiDevice *device,
+ QmiClientPdc *client,
+ GCancellable *cancellable)
+{
+ Context *context;
+
+ context = g_slice_new0 (Context);
+ context->device = g_object_ref (device);
+ context->client = g_object_ref (client);
+ context->cancellable = g_object_ref (cancellable);
+ return context;
+}
+
+static void
+context_free (Context *context)
+{
+ guint i;
+
+ if (!context)
+ return;
+
+ if (context->config_list) {
+ for (i = 0; i < context->config_list->len; i++) {
+ ConfigInfo *current_config;
+
+ current_config = &g_array_index (context->config_list, ConfigInfo, i);
+ g_free (current_config->description);
+ if (current_config->id)
+ g_array_unref (current_config->id);
+ }
+ g_array_unref (context->config_list);
+ g_signal_handler_disconnect (context->client, context->list_configs_indication_id);
+ g_signal_handler_disconnect (context->client, context->get_config_info_indication_id);
+ g_signal_handler_disconnect (context->client, context->get_selected_config_indication_id);
+ }
+
+ if (context->load_config_file_data) {
+ g_array_unref (context->load_config_file_data->checksum);
+ g_mapped_file_unref (context->load_config_file_data->mapped_file);
+ g_slice_free (LoadConfigFileData, context->load_config_file_data);
+ g_signal_handler_disconnect (context->client, context->load_config_indication_id);
+ }
+
+ if (context->set_selected_config_indication_id)
+ g_signal_handler_disconnect (context->client, context->set_selected_config_indication_id);
+
+ if (context->activate_config_indication_id)
+ g_signal_handler_disconnect (context->client, context->activate_config_indication_id);
+
+ if (context->device_removed_indication_id)
+ g_signal_handler_disconnect (context->device, context->device_removed_indication_id);
+
+ if (context->deactivate_config_indication_id)
+ g_signal_handler_disconnect (context->client, context->deactivate_config_indication_id);
+
+ g_object_unref (context->cancellable);
+ g_object_unref (context->client);
+ g_object_unref (context->device);
+
+ g_slice_free (Context, context);
+}
+
+static void
+operation_shutdown (gboolean operation_status)
+{
+ /* Cleanup context and finish async operation */
+ qmicli_async_operation_done (operation_status, ctx->skip_cid_release);
+ context_free (ctx);
+ ctx = NULL;
+}
+
+/******************************************************************************/
+/* Common */
+
+#if defined HAVE_QMI_ACTION_PDC_ACTIVATE_CONFIG || \
+ defined HAVE_QMI_ACTION_PDC_DEACTIVATE_CONFIG || \
+ defined HAVE_QMI_MESSAGE_PDC_DELETE_CONFIG
+
+static gboolean
+parse_type_and_id (const gchar *str,
+ QmiPdcConfigurationType *config_type,
+ GArray **id)
+{
+ guint num_parts;
+ g_auto(GStrv) substrings = NULL;
+
+ substrings = g_strsplit (str, ",", -1);
+ num_parts = g_strv_length (substrings);
+
+ if (num_parts != 2) {
+ g_printerr ("Expected 2 parameters, but found %u\n", num_parts);
+ return FALSE;
+ }
+
+ if (!qmicli_read_pdc_configuration_type_from_string (substrings[0], config_type)) {
+ g_printerr ("Incorrect id specified: %s\n", substrings[0]);
+ return FALSE;
+ }
+
+ if (!qmicli_read_binary_array_from_string (substrings[1], id)) {
+ g_printerr ("Incorrect config type specified: %s\n", substrings[1]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#endif /* HAVE_QMI_ACTION_PDC_ACTIVATE_CONFIG
+ * HAVE_QMI_ACTION_PDC_DEACTIVATE_CONFIG
+ * HAVE_QMI_MESSAGE_PDC_DELETE_CONFIG */
+
+/******************************************************************************/
+/* List configs and get selected config */
+
+#if defined HAVE_QMI_ACTION_PDC_LIST_CONFIGS
+
+static const char *
+status_string (GArray *id)
+{
+ if (!id)
+ return "Unknown";
+ if (ctx->active_config_id &&
+ id->len == ctx->active_config_id->len &&
+ memcmp (id->data, ctx->active_config_id->data, id->len) == 0)
+ return "Active";
+ if (ctx->pending_config_id &&
+ id->len == ctx->pending_config_id->len &&
+ memcmp (id->data, ctx->pending_config_id->data, id->len) == 0)
+ return "Pending";
+ return "Inactive";
+}
+
+static void
+print_configs (GArray *configs)
+{
+ guint i;
+
+ g_printf ("Total configurations: %u\n", ctx->config_list->len);
+ for (i = 0; i < ctx->config_list->len; i++) {
+ ConfigInfo *current_config;
+ char *id_str;
+
+ current_config = &g_array_index (ctx->config_list, ConfigInfo, i);
+
+ g_printf ("Configuration %u:\n", i + 1);
+ g_printf ("\tDescription: %s\n", current_config->description);
+ g_printf ("\tType: %s\n", qmi_pdc_configuration_type_get_string (current_config->config_type));
+ g_printf ("\tSize: %u\n", current_config->total_size);
+ g_printf ("\tStatus: %s\n", status_string (current_config->id));
+ g_printf ("\tVersion: 0x%X\n", current_config->version);
+ id_str = qmicli_get_raw_data_printable (current_config->id, 80, "");
+ g_printf ("\tID: %s\n", id_str ? id_str : "none");
+ g_free (id_str);
+ }
+}
+
+static void
+check_list_config_completed (void)
+{
+ if (ctx->configs_loaded == ctx->config_list->len &&
+ ctx->ids_loaded) {
+ print_configs (ctx->config_list);
+ operation_shutdown (TRUE);
+ }
+}
+
+static void
+get_config_info_ready (QmiClientPdc *client,
+ GAsyncResult *res)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(QmiMessagePdcGetConfigInfoOutput) output = NULL;
+
+ output = qmi_client_pdc_get_config_info_finish (client, res, &error);
+ if (!output) {
+ g_printerr ("error: operation failed: %s\n", error->message);
+ operation_shutdown (FALSE);
+ return;
+ }
+
+ if (!qmi_message_pdc_get_config_info_output_get_result (output, &error)) {
+ g_printerr ("error: couldn't get config info: %s\n", error->message);
+ operation_shutdown (FALSE);
+ return;
+ }
+}
+
+static void
+get_config_info_ready_indication (QmiClientPdc *client,
+ QmiIndicationPdcGetConfigInfoOutput *output)
+{
+ g_autoptr(GError) error = NULL;
+ ConfigInfo *current_config = NULL;
+ guint32 token;
+ const gchar *description;
+ guint i;
+ guint16 error_code = 0;
+
+ if (!qmi_indication_pdc_get_config_info_output_get_indication_result (output, &error_code, &error)) {
+ g_printerr ("error: couldn't get config info: %s\n", error->message);
+ operation_shutdown (FALSE);
+ return;
+ }
+
+ if (error_code != 0) {
+ g_printerr ("error: couldn't get config info: %s\n", qmi_protocol_error_get_string ((QmiProtocolError) error_code));
+ operation_shutdown (FALSE);
+ return;
+ }
+
+ if (!qmi_indication_pdc_get_config_info_output_get_token (output, &token, &error)) {
+ g_printerr ("error: couldn't get config info token: %s\n", error->message);
+ operation_shutdown (FALSE);
+ return;
+ }
+
+ /* Look for the current config in the list */
+ for (i = 0; i < ctx->config_list->len; i++) {
+ current_config = &g_array_index (ctx->config_list, ConfigInfo, i);
+ if (current_config->token == token)
+ break;
+ }
+
+ /* Store total size, version and description of the current config */
+ if (!qmi_indication_pdc_get_config_info_output_get_total_size (output,
+ ¤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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#include "qmicli.h"
+#include "qmicli-helpers.h"
+
+/* 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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2020 Google Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2012-2017 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+#include <qmi-common.h>
+
+#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 (" <none>");
+ g_print ("\n");
+ }
+ else
+ g_print (" <none>\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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2014-2017 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2014-2017 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2015 Velocloud Inc.
+ * Copyright (C) 2012-2017 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2015-2017 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2012-2023 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <gio/gio.h>
+#include <glib-unix.h>
+
+#include <libqmi-glib.h>
+
+#if QMI_MBIM_QMUX_SUPPORTED
+#include <libmbim-glib.h>
+#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 <http://gnu.org/licenses/gpl-2.0.html>\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
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2012-2017 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <glib.h>
+#include <libqmi-glib.h>
+
+#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__ */