diff options
author | Aleksander Morgado <aleksander@lanedo.com> | 2013-08-06 13:01:54 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2013-09-05 15:36:35 +0200 |
commit | 852783f222a0db3666ef4ab24879ec20ff87c128 (patch) | |
tree | cf03f1cc6291974b235980506e7e73c448ca37d0 | |
parent | 4f10b4ed239cf25c2fa3205baaab08889cdefd0f (diff) | |
download | libqmi-852783f222a0db3666ef4ab24879ec20ff87c128.tar.gz |
qmi-proxy: initial implementation
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | data/qmi-service-ctl.json | 16 | ||||
-rw-r--r-- | docs/reference/libqmi-glib/libqmi-glib-common.sections | 19 | ||||
-rw-r--r-- | docs/reference/libqmi-glib/libqmi-glib-docs.xml | 1 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/libqmi-glib/Makefile.am | 6 | ||||
-rw-r--r-- | src/libqmi-glib/libqmi-glib.h | 1 | ||||
-rw-r--r-- | src/libqmi-glib/qmi-message.c | 14 | ||||
-rw-r--r-- | src/libqmi-glib/qmi-message.h | 1 | ||||
-rw-r--r-- | src/libqmi-glib/qmi-proxy.c | 521 | ||||
-rw-r--r-- | src/libqmi-glib/qmi-proxy.h | 55 | ||||
-rw-r--r-- | src/qmi-proxy/Makefile.am | 16 | ||||
-rw-r--r-- | src/qmi-proxy/qmi-proxy.c | 171 |
14 files changed, 824 insertions, 4 deletions
@@ -62,6 +62,10 @@ src/qmicli/test/Makefile src/qmicli/test/Makefile.in src/qmicli/test/test-helpers +src/qmi-proxy/.deps +src/qmi-proxy/.libs +src/qmi-proxy/qmi-proxy + build-aux/qmi-codegen/*.pyc docs/reference/libqmi-glib/version.xml diff --git a/configure.ac b/configure.ac index 91257c99..343126dc 100644 --- a/configure.ac +++ b/configure.ac @@ -97,6 +97,7 @@ AC_CONFIG_FILES([Makefile src/libqmi-glib/test/Makefile src/qmicli/Makefile src/qmicli/test/Makefile + src/qmi-proxy/Makefile utils/Makefile docs/Makefile docs/reference/Makefile diff --git a/data/qmi-service-ctl.json b/data/qmi-service-ctl.json index 1ce2ddc6..1e293f7c 100644 --- a/data/qmi-service-ctl.json +++ b/data/qmi-service-ctl.json @@ -144,5 +144,19 @@ { "name" : "Sync", "type" : "Indication", "service" : "CTL", - "id" : "0x0027" } + "id" : "0x0027" }, + + // ********************************************************************************* + // Internal + { "name" : "Internal Proxy Open", + "type" : "Message", + "service" : "CTL", + "id" : "0xFF00", + "input" : [ { "name" : "Device Path", + "id" : "0x01", + "mandatory" : "yes", + "type" : "TLV", + "format" : "string" } ], + "output" : [ { "common-ref" : "Operation Result" } ] } + ] diff --git a/docs/reference/libqmi-glib/libqmi-glib-common.sections b/docs/reference/libqmi-glib/libqmi-glib-common.sections index 4c7bb795..7048e945 100644 --- a/docs/reference/libqmi-glib/libqmi-glib-common.sections +++ b/docs/reference/libqmi-glib/libqmi-glib-common.sections @@ -79,6 +79,24 @@ qmi_device_get_type </SECTION> <SECTION> +<FILE>qmi-proxy</FILE> +<TITLE>QmiProxy</TITLE> +QMI_PROXY_SOCKET_PATH +QmiProxy +qmi_proxy_new +<SUBSECTION Standard> +QmiProxyClass +QMI_PROXY +QMI_PROXY_CLASS +QMI_PROXY_GET_CLASS +QMI_IS_PROXY +QMI_IS_PROXY_CLASS +QMI_TYPE_PROXY +QmiProxyPrivate +qmi_proxy_get_type +</SECTION> + +<SECTION> <FILE>qmi-enums</FILE> QmiService <SUBSECTION Methods> @@ -731,6 +749,7 @@ qmi_message_new_from_raw qmi_message_response_new qmi_message_ref qmi_message_unref +qmi_message_is_request qmi_message_is_response qmi_message_is_indication qmi_message_get_service diff --git a/docs/reference/libqmi-glib/libqmi-glib-docs.xml b/docs/reference/libqmi-glib/libqmi-glib-docs.xml index 706312ec..1795b596 100644 --- a/docs/reference/libqmi-glib/libqmi-glib-docs.xml +++ b/docs/reference/libqmi-glib/libqmi-glib-docs.xml @@ -43,6 +43,7 @@ <xi:include href="xml/qmi-message.xml"/> <xi:include href="xml/qmi-device.xml"/> <xi:include href="xml/qmi-client.xml"/> + <xi:include href="xml/qmi-proxy.xml"/> <xi:include href="xml/qmi-enums.xml"/> <xi:include href="xml/qmi-errors.xml"/> <xi:include href="xml/qmi-utils.xml"/> diff --git a/src/Makefile.am b/src/Makefile.am index 6d08b9c1..9f52adf7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,2 +1,2 @@ -SUBDIRS = libqmi-glib qmicli +SUBDIRS = libqmi-glib qmicli qmi-proxy diff --git a/src/libqmi-glib/Makefile.am b/src/libqmi-glib/Makefile.am index b97c5731..76762805 100644 --- a/src/libqmi-glib/Makefile.am +++ b/src/libqmi-glib/Makefile.am @@ -30,7 +30,8 @@ libqmi_glib_la_SOURCES = \ qmi-utils.h qmi-utils.c \ qmi-message.h qmi-message.c \ qmi-device.h qmi-device.c \ - qmi-client.h qmi-client.c + qmi-client.h qmi-client.c \ + qmi-proxy.h qmi-proxy.c libqmi_glib_la_LIBADD = \ ${top_builddir}/src/libqmi-glib/generated/libqmi-glib-generated.la \ @@ -57,7 +58,8 @@ include_HEADERS = \ qmi-utils.h \ qmi-message.h \ qmi-device.h \ - qmi-client.h + qmi-client.h \ + qmi-proxy.h EXTRA_DIST = \ qmi-version.h.in diff --git a/src/libqmi-glib/libqmi-glib.h b/src/libqmi-glib/libqmi-glib.h index 2143e860..c4f8ba28 100644 --- a/src/libqmi-glib/libqmi-glib.h +++ b/src/libqmi-glib/libqmi-glib.h @@ -31,6 +31,7 @@ #include "qmi-version.h" #include "qmi-device.h" #include "qmi-client.h" +#include "qmi-proxy.h" #include "qmi-message.h" #include "qmi-enums.h" #include "qmi-utils.h" diff --git a/src/libqmi-glib/qmi-message.c b/src/libqmi-glib/qmi-message.c index 55165623..c016f5e4 100644 --- a/src/libqmi-glib/qmi-message.c +++ b/src/libqmi-glib/qmi-message.c @@ -143,6 +143,20 @@ get_qmi_flags (QmiMessage *self) } /** + * qmi_message_is_request: + * @self: a #QmiMessage. + * + * Checks whether the given #QmiMessage is a request. + * + * Returns: %TRUE if @self is a request message, %FALSE otherwise. + */ +gboolean +qmi_message_is_request (QmiMessage *self) +{ + return (!qmi_message_is_response (self) && !qmi_message_is_indication (self)); +} + +/** * qmi_message_is_response: * @self: a #QmiMessage. * diff --git a/src/libqmi-glib/qmi-message.h b/src/libqmi-glib/qmi-message.h index ccc0167c..958df035 100644 --- a/src/libqmi-glib/qmi-message.h +++ b/src/libqmi-glib/qmi-message.h @@ -67,6 +67,7 @@ void qmi_message_unref (QmiMessage *self); /*****************************************************************************/ /* QMI Message content getters */ +gboolean qmi_message_is_request (QmiMessage *self); gboolean qmi_message_is_response (QmiMessage *self); gboolean qmi_message_is_indication (QmiMessage *self); QmiService qmi_message_get_service (QmiMessage *self); diff --git a/src/libqmi-glib/qmi-proxy.c b/src/libqmi-glib/qmi-proxy.c new file mode 100644 index 00000000..bb692c04 --- /dev/null +++ b/src/libqmi-glib/qmi-proxy.c @@ -0,0 +1,521 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + * libqmi-glib -- GLib/GIO based library to control QMI devices + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright (C) 2013 Aleksander Morgado <aleksander@lanedo.com> + */ + +#include <string.h> +#include <ctype.h> +#include <sys/file.h> +#include <errno.h> + +#include <glib.h> +#include <glib/gstdio.h> +#include <gio/gunixsocketaddress.h> + +#include "qmi-error-types.h" +#include "qmi-device.h" +#include "qmi-ctl.h" +#include "qmi-utils.h" +#include "qmi-proxy.h" + +#define BUFFER_SIZE 512 +#define QMI_MESSAGE_CTL_INTERNAL_PROXY_OPEN 0xFF00 +#define QMI_MESSAGE_CTL_INTERNAL_PROXY_OPEN_INPUT_TLV_DEVICE_PATH 0x01 + +G_DEFINE_TYPE (QmiProxy, qmi_proxy, G_TYPE_OBJECT) + +struct _QmiProxyPrivate { + /* Unix socket service */ + GSocketService *socket_service; + + /* Clients */ + GList *clients; + + /* Devices */ + GList *devices; +}; + +/*****************************************************************************/ + +typedef struct { + QmiProxy *proxy; /* not full ref */ + GSocketConnection *connection; + GSource *connection_readable_source; + GByteArray *buffer; + QmiDevice *device; + QmiMessage *internal_proxy_open_request; +} Client; + +static gboolean connection_readable_cb (GSocket *socket, GIOCondition condition, Client *client); + +static void +client_free (Client *client) +{ + g_source_destroy (client->connection_readable_source); + g_source_unref (client->connection_readable_source); + + g_output_stream_close (g_io_stream_get_output_stream (G_IO_STREAM (client->connection)), NULL, NULL); + + if (client->buffer) + g_byte_array_unref (client->buffer); + + if (client->internal_proxy_open_request) + g_byte_array_unref (client->internal_proxy_open_request); + + g_object_unref (client->connection); + g_slice_free (Client, client); +} + +static void +connection_close (Client *client) +{ + QmiProxy *self = client->proxy; + + client_free (client); + self->priv->clients = g_list_remove (self->priv->clients, client); +} + +static QmiDevice * +find_device_for_path (QmiProxy *self, + const gchar *path) +{ + GList *l; + + for (l = self->priv->devices; l; l = g_list_next (l)) { + QmiDevice *device; + + device = (QmiDevice *)l->data; + + /* Return if found */ + if (g_str_equal (qmi_device_get_path (device), path)) + return device; + } + + return NULL; +} + +static gboolean +send_message (Client *client, + QmiMessage *message, + GError **error) +{ + if (!g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (client->connection)), + message->data, + message->len, + NULL, /* bytes_written */ + NULL, /* cancellable */ + error)) { + g_prefix_error (error, "Cannot send message to client: "); + return FALSE; + } + + return TRUE; +} + +static void +complete_internal_proxy_open (Client *client) +{ + QmiMessage *response; + GError *error = NULL; + + g_debug ("connection to QMI device '%s' established", qmi_device_get_path (client->device)); + + g_assert (client->internal_proxy_open_request != NULL); + response = qmi_message_response_new (client->internal_proxy_open_request, QMI_PROTOCOL_ERROR_NONE); + + if (!send_message (client, response, &error)) { + qmi_message_unref (response); + connection_close (client); + return; + } + + qmi_message_unref (response); + qmi_message_unref (client->internal_proxy_open_request); + client->internal_proxy_open_request = NULL; +} + +static void +device_open_ready (QmiDevice *device, + GAsyncResult *res, + Client *client) +{ + QmiProxy *self = client->proxy; + QmiDevice *existing; + GError *error = NULL; + + if (!qmi_device_open_finish (device, res, &error)) { + g_debug ("couldn't open QMI device: %s", error->message); + g_debug ("client connection closed"); + connection_close (client); + g_error_free (error); + return; + } + + /* Store device in the proxy independently */ + existing = find_device_for_path (self, qmi_device_get_path (client->device)); + if (existing) { + /* Race condition, we created two QmiDevices for the same port, just skip ours, no big deal */ + g_object_unref (client->device); + client->device = g_object_ref (existing); + } else { + /* Keep the newly added device in the proxy */ + self->priv->devices = g_list_append (self->priv->devices, g_object_ref (client->device)); + } + + complete_internal_proxy_open (client); +} + +static void +device_new_ready (GObject *source, + GAsyncResult *res, + Client *client) +{ + GError *error = NULL; + + client->device = qmi_device_new_finish (res, &error); + if (!client->device) { + g_debug ("couldn't open QMI device: %s", error->message); + g_debug ("client connection closed"); + connection_close (client); + g_error_free (error); + return; + } + + qmi_device_open (client->device, + QMI_DEVICE_OPEN_FLAGS_NONE, + 10, + NULL, + (GAsyncReadyCallback)device_open_ready, + client); +} + +static gboolean +process_internal_proxy_open (Client *client, + QmiMessage *message) +{ + QmiProxy *self = client->proxy; + const guint8 *buffer; + guint16 buffer_len; + gchar *device_file_path; + + buffer = qmi_message_get_raw_tlv (message, + QMI_MESSAGE_CTL_INTERNAL_PROXY_OPEN_INPUT_TLV_DEVICE_PATH, + &buffer_len); + if (!buffer) { + g_debug ("ignoring message from client: invalid proxy open request"); + return FALSE; + } + + qmi_utils_read_string_from_buffer (&buffer, &buffer_len, 0, 0, &device_file_path); + g_debug ("valid request to open connection to QMI device file: %s", device_file_path); + + + /* Keep it */ + client->internal_proxy_open_request = qmi_message_ref (message); + + client->device = find_device_for_path (self, device_file_path); + + /* Need to create a device ourselves */ + if (!client->device) { + GFile *file; + + file = g_file_new_for_path (device_file_path); + qmi_device_new (file, + NULL, + (GAsyncReadyCallback)device_new_ready, + client); + g_object_unref (file); + g_free (device_file_path); + return TRUE; + } + + g_free (device_file_path); + + /* Keep a reference to the device in the client */ + g_object_ref (client->device); + + complete_internal_proxy_open (client); + return FALSE; +} + +static void +device_command_ready (QmiDevice *device, + GAsyncResult *res, + Client *client) +{ + QmiMessage *response; + GError *error = NULL; + + response = qmi_device_command_finish (device, res, &error); + if (!response) { + g_warning ("sending request to device failed: %s", error->message); + g_error_free (error); + return; + } + + if (!send_message (client, response, &error)) { + qmi_message_unref (response); + connection_close (client); + return; + } + + qmi_message_unref (response); +} + +static gboolean +process_message (Client *client, + QmiMessage *message) +{ + /* Accept only request messages from the client */ + if (!qmi_message_is_request (message)) { + g_debug ("invalid message from client: not a request message"); + return FALSE; + } + + if (qmi_message_get_service (message) == QMI_SERVICE_CTL) { + if (qmi_message_get_message_id (message) == QMI_MESSAGE_CTL_INTERNAL_PROXY_OPEN) + return process_internal_proxy_open (client, message); + + /* CTL, fixup transaction id and keep on */ + } + + /* Non-CTL service, just forward to qmi device */ + + qmi_device_command (client->device, + message, + 10, /* for now... */ + NULL, + (GAsyncReadyCallback)device_command_ready, + client); + return TRUE; +} + +static void +parse_request (Client *client) +{ + do { + GError *error = NULL; + QmiMessage *message; + + /* Every message received must start with the QMUX marker. + * If it doesn't, we broke framing :-/ + * If we broke framing, an error should be reported and the device + * should get closed */ + if (client->buffer->len > 0 && + client->buffer->data[0] != QMI_MESSAGE_QMUX_MARKER) { + /* TODO: Report fatal error */ + g_warning ("QMI framing error detected"); + return; + } + + message = qmi_message_new_from_raw (client->buffer, &error); + if (!message) { + if (!error) + /* More data we need */ + return; + + /* Warn about the issue */ + g_warning ("Invalid QMI message received: '%s'", + error->message); + g_error_free (error); + } else { + /* Play with the received message */ + process_message (client, message); + qmi_message_unref (message); + } + } while (client->buffer->len > 0); +} + +static gboolean +connection_readable_cb (GSocket *socket, + GIOCondition condition, + Client *client) +{ + guint8 buffer[BUFFER_SIZE]; + GError *error = NULL; + gssize read; + + if (condition & G_IO_HUP || condition & G_IO_ERR) { + g_debug ("client connection closed"); + connection_close (client); + return FALSE; + } + + if (!(condition & G_IO_IN || condition & G_IO_PRI)) + return TRUE; + + read = g_input_stream_read (g_io_stream_get_input_stream (G_IO_STREAM (client->connection)), + buffer, + BUFFER_SIZE, + NULL, + &error); + if (read < 0) { + g_warning ("Error reading from istream: %s", error ? error->message : "unknown"); + if (error) + g_error_free (error); + /* Close the device */ + connection_close (client); + return FALSE; + } + + if (read == 0) + return TRUE; + + /* else, read > 0 */ + if (!G_UNLIKELY (client->buffer)) + client->buffer = g_byte_array_sized_new (read); + g_byte_array_append (client->buffer, buffer, read); + + /* Try to parse input messages */ + parse_request (client); + + return TRUE; +} + +static void +incoming_cb (GSocketService *service, + GSocketConnection *connection, + GObject *unused, + QmiProxy *self) +{ + Client *client; + + g_debug ("client connection open..."); + + /* Create client */ + client = g_slice_new0 (Client); + client->proxy = self; + client->connection = g_object_ref (connection); + client->connection_readable_source = g_socket_create_source (g_socket_connection_get_socket (client->connection), + G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, + NULL); + g_source_set_callback (client->connection_readable_source, + (GSourceFunc)connection_readable_cb, + client, + NULL); + g_source_attach (client->connection_readable_source, NULL); + + /* Keep the client info around */ + self->priv->clients = g_list_append (self->priv->clients, client); +} + +static gboolean +setup_socket_service (QmiProxy *self, + GError **error) +{ + GSocketAddress *socket_address; + GSocket *socket; + + socket = g_socket_new (G_SOCKET_FAMILY_UNIX, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + error); + if (!socket) + return FALSE; + + /* Bind to address */ + socket_address = (g_unix_socket_address_new_with_type ( + QMI_PROXY_SOCKET_PATH, + -1, + G_UNIX_SOCKET_ADDRESS_ABSTRACT)); + if (!g_socket_bind (socket, socket_address, TRUE, error)) + return FALSE; + g_object_unref (socket_address); + + g_debug ("creating UNIX socket service..."); + + /* Listen */ + if (!g_socket_listen (socket, error)) { + g_object_unref (socket); + return FALSE; + } + + /* Create socket service */ + self->priv->socket_service = g_socket_service_new (); + g_signal_connect (self->priv->socket_service, "incoming", G_CALLBACK (incoming_cb), self); + if (!g_socket_listener_add_socket (G_SOCKET_LISTENER (self->priv->socket_service), + socket, + NULL, /* don't pass an object, will take a reference */ + error)) { + g_prefix_error (error, "Error adding socket at '%s' to socket service: ", QMI_PROXY_SOCKET_PATH); + g_object_unref (socket); + return FALSE; + } + + g_debug ("starting UNIX socket service at '%s'...", QMI_PROXY_SOCKET_PATH); + g_socket_service_start (self->priv->socket_service); + g_object_unref (socket); + return TRUE; +} + +/*****************************************************************************/ + +QmiProxy * +qmi_proxy_new (GError **error) +{ + QmiProxy *self; + + self = g_object_new (QMI_TYPE_PROXY, NULL); + if (!setup_socket_service (self, error)) + g_clear_object (&self); + return self; +} + +static void +qmi_proxy_init (QmiProxy *self) +{ + /* Setup private data */ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + QMI_TYPE_PROXY, + QmiProxyPrivate); +} + +static void +dispose (GObject *object) +{ + QmiProxyPrivate *priv = QMI_PROXY (object)->priv; + + if (priv->clients) { + g_list_free_full (priv->clients, (GDestroyNotify) client_free); + priv->clients = NULL; + } + + if (priv->socket_service) { + if (g_socket_service_is_active (priv->socket_service)) + g_socket_service_stop (priv->socket_service); + g_clear_object (&priv->socket_service); + g_unlink (QMI_PROXY_SOCKET_PATH); + g_debug ("UNIX socket service at '%s' stopped", QMI_PROXY_SOCKET_PATH); + } + + G_OBJECT_CLASS (qmi_proxy_parent_class)->dispose (object); +} + +static void +qmi_proxy_class_init (QmiProxyClass *proxy_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (proxy_class); + + g_type_class_add_private (object_class, sizeof (QmiProxyPrivate)); + + /* Virtual methods */ + object_class->dispose = dispose; +} diff --git a/src/libqmi-glib/qmi-proxy.h b/src/libqmi-glib/qmi-proxy.h new file mode 100644 index 00000000..9845dbef --- /dev/null +++ b/src/libqmi-glib/qmi-proxy.h @@ -0,0 +1,55 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * libqmi-glib -- GLib/GIO based library to control QMI devices + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright (C) 2013 Aleksander Morgado <aleksander@lanedo.com> + */ + +#ifndef QMI_PROXY_H +#define QMI_PROXY_H + +#include <glib-object.h> +#include <gio/gio.h> + +#define QMI_TYPE_PROXY (qmi_proxy_get_type ()) +#define QMI_PROXY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), QMI_TYPE_PROXY, QmiProxy)) +#define QMI_PROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), QMI_TYPE_PROXY, QmiProxyClass)) +#define QMI_IS_PROXY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), QMI_TYPE_PROXY)) +#define QMI_IS_PROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), QMI_TYPE_PROXY)) +#define QMI_PROXY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), QMI_TYPE_PROXY, QmiProxyClass)) + +typedef struct _QmiProxy QmiProxy; +typedef struct _QmiProxyClass QmiProxyClass; +typedef struct _QmiProxyPrivate QmiProxyPrivate; + +#define QMI_PROXY_SOCKET_PATH "qmi-proxy" + +struct _QmiProxy { + GObject parent; + QmiProxyPrivate *priv; +}; + +struct _QmiProxyClass { + GObjectClass parent; +}; + +GType qmi_proxy_get_type (void); + +QmiProxy *qmi_proxy_new (GError **error); + +#endif /* QMI_PROXY_H */ diff --git a/src/qmi-proxy/Makefile.am b/src/qmi-proxy/Makefile.am new file mode 100644 index 00000000..114973e1 --- /dev/null +++ b/src/qmi-proxy/Makefile.am @@ -0,0 +1,16 @@ + +sbin_PROGRAMS = qmi-proxy + +qmi_proxy_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/libqmi-glib \ + -I$(top_srcdir)/src/libqmi-glib/generated \ + -I$(top_builddir)/src/libqmi-glib \ + -I$(top_builddir)/src/libqmi-glib/generated + +qmi_proxy_SOURCES = qmi-proxy.c + +qmi_proxy_LDADD = \ + $(GLIB_LIBS) \ + $(top_builddir)/src/libqmi-glib/libqmi-glib.la diff --git a/src/qmi-proxy/qmi-proxy.c b/src/qmi-proxy/qmi-proxy.c new file mode 100644 index 00000000..e879e725 --- /dev/null +++ b/src/qmi-proxy/qmi-proxy.c @@ -0,0 +1,171 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * qmid -- A proxy to communicate with QMI ports + * + * 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 Aleksander Morgado <aleksander@gnu.org> + */ + +#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> + +#define PROGRAM_NAME "qmid" +#define PROGRAM_VERSION PACKAGE_VERSION + +/* Globals */ +static GMainLoop *loop; +static QmiProxy *proxy; + +/* Main options */ +static gboolean version_flag; + +static GOptionEntry main_entries[] = { + { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag, + "Print version", + NULL + }, + { NULL } +}; + +static gboolean +quit_cb (gpointer user_data) +{ + if (loop) { + g_warning ("Caught signal, stopping the loop..."); + g_idle_add ((GSourceFunc) g_main_loop_quit, loop); + } + + return FALSE; +} + +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; + + 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_FLAG_FATAL: + case G_LOG_LEVEL_ERROR: + log_level_str = "-Error **"; + err = TRUE; + break; + + case G_LOG_LEVEL_DEBUG: + log_level_str = "[Debug]"; + break; + + default: + log_level_str = ""; + break; + } + + g_fprintf (err ? stderr : stdout, + "[%s] %s %s\n", + time_str, + log_level_str, + message); +} + +static void +print_version_and_exit (void) +{ + g_print ("\n" + PROGRAM_NAME " " PROGRAM_VERSION "\n" + "Copyright (2013) Aleksander Morgado\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); +} + +/*****************************************************************************/ + +int main (int argc, char **argv) +{ + GError *error = NULL; + GOptionContext *context; + + setlocale (LC_ALL, ""); + + g_type_init (); + + /* Setup option context, process it and destroy it */ + context = g_option_context_new ("- Proxy for QMI devices"); + 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); + + /* Setup signals */ + g_unix_signal_add (SIGINT, quit_cb, NULL); + g_unix_signal_add (SIGHUP, quit_cb, NULL); + g_unix_signal_add (SIGTERM, quit_cb, NULL); + + /* Setup proxy */ + proxy = qmi_proxy_new (&error); + if (!proxy) { + g_printerr ("error: %s\n", error->message); + exit (EXIT_FAILURE); + } + + /* Loop */ + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + g_main_loop_unref (loop); + + /* Cleanup; releases socket and such */ + g_object_unref (proxy); + + return EXIT_SUCCESS; +} |