2024-07-23 19:56:05 +00:00
|
|
|
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/*
|
2024-07-23 20:16:45 +00:00
|
|
|
* qmi-nmea -- enable location services and print NMEA strings
|
2024-07-23 19:56:05 +00:00
|
|
|
*
|
|
|
|
* 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/>.
|
|
|
|
*
|
2024-07-23 20:16:45 +00:00
|
|
|
* This program started life as qmicli, which is
|
|
|
|
*
|
2024-07-23 19:56:05 +00:00
|
|
|
* Copyright (C) 2012-2023 Aleksander Morgado <aleksander@aleksander.es>
|
|
|
|
* Copyright (c) 2022 Qualcomm Innovation Center, Inc.
|
2024-07-23 20:16:45 +00:00
|
|
|
*
|
|
|
|
* Changes Copyright (C) 2024 Daniel Barlow
|
2024-07-23 19:56:05 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#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"
|
|
|
|
|
2024-07-23 20:12:21 +00:00
|
|
|
#define PROGRAM_NAME "qmi-nmea"
|
2024-07-23 19:56:05 +00:00
|
|
|
#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;
|
|
|
|
#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
|
|
|
|
},
|
|
|
|
#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
|
|
|
|
{ "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
|
|
|
|
},
|
|
|
|
{ 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_LOC:
|
|
|
|
#if defined HAVE_QMI_SERVICE_LOC
|
|
|
|
qmicli_loc_run (dev, QMI_CLIENT_LOC (client), cancellable);
|
|
|
|
return;
|
|
|
|
#else
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
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
|
|
|
|
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;
|
2024-07-23 20:16:45 +00:00
|
|
|
open_flags |= QMI_DEVICE_OPEN_FLAGS_PROXY;
|
2024-07-23 19:56:05 +00:00
|
|
|
#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 defined HAVE_QMI_SERVICE_LOC
|
|
|
|
if (qmicli_loc_options_enabled ()) {
|
|
|
|
service = QMI_SERVICE_LOC;
|
|
|
|
actions_enabled++;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/* 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_LOC
|
|
|
|
g_option_context_add_group (context, qmicli_loc_get_option_group ());
|
|
|
|
#endif
|
|
|
|
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);
|
|
|
|
}
|