qmi-proxy: initial implementation
This commit is contained in:
parent
4f10b4ed23
commit
852783f222
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" } ] }
|
||||
|
||||
]
|
||||
|
|
|
@ -78,6 +78,24 @@ QmiDevicePrivate
|
|||
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
|
||||
|
@ -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
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
|
||||
SUBDIRS = libqmi-glib qmicli
|
||||
SUBDIRS = libqmi-glib qmicli qmi-proxy
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -142,6 +142,20 @@ get_qmi_flags (QmiMessage *self)
|
|||
return ((struct full_message *)(self->data))->qmi.service.header.flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue