diff --git a/cli/Makefile.am b/cli/Makefile.am index dca3dde..b55cced 100644 --- a/cli/Makefile.am +++ b/cli/Makefile.am @@ -7,7 +7,9 @@ qmicli_CPPFLAGS = \ -I$(top_builddir)/src qmicli_SOURCES = \ - qmicli.c + qmicli.c \ + qmicli.h \ + qmicli-ctl.c qmicli_LDADD = \ $(QMICLI_LIBS) \ diff --git a/cli/qmicli-ctl.c b/cli/qmicli-ctl.c new file mode 100644 index 0000000..2b7af44 --- /dev/null +++ b/cli/qmicli-ctl.c @@ -0,0 +1,189 @@ +/* -*- 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Copyright (C) 2012 Aleksander Morgado + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include "qmicli.h" + +/* Context */ +typedef struct { + QmiDevice *device; + QmiClientCtl *client; + GCancellable *cancellable; +} Context; +static Context *ctx; + +/* Options */ +static gboolean get_version_info_flag; + +static GOptionEntry entries[] = { + { "ctl-get-version-info", 0, 0, G_OPTION_ARG_NONE, &get_version_info_flag, + "Get QMI version info", + NULL + }, + { NULL } +}; + +GOptionGroup * +qmicli_ctl_get_option_group (void) +{ + GOptionGroup *group; + + group = g_option_group_new ("ctl", + "CTL options", + "Show Control options", + NULL, + NULL); + g_option_group_add_entries (group, entries); + + return group; +} + +gboolean +qmicli_ctl_options_enabled (void) +{ + static guint n_actions = 0; + static gboolean checked = FALSE; + + if (checked) + return !!n_actions; + + n_actions = (get_version_info_flag); + + if (n_actions > 1) { + g_printerr ("error: too many CTL actions requested\n"); + exit (EXIT_FAILURE); + } + + checked = TRUE; + return !!n_actions; +} + +static void +context_free (Context *ctx) +{ + if (!ctx) + return; + + if (ctx->cancellable) + g_object_unref (ctx->cancellable); + if (ctx->device) + g_object_unref (ctx->device); + if (ctx->client) + g_object_unref (ctx->client); + g_slice_free (Context, ctx); +} + +static void +client_release_ready (QmiClient *client, + GAsyncResult *res) +{ + GError *error = NULL; + + if (!qmi_client_release_finish (client, res, &error)) { + g_printerr ("error: couldn't release client CID: %s", error->message); + exit (EXIT_FAILURE); + } + + g_debug ("Client CID released"); + + /* Cleanup context and finish async operation */ + context_free (ctx); + qmicli_async_operation_done (); +} + +static void +shutdown (void) +{ + /* NOTE: we don't really need to run explicit release for the CTL client */ + qmi_client_release (QMI_CLIENT (ctx->client), + 10, + (GAsyncReadyCallback)client_release_ready, + NULL); +} + +static void +get_version_info_ready (QmiClientCtl *client, + GAsyncResult *result) +{ + GError *error = NULL; + GArray *services; + guint i; + + services = qmi_client_ctl_get_version_info_finish (client, result, &error); + if (!services) { + g_printerr ("error: couldn't get version info: %s\n", + error->message); + exit (EXIT_FAILURE); + } + + g_print ("[%s] Supported services:\n", + qmi_device_get_path_display (ctx->device)); + for (i = 0; i < services->len; i++) { + QmiCtlVersionInfo *service; + + service = &g_array_index (services, QmiCtlVersionInfo, i); + g_print ("\t%s (%u.%u)\n", + qmi_service_get_string (service->service_type), + service->major_version, + service->minor_version); + } + + g_array_unref (services); + + shutdown (); +} + +void +qmicli_ctl_run (QmiDevice *device, + GCancellable *cancellable) +{ + /* Initialize context */ + ctx = g_slice_new (Context); + ctx->device = g_object_ref (device); + if (cancellable) + ctx->cancellable = g_object_ref (cancellable); + + /* The CTL client should be directly retrievable from the QmiDevice */ + ctx->client = qmi_device_get_client_ctl (device); + + /* Request to get version info? */ + if (get_version_info_flag) { + g_debug ("Asynchronously getting version info..."); + qmi_client_ctl_get_version_info (ctx->client, + 10, + ctx->cancellable, + (GAsyncReadyCallback)get_version_info_ready, + NULL); + return; + } + + g_warn_if_reached (); +} diff --git a/cli/qmicli.c b/cli/qmicli.c index eddd755..081f8cd 100644 --- a/cli/qmicli.c +++ b/cli/qmicli.c @@ -30,12 +30,15 @@ #include +#include "qmicli.h" + #define PROGRAM_NAME "qmicli" #define PROGRAM_VERSION PACKAGE_VERSION /* Globals */ static GMainLoop *loop; static GCancellable *cancellable; +static QmiDevice *device; /* Main options */ static gchar *device_str; @@ -133,8 +136,8 @@ print_version_and_exit (void) /*****************************************************************************/ /* Running asynchronously */ -static void -async_operation_done (void) +void +qmicli_async_operation_done (void) { if (cancellable) { g_object_unref (cancellable); @@ -144,27 +147,9 @@ async_operation_done (void) g_main_loop_quit (loop); } -typedef struct { - GCancellable *cancellable; - QmiDevice *device; -} AsyncRunContext; - -static void -async_run_context_complete_and_free (AsyncRunContext *ctx) -{ - if (ctx->cancellable) - g_object_unref (ctx->cancellable); - if (ctx->device) - g_object_unref (ctx->device); - g_slice_free (AsyncRunContext, ctx); - - async_operation_done (); -} - static void device_open_ready (QmiDevice *device, - GAsyncResult *res, - AsyncRunContext *ctx) + GAsyncResult *res) { GError *error = NULL; @@ -174,51 +159,37 @@ device_open_ready (QmiDevice *device, exit (EXIT_FAILURE); } - if (!qmi_device_close (ctx->device, &error)) { - g_printerr ("error: couldn't close the QmiDevice: %s\n", - error->message); + /* As soon as we get the QmiDevice, launch the specific action requested */ + + /* CTL options? */ + if (qmicli_ctl_options_enabled ()) + qmicli_ctl_run (device, cancellable); + /* No options? */ + else { + g_printerr ("error: no actions specified\n"); exit (EXIT_FAILURE); } - - /* All done */ - async_run_context_complete_and_free (ctx); } static void device_new_ready (GObject *unused, - GAsyncResult *res, - AsyncRunContext *ctx) + GAsyncResult *res) { GError *error = NULL; - ctx->device = qmi_device_new_finish (res, &error); - if (!ctx->device) { + device = qmi_device_new_finish (res, &error); + if (!device) { g_printerr ("error: couldn't create QmiDevice: %s\n", error->message); exit (EXIT_FAILURE); } /* Open the device */ - qmi_device_open (ctx->device, + qmi_device_open (device, 5, - ctx->cancellable, + cancellable, (GAsyncReadyCallback)device_open_ready, - ctx); -} - -static void -run (GFile *file, - GCancellable *cancellable) -{ - AsyncRunContext *ctx; - - ctx = g_slice_new0 (AsyncRunContext); - ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL; - - qmi_device_new (file, - cancellable, - (GAsyncReadyCallback)device_new_ready, - ctx); + NULL); } /*****************************************************************************/ @@ -235,6 +206,8 @@ int main (int argc, char **argv) /* Setup option context, process it and destroy it */ context = g_option_context_new ("- Control QMI devices"); + g_option_context_add_group (context, + qmicli_ctl_get_option_group ()); g_option_context_add_main_entries (context, main_entries, NULL); g_option_context_parse (context, &argc, &argv, NULL); g_option_context_free (context); @@ -263,11 +236,17 @@ int main (int argc, char **argv) cancellable = g_cancellable_new (); loop = g_main_loop_new (NULL, FALSE); - run (file, cancellable); + /* Launch QmiDevice creation */ + qmi_device_new (file, + cancellable, + (GAsyncReadyCallback)device_new_ready, + NULL); g_main_loop_run (loop); if (cancellable) g_object_unref (cancellable); + if (device) + g_object_unref (device); g_main_loop_unref (loop); g_object_unref (file); diff --git a/cli/qmicli.h b/cli/qmicli.h new file mode 100644 index 0000000..d354c6d --- /dev/null +++ b/cli/qmicli.h @@ -0,0 +1,35 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * qmicli -- Command line interface to control QMI devices + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Copyright (C) 2012 Aleksander Morgado + */ + +#include + +#ifndef __QMICLI_H__ +#define __QMICLI_H__ + +/* Common */ +void qmicli_async_operation_done (void); + +/* CTL group */ +GOptionGroup *qmicli_ctl_get_option_group (void); +gboolean qmicli_ctl_options_enabled (void); +void qmicli_ctl_run (QmiDevice *device, + GCancellable *cancellable); + +#endif /* __QMICLI_H__ */