blob: 4743e25359fc64b2058b3745677bb6d9b12dae96 [file] [log] [blame]
/* -*- 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) 2012 Lanedo GmbH
* Copyright (C) 2012-2017 Aleksander Morgado <aleksander@aleksander.es>
* Copyright (C) 2019 Eric Caruso <ejcaruso@chromium.org>
*/
#include "qmi-endpoint.h"
#include "qmi-helpers.h"
#include "qmi-error-types.h"
#include "qmi-errors.h"
G_DEFINE_TYPE (QmiEndpoint, qmi_endpoint, G_TYPE_OBJECT)
struct _QmiEndpointPrivate {
GByteArray *buffer;
QmiFile *file;
};
enum {
PROP_0,
PROP_FILE,
PROP_LAST
};
enum {
SIGNAL_NEW_DATA,
SIGNAL_HANGUP,
SIGNAL_LAST
};
static GParamSpec *properties[PROP_LAST];
static guint signals[SIGNAL_LAST] = { 0 };
/*****************************************************************************/
gboolean
qmi_endpoint_parse_buffer (QmiEndpoint *self,
QmiMessageHandler handler,
gpointer user_data,
GError **error)
{
do {
GError *inner_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 (self->priv->buffer->len > 0 &&
self->priv->buffer->data[0] != QMI_MESSAGE_QMUX_MARKER) {
g_set_error (error,
QMI_PROTOCOL_ERROR,
QMI_PROTOCOL_ERROR_MALFORMED_MESSAGE,
"QMI framing error detected");
g_signal_emit (self, signals[SIGNAL_HANGUP], 0);
return FALSE;
}
message = qmi_message_new_from_raw (self->priv->buffer, &inner_error);
if (!message) {
if (!inner_error)
/* More data we need */
return TRUE;
/* Warn about the issue */
g_warning ("[%s] invalid message received: '%s'",
qmi_file_get_path_display (self->priv->file),
inner_error->message);
g_error_free (inner_error);
if (qmi_utils_get_traces_enabled ()) {
gchar *printable;
guint len = MIN (self->priv->buffer->len, 2048);
printable = qmi_helpers_str_hex (self->priv->buffer->data, len, ':');
g_debug ("<<<<<< RAW INVALID MESSAGE:\n"
"<<<<<< length = %u\n"
"<<<<<< data = %s\n",
self->priv->buffer->len, /* show full buffer len */
printable);
g_free (printable);
}
} else {
/* Play with the received message */
handler (message, user_data);
qmi_message_unref (message);
}
} while (self->priv->buffer->len > 0);
return TRUE;
}
void
qmi_endpoint_add_message (QmiEndpoint *self,
const guint8 *data,
guint len)
{
self->priv->buffer = g_byte_array_append (self->priv->buffer, data, len);
g_signal_emit (self, signals[SIGNAL_NEW_DATA], 0);
}
/*****************************************************************************/
static gboolean
endpoint_setup_indications_finish (QmiEndpoint *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
endpoint_setup_indications (QmiEndpoint *self,
guint timeout,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, cancellable, callback, user_data);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
/*****************************************************************************/
const gchar *
qmi_endpoint_get_name (QmiEndpoint *self)
{
return qmi_file_get_path_display (self->priv->file);
}
gboolean
qmi_endpoint_open_finish (QmiEndpoint *self,
GAsyncResult *res,
GError **error)
{
return QMI_ENDPOINT_GET_CLASS (self)->open_finish (self, res, error);
}
void
qmi_endpoint_open (QmiEndpoint *self,
gboolean use_proxy,
guint timeout,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_assert (QMI_ENDPOINT_GET_CLASS (self)->open &&
QMI_ENDPOINT_GET_CLASS (self)->open_finish);
return QMI_ENDPOINT_GET_CLASS (self)->open (self,
use_proxy,
timeout,
cancellable,
callback,
user_data);
}
gboolean
qmi_endpoint_is_open (QmiEndpoint *self)
{
g_assert (QMI_ENDPOINT_GET_CLASS (self)->is_open);
return QMI_ENDPOINT_GET_CLASS (self)->is_open (self);
}
gboolean
qmi_endpoint_setup_indications_finish (QmiEndpoint *self,
GAsyncResult *res,
GError **error)
{
return QMI_ENDPOINT_GET_CLASS (self)->setup_indications_finish (self, res, error);
}
void
qmi_endpoint_setup_indications (QmiEndpoint *self,
guint timeout,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_assert (QMI_ENDPOINT_GET_CLASS (self)->setup_indications &&
QMI_ENDPOINT_GET_CLASS (self)->setup_indications_finish);
return QMI_ENDPOINT_GET_CLASS (self)->setup_indications (self,
timeout,
cancellable,
callback,
user_data);
}
gboolean
qmi_endpoint_send (QmiEndpoint *self,
QmiMessage *message,
guint timeout,
GCancellable *cancellable,
GError **error)
{
g_assert (QMI_ENDPOINT_GET_CLASS (self)->send);
return QMI_ENDPOINT_GET_CLASS (self)->send (self, message, timeout, cancellable, error);
}
gboolean
qmi_endpoint_close_finish (QmiEndpoint *self,
GAsyncResult *res,
GError **error)
{
return QMI_ENDPOINT_GET_CLASS (self)->close_finish (self, res, error);
}
void
qmi_endpoint_close (QmiEndpoint *self,
guint timeout,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_assert (QMI_ENDPOINT_GET_CLASS (self)->close &&
QMI_ENDPOINT_GET_CLASS (self)->close_finish);
return QMI_ENDPOINT_GET_CLASS (self)->close (self,
timeout,
cancellable,
callback,
user_data);
}
/*****************************************************************************/
static void
set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
QmiEndpoint *self = QMI_ENDPOINT (object);
switch (prop_id) {
case PROP_FILE:
g_assert (self->priv->file == NULL);
self->priv->file = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
QmiEndpoint *self = QMI_ENDPOINT (object);
switch (prop_id) {
case PROP_FILE:
g_value_set_object (value, self->priv->file);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/*****************************************************************************/
static void
qmi_endpoint_init (QmiEndpoint *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
QMI_TYPE_ENDPOINT,
QmiEndpointPrivate);
self->priv->buffer = g_byte_array_new ();
}
static void
dispose (GObject *object)
{
QmiEndpoint *self = QMI_ENDPOINT (object);
g_clear_pointer (&self->priv->buffer, g_byte_array_unref);
g_clear_object (&self->priv->file);
G_OBJECT_CLASS (qmi_endpoint_parent_class)->dispose (object);
}
static void
qmi_endpoint_class_init (QmiEndpointClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (object_class, sizeof (QmiEndpointPrivate));
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->dispose = dispose;
klass->setup_indications = endpoint_setup_indications;
klass->setup_indications_finish = endpoint_setup_indications_finish;
properties[PROP_FILE] =
g_param_spec_object (QMI_ENDPOINT_FILE,
"Device file",
"File to the underlying QMI device",
QMI_TYPE_FILE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (object_class, PROP_FILE, properties[PROP_FILE]);
signals[SIGNAL_NEW_DATA] =
g_signal_new (QMI_ENDPOINT_SIGNAL_NEW_DATA,
G_OBJECT_CLASS_TYPE (G_OBJECT_CLASS (klass)),
G_SIGNAL_RUN_LAST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
0);
signals[SIGNAL_HANGUP] =
g_signal_new (QMI_ENDPOINT_SIGNAL_HANGUP,
G_OBJECT_CLASS_TYPE (G_OBJECT_CLASS (klass)),
G_SIGNAL_RUN_LAST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
0);
}