From cb6985816bdafc40fee6d57c6320e18906990ba7 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Tue, 3 Jul 2012 15:58:43 +0200 Subject: build: prepare sources to treat `libqmi-glib' not as the only library Instead of keeping the libqmi-glib sources under `src', we'll have a more specific `libqmi-glib' directory instead. Also, update autotools to reflect as `libqmi' the name of the project. --- .gitignore | 32 +- Makefile.am | 2 +- autogen.sh | 4 +- cli/Makefile.am | 6 +- configure.ac | 12 +- libqmi-glib/Makefile.am | 118 +++ libqmi-glib/libqmi-glib.h | 35 + libqmi-glib/qmi-client.c | 265 +++++++ libqmi-glib/qmi-client.h | 79 ++ libqmi-glib/qmi-device.c | 1812 +++++++++++++++++++++++++++++++++++++++++++ libqmi-glib/qmi-device.h | 145 ++++ libqmi-glib/qmi-enums-dms.h | 195 +++++ libqmi-glib/qmi-enums-wds.h | 197 +++++ libqmi-glib/qmi-enums.h | 86 ++ libqmi-glib/qmi-errors.h | 212 +++++ libqmi-glib/qmi-message.c | 707 +++++++++++++++++ libqmi-glib/qmi-message.h | 104 +++ libqmi-glib/qmi-utils.c | 312 ++++++++ libqmi-glib/qmi-utils.h | 95 +++ m4/compiler-warnings.m4 | 2 +- src/Makefile.am | 118 --- src/libqmi-glib.h | 35 - src/qmi-client.c | 265 ------- src/qmi-client.h | 79 -- src/qmi-device.c | 1812 ------------------------------------------- src/qmi-device.h | 145 ---- src/qmi-enums-dms.h | 195 ----- src/qmi-enums-wds.h | 197 ----- src/qmi-enums.h | 86 -- src/qmi-errors.h | 212 ----- src/qmi-message.c | 707 ----------------- src/qmi-message.h | 104 --- src/qmi-utils.c | 312 -------- src/qmi-utils.h | 95 --- 34 files changed, 4391 insertions(+), 4391 deletions(-) create mode 100644 libqmi-glib/Makefile.am create mode 100644 libqmi-glib/libqmi-glib.h create mode 100644 libqmi-glib/qmi-client.c create mode 100644 libqmi-glib/qmi-client.h create mode 100644 libqmi-glib/qmi-device.c create mode 100644 libqmi-glib/qmi-device.h create mode 100644 libqmi-glib/qmi-enums-dms.h create mode 100644 libqmi-glib/qmi-enums-wds.h create mode 100644 libqmi-glib/qmi-enums.h create mode 100644 libqmi-glib/qmi-errors.h create mode 100644 libqmi-glib/qmi-message.c create mode 100644 libqmi-glib/qmi-message.h create mode 100644 libqmi-glib/qmi-utils.c create mode 100644 libqmi-glib/qmi-utils.h delete mode 100644 src/Makefile.am delete mode 100644 src/libqmi-glib.h delete mode 100644 src/qmi-client.c delete mode 100644 src/qmi-client.h delete mode 100644 src/qmi-device.c delete mode 100644 src/qmi-device.h delete mode 100644 src/qmi-enums-dms.h delete mode 100644 src/qmi-enums-wds.h delete mode 100644 src/qmi-enums.h delete mode 100644 src/qmi-errors.h delete mode 100644 src/qmi-message.c delete mode 100644 src/qmi-message.h delete mode 100644 src/qmi-utils.c delete mode 100644 src/qmi-utils.h diff --git a/.gitignore b/.gitignore index cf98f78..1c7f91e 100644 --- a/.gitignore +++ b/.gitignore @@ -31,22 +31,22 @@ depcomp data/pkg-config/qmi-glib.pc -src/.libs -src/.deps -src/Makefile -src/Makefile.in -src/libqmi-glib.la -src/qmi-error-types.c -src/qmi-error-types.h -src/qmi-enum-types.h -src/qmi-enum-types.c -src/qmi-ctl.h -src/qmi-ctl.c -src/qmi-dms.h -src/qmi-dms.c -src/qmi-wds.h -src/qmi-wds.c -src/*.stamp +libqmi-glib/.libs +libqmi-glib/.deps +libqmi-glib/Makefile +libqmi-glib/Makefile.in +libqmi-glib/libqmi-glib.la +libqmi-glib/qmi-error-types.c +libqmi-glib/qmi-error-types.h +libqmi-glib/qmi-enum-types.h +libqmi-glib/qmi-enum-types.c +libqmi-glib/qmi-ctl.h +libqmi-glib/qmi-ctl.c +libqmi-glib/qmi-dms.h +libqmi-glib/qmi-dms.c +libqmi-glib/qmi-wds.h +libqmi-glib/qmi-wds.c +libqmi-glib/*.stamp cli/.deps cli/.libs diff --git a/Makefile.am b/Makefile.am index 4e6c6e4..0a5aa80 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = . data build-aux src cli utils +SUBDIRS = . data build-aux libqmi-glib cli utils ACLOCAL_AMFLAGS = -I m4 diff --git a/autogen.sh b/autogen.sh index cc96623..1a8a162 100755 --- a/autogen.sh +++ b/autogen.sh @@ -4,10 +4,10 @@ srcdir=`dirname $0` test -z "$srcdir" && srcdir=. REQUIRED_AUTOMAKE_VERSION=1.9 -PKG_NAME=libqmi-glib +PKG_NAME=libqmi (test -f $srcdir/configure.ac \ - && test -f $srcdir/src/libqmi-glib.h) || { + && test -f $srcdir/libqmi-glib/libqmi-glib.h) || { echo -n "**Error**: Directory "\`$srcdir\'" does not look like the" echo " top-level $PKG_NAME directory" exit 1 diff --git a/cli/Makefile.am b/cli/Makefile.am index b46fc3d..65fd0db 100644 --- a/cli/Makefile.am +++ b/cli/Makefile.am @@ -3,8 +3,8 @@ bin_PROGRAMS = qmicli qmicli_CPPFLAGS = \ $(QMICLI_CFLAGS) \ -I$(top_srcdir) \ - -I$(top_srcdir)/src \ - -I$(top_builddir)/src + -I$(top_srcdir)/libqmi-glib \ + -I$(top_builddir)/libqmi-glib qmicli_SOURCES = \ qmicli.c \ @@ -14,4 +14,4 @@ qmicli_SOURCES = \ qmicli_LDADD = \ $(QMICLI_LIBS) \ - $(top_builddir)/src/libqmi-glib.la + $(top_builddir)/libqmi-glib/libqmi-glib.la diff --git a/configure.ac b/configure.ac index afef048..6406b3e 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ([2.68]) -AC_INIT([libqmi-glib], [0.0.1], [aleksander@gnu.org]) +AC_INIT([libqmi], [0.0.1], [libqmi-devel@lists.freedesktop.org]) AC_CONFIG_SRCDIR([config.h.in]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) @@ -24,9 +24,9 @@ LT_PREREQ([2.2]) LT_INIT dnl Specific warnings to always use -LIBQMI_GLIB_COMPILER_WARNINGS +LIBQMI_COMPILER_WARNINGS -dnl General dependencies +dnl General dependencies for libqmi-glib PKG_CHECK_MODULES(LIBQMI_GLIB, glib-2.0 >= 2.32 gobject-2.0 @@ -34,7 +34,7 @@ PKG_CHECK_MODULES(LIBQMI_GLIB, AC_SUBST(LIBQMI_GLIB_CFLAGS) AC_SUBST(LIBQMI_GLIB_LIBS) -dnl General cli dependencies +dnl General dependencies for qmicli PKG_CHECK_MODULES(QMICLI, glib-2.0 >= 2.28 gobject-2.0 @@ -52,13 +52,13 @@ AC_CONFIG_FILES([Makefile build-aux/Makefile build-aux/templates/Makefile build-aux/qmi-codegen/Makefile - src/Makefile + libqmi-glib/Makefile cli/Makefile utils/Makefile]) AC_OUTPUT echo " - libqmi-glib $VERSION + libqmi (libqmi-glib, qmicli) $VERSION ============================================== compiler: ${CC} diff --git a/libqmi-glib/Makefile.am b/libqmi-glib/Makefile.am new file mode 100644 index 0000000..71d85c3 --- /dev/null +++ b/libqmi-glib/Makefile.am @@ -0,0 +1,118 @@ + +lib_LTLIBRARIES = libqmi-glib.la + +libqmi_glib_la_CPPFLAGS = \ + $(LIBQMI_GLIB_CFLAGS) \ + -I$(top_srcdir) \ + -I$(top_builddir) \ + -I$(top_srcdir)/libqmi-glib \ + -I$(top_builddir)/libqmi-glib + +# Error types +qmi-error-types.h: qmi-errors.h $(top_srcdir)/build-aux/templates/qmi-error-types-template.h + $(AM_V_GEN) $(GLIB_MKENUMS) \ + --fhead "#ifndef __LIBQMI_GLIB_ERROR_TYPES_H__\n#define __LIBQMI_GLIB_ERROR_TYPES_H__\n#include \"qmi-errors.h\"\n" \ + --template $(top_srcdir)/build-aux/templates/qmi-error-types-template.h \ + --ftail "#endif /* __LIBQMI_GLIB_ERROR_TYPES_H__ */\n" \ + qmi-errors.h > $@ + +qmi-error-types.c: qmi-errors.h qmi-error-types.h $(top_srcdir)/build-aux/templates/qmi-error-types-template.c + $(AM_V_GEN) $(GLIB_MKENUMS) \ + --fhead "#include \"qmi-errors.h\"\n#include \"qmi-error-types.h\"\n" \ + --template $(top_srcdir)/build-aux/templates/qmi-error-types-template.c \ + qmi-errors.h > $@ + +# Enum types +ENUMS = qmi-enums.h qmi-enums-wds.h qmi-enums-dms.h qmi-dms.h qmi-wds.h qmi-ctl.h +qmi-enum-types.h: $(ENUMS) $(top_srcdir)/build-aux/templates/qmi-enum-types-template.h qmi-ctl.stamp qmi-dms.stamp qmi-wds.stamp + $(AM_V_GEN) $(GLIB_MKENUMS) \ + --fhead "#ifndef __LIBQMI_GLIB_ENUM_TYPES_H__\n#define __LIBQMI_GLIB_ENUM_TYPES_H__\n#include \"qmi-enums.h\"\n#include \"qmi-enums-wds.h\"\n#include \"qmi-enums-dms.h\"\n#include \"qmi-ctl.h\"\n#include \"qmi-dms.h\"\n#include \"qmi-wds.h\"\n" \ + --template $(top_srcdir)/build-aux/templates/qmi-enum-types-template.h \ + --ftail "#endif /* __LIBQMI_GLIB_ENUM_TYPES_H__ */\n" \ + $(ENUMS) > $@ + +qmi-enum-types.c: $(ENUMS) qmi-enum-types.h $(top_srcdir)/build-aux/templates/qmi-enum-types-template.c + $(AM_V_GEN) $(GLIB_MKENUMS) \ + --fhead "#include \"qmi-enum-types.h\"\n" \ + --template $(top_srcdir)/build-aux/templates/qmi-enum-types-template.c \ + $(ENUMS) > $@ + +# CTL service +qmi-ctl.stamp: $(top_srcdir)/data/qmi-service-ctl.json $(top_srcdir)/build-aux/qmi-codegen/*.py $(top_srcdir)/build-aux/qmi-codegen/qmi-codegen + $(AM_V_GEN) \ + rm -f qmi-ctl.h && \ + rm -f qmi-ctl.c && \ + $(top_srcdir)/build-aux/qmi-codegen/qmi-codegen \ + --input $(top_srcdir)/data/qmi-service-ctl.json \ + --include $(top_srcdir)/data/qmi-common.json \ + --output qmi-ctl && \ + touch $@ + +# DMS service +qmi-dms.stamp: $(top_srcdir)/data/qmi-service-dms.json $(top_srcdir)/build-aux/qmi-codegen/*.py $(top_srcdir)/build-aux/qmi-codegen/qmi-codegen + $(AM_V_GEN) \ + rm -f qmi-dms.h && \ + rm -f qmi-dms.c && \ + $(top_srcdir)/build-aux/qmi-codegen/qmi-codegen \ + --input $(top_srcdir)/data/qmi-service-dms.json \ + --include $(top_srcdir)/data/qmi-common.json \ + --output qmi-dms && \ + touch $@ + +# WDS service +qmi-wds.stamp: $(top_srcdir)/data/qmi-service-wds.json $(top_srcdir)/build-aux/qmi-codegen/*.py $(top_srcdir)/build-aux/qmi-codegen/qmi-codegen + $(AM_V_GEN) \ + rm -f qmi-wds.h && \ + rm -f qmi-wds.c && \ + $(top_srcdir)/build-aux/qmi-codegen/qmi-codegen \ + --input $(top_srcdir)/data/qmi-service-wds.json \ + --include $(top_srcdir)/data/qmi-common.json \ + --output qmi-wds && \ + touch $@ + +# Additional dependencies +qmi-device.c: qmi-error-types.h qmi-enum-types.h +qmi-client.c: qmi-error-types.h qmi-enum-types.h +qmi-message.c: qmi-error-types.h qmi-enum-types.h qmi-ctl.h qmi-dms.h qmi-wds.h +qmi-ctl.h: qmi-ctl.stamp +qmi-ctl.c: qmi-error-types.h qmi-enum-types.h qmi-ctl.stamp +qmi-dms.h: qmi-dms.stamp +qmi-dms.c: qmi-error-types.h qmi-enum-types.h qmi-dms.stamp +qmi-wds.h: qmi-wds.stamp +qmi-wds.c: qmi-error-types.h qmi-enum-types.h qmi-wds.stamp + +libqmi_glib_la_SOURCES = \ + libqmi-glib.h \ + qmi-errors.h qmi-error-types.h qmi-error-types.c \ + qmi-enums-wds.h \ + qmi-enums-dms.h \ + qmi-enums.h qmi-enum-types.h qmi-enum-types.c \ + qmi-utils.h qmi-utils.c \ + qmi-message.h qmi-message.c \ + qmi-device.h qmi-device.c \ + qmi-client.h qmi-client.c + +nodist_libqmi_glib_la_SOURCES = \ + qmi-ctl.c qmi-ctl.h \ + qmi-dms.c qmi-dms.h \ + qmi-wds.c qmi-wds.h + +libqmi_glib_la_LIBADD = \ + $(LIBQMI_GLIB_LIBS) + +includedir = @includedir@/libqmi-glib +include_HEADERS = \ + libqmi-glib.h \ + qmi-errors.h qmi-error-types.h \ + qmi-enums.h qmi-enum-types.h \ + qmi-message.h \ + qmi-device.h \ + qmi-client.h \ + qmi-ctl.h \ + qmi-enums-dms.h qmi-dms.h \ + qmi-enums-wds.h qmi-wds.h + +CLEANFILES = \ + qmi-ctl.h qmi-ctl.c qmi-ctl.stamp \ + qmi-dms.h qmi-dms.c qmi-dms.stamp \ + qmi-wds.h qmi-wds.c qmi-wds.stamp diff --git a/libqmi-glib/libqmi-glib.h b/libqmi-glib/libqmi-glib.h new file mode 100644 index 0000000..c6d6a65 --- /dev/null +++ b/libqmi-glib/libqmi-glib.h @@ -0,0 +1,35 @@ +/* -*- 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 Aleksander Morgado + */ + +#ifndef _LIBQMI_GLIB_H_ +#define _LIBQMI_GLIB_H_ + +#include "qmi-errors.h" +#include "qmi-error-types.h" +#include "qmi-enum-types.h" + +#include "qmi-device.h" +#include "qmi-client.h" +#include "qmi-dms.h" +#include "qmi-wds.h" + +#endif /* _LIBQMI_GLIB_H_ */ diff --git a/libqmi-glib/qmi-client.c b/libqmi-glib/qmi-client.c new file mode 100644 index 0000000..f3e6bab --- /dev/null +++ b/libqmi-glib/qmi-client.c @@ -0,0 +1,265 @@ +/* -*- 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 Aleksander Morgado + */ + +#include + +#include "qmi-error-types.h" +#include "qmi-enum-types.h" +#include "qmi-device.h" +#include "qmi-client.h" +#include "qmi-ctl.h" + +G_DEFINE_ABSTRACT_TYPE (QmiClient, qmi_client, G_TYPE_OBJECT); + +enum { + PROP_0, + PROP_DEVICE, + PROP_SERVICE, + PROP_CID, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + +struct _QmiClientPrivate { + QmiDevice *device; + QmiService service; + guint8 cid; + + guint16 transaction_id; +}; + +/*****************************************************************************/ + +/** + * qmi_client_get_device: + * @self: a #QmiClient + * + * Get the #QmiDevice associated with this #QmiClient. + * + * Returns: a #GObject that must be freed with g_object_unref(). + */ +GObject * +qmi_client_get_device (QmiClient *self) +{ + GObject *device; + + g_return_val_if_fail (QMI_IS_CLIENT (self), NULL); + + g_object_get (G_OBJECT (self), + QMI_CLIENT_DEVICE, &device, + NULL); + + return device; +} + +/** + * qmi_client_peek_device: + * @self: a #QmiClient. + * + * Get the #QmiDevice associated with this #QmiClient, without increasing the reference count + * on the returned object. + * + * Returns: a #GObject. Do not free the returned object, it is owned by @self. + */ +GObject * +qmi_client_peek_device (QmiClient *self) +{ + g_return_val_if_fail (QMI_IS_CLIENT (self), NULL); + + return G_OBJECT (self->priv->device); +} + +/** + * qmi_client_get_service: + * @self: A #QmiClient + * + * Get the service being used by this #QmiClient. + * + * Returns: a #QmiService. + */ +QmiService +qmi_client_get_service (QmiClient *self) +{ + g_return_val_if_fail (QMI_IS_CLIENT (self), QMI_SERVICE_UNKNOWN); + + return self->priv->service; +} + +/** + * qmi_client_get_cid: + * @self: A #QmiClient + * + * Get the client ID of this #QmiClient. + * + * Returns: the client ID. + */ +guint8 +qmi_client_get_cid (QmiClient *self) +{ + g_return_val_if_fail (QMI_IS_CLIENT (self), QMI_CID_NONE); + + return self->priv->cid; +} + +/** + * qmi_client_get_next_transaction_id: + * @self: A #QmiClient + * + * Acquire the next transaction ID of this #QmiClient. + * The internal transaction ID gets incremented. + * + * Returns: the next transaction ID. + */ +guint16 +qmi_client_get_next_transaction_id (QmiClient *self) +{ + guint16 next; + + g_return_val_if_fail (QMI_IS_CLIENT (self), 0); + + next = self->priv->transaction_id; + + /* Don't go further than 8bits in the CTL service */ + if ((self->priv->service == QMI_SERVICE_CTL && + self->priv->transaction_id == G_MAXUINT8) || + self->priv->transaction_id == G_MAXUINT16) + /* Reset! */ + self->priv->transaction_id = 0x01; + else + self->priv->transaction_id++; + + return next; +} + +/*****************************************************************************/ + +void +qmi_client_process_indication (QmiClient *self, + QmiMessage *message) +{ + if (QMI_CLIENT_GET_CLASS (self)->process_indication) + QMI_CLIENT_GET_CLASS (self)->process_indication (self, message); +} + +/*****************************************************************************/ + +static void +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + QmiClient *self = QMI_CLIENT (object); + + switch (prop_id) { + case PROP_DEVICE: + /* NOTE!! We do NOT keep a reference to the device here. + * Clients are OWNED by the device */ + self->priv->device = g_value_get_object (value); + break; + case PROP_SERVICE: + self->priv->service = g_value_get_enum (value); + break; + case PROP_CID: + self->priv->cid = (guint8)g_value_get_uint (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) +{ + QmiClient *self = QMI_CLIENT (object); + + switch (prop_id) { + case PROP_DEVICE: + g_value_set_object (value, self->priv->device); + break; + case PROP_SERVICE: + g_value_set_enum (value, self->priv->service); + break; + case PROP_CID: + g_value_set_uint (value, (guint)self->priv->cid); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +qmi_client_init (QmiClient *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), + QMI_TYPE_CLIENT, + QmiClientPrivate); + + /* Defaults */ + self->priv->service = QMI_SERVICE_UNKNOWN; + self->priv->transaction_id = 0x01; + self->priv->cid = QMI_CID_NONE; +} + +static void +qmi_client_class_init (QmiClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (QmiClientPrivate)); + + object_class->get_property = get_property; + object_class->set_property = set_property; + + properties[PROP_DEVICE] = + g_param_spec_object (QMI_CLIENT_DEVICE, + "Device", + "The QMI device", + QMI_TYPE_DEVICE, + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_DEVICE, properties[PROP_DEVICE]); + + properties[PROP_SERVICE] = + g_param_spec_enum (QMI_CLIENT_SERVICE, + "Service", + "QMI service this client is using", + QMI_TYPE_SERVICE, + QMI_SERVICE_UNKNOWN, + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_SERVICE, properties[PROP_SERVICE]); + + properties[PROP_CID] = + g_param_spec_uint (QMI_CLIENT_CID, + "Client ID", + "ID of the client registered into the QMI device", + 0, + G_MAXUINT8, + QMI_CID_NONE, + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_CID, properties[PROP_CID]); +} diff --git a/libqmi-glib/qmi-client.h b/libqmi-glib/qmi-client.h new file mode 100644 index 0000000..828211e --- /dev/null +++ b/libqmi-glib/qmi-client.h @@ -0,0 +1,79 @@ +/* -*- 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 Aleksander Morgado + */ + +#ifndef _LIBQMI_GLIB_QMI_CLIENT_H_ +#define _LIBQMI_GLIB_QMI_CLIENT_H_ + +#include + +#include "qmi-enums.h" +#include "qmi-message.h" + +G_BEGIN_DECLS + +#define QMI_CID_NONE 0x00 +#define QMI_CID_BROADCAST 0xFF + +#define QMI_TYPE_CLIENT (qmi_client_get_type ()) +#define QMI_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), QMI_TYPE_CLIENT, QmiClient)) +#define QMI_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), QMI_TYPE_CLIENT, QmiClientClass)) +#define QMI_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), QMI_TYPE_CLIENT)) +#define QMI_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), QMI_TYPE_CLIENT)) +#define QMI_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), QMI_TYPE_CLIENT, QmiClientClass)) + +typedef struct _QmiClient QmiClient; +typedef struct _QmiClientClass QmiClientClass; +typedef struct _QmiClientPrivate QmiClientPrivate; + +#define QMI_CLIENT_DEVICE "client-device" +#define QMI_CLIENT_SERVICE "client-service" +#define QMI_CLIENT_CID "client-cid" + +struct _QmiClient { + GObject parent; + QmiClientPrivate *priv; +}; + +struct _QmiClientClass { + GObjectClass parent; + + /* Virtual method to get indications processed */ + void (* process_indication) (QmiClient *self, + QmiMessage *message); +}; + +GType qmi_client_get_type (void); + +GObject *qmi_client_get_device (QmiClient *self); +GObject *qmi_client_peek_device (QmiClient *self); +QmiService qmi_client_get_service (QmiClient *self); +guint8 qmi_client_get_cid (QmiClient *self); + +guint16 qmi_client_get_next_transaction_id (QmiClient *self); + +/* not part of the public API */ +void qmi_client_process_indication (QmiClient *self, + QmiMessage *message); + +G_END_DECLS + +#endif /* _LIBQMI_GLIB_QMI_CLIENT_H_ */ diff --git a/libqmi-glib/qmi-device.c b/libqmi-glib/qmi-device.c new file mode 100644 index 0000000..170795d --- /dev/null +++ b/libqmi-glib/qmi-device.c @@ -0,0 +1,1812 @@ +/* -*- 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 Aleksander Morgado + */ + +#include +#include +#include +#include +#include +#include + +#include "qmi-device.h" +#include "qmi-message.h" +#include "qmi-ctl.h" +#include "qmi-dms.h" +#include "qmi-wds.h" +#include "qmi-utils.h" +#include "qmi-error-types.h" +#include "qmi-enum-types.h" + +static void async_initable_iface_init (GAsyncInitableIface *iface); + +G_DEFINE_TYPE_EXTENDED (QmiDevice, qmi_device, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)) + +enum { + PROP_0, + PROP_FILE, + PROP_CLIENT_CTL, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + +struct _QmiDevicePrivate { + /* File */ + GFile *file; + gchar *path; + gchar *path_display; + + /* Implicit CTL client */ + QmiClientCtl *client_ctl; + + /* Supported services */ + GArray *supported_services; + + /* I/O channel, set when the file is open */ + GIOChannel *iochannel; + guint watch_id; + GByteArray *response; + + /* HT to keep track of ongoing transactions */ + GHashTable *transactions; + + /* HT of clients that want to get indications */ + GHashTable *registered_clients; +}; + +#define BUFFER_SIZE 2048 + +/*****************************************************************************/ +/* Message transactions (private) */ + +typedef struct { + QmiMessage *message; + GSimpleAsyncResult *result; + guint timeout_id; +} Transaction; + +static Transaction * +transaction_new (QmiDevice *self, + QmiMessage *message, + GAsyncReadyCallback callback, + gpointer user_data) +{ + Transaction *tr; + + tr = g_slice_new0 (Transaction); + tr->message = qmi_message_ref (message); + tr->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + transaction_new); + + return tr; +} + +static void +transaction_complete_and_free (Transaction *tr, + QmiMessage *reply, + const GError *error) +{ + g_assert (reply != NULL || error != NULL); + + if (tr->timeout_id) + g_source_remove (tr->timeout_id); + + if (reply) + g_simple_async_result_set_op_res_gpointer (tr->result, + qmi_message_ref (reply), + (GDestroyNotify)qmi_message_unref); + else + g_simple_async_result_set_from_error (tr->result, error); + + g_simple_async_result_complete_in_idle (tr->result); + g_object_unref (tr->result); + qmi_message_unref (tr->message); + g_slice_free (Transaction, tr); +} + +static inline gpointer +build_transaction_key (QmiMessage *message) +{ + gpointer key; + guint8 service; + guint8 client_id; + guint16 transaction_id; + + service = (guint8)qmi_message_get_service (message); + client_id = qmi_message_get_client_id (message); + transaction_id = qmi_message_get_transaction_id (message); + + /* We're putting a 32 bit value into a gpointer */ + key = GUINT_TO_POINTER ((((service << 8) | client_id) << 16) | transaction_id); + +#ifdef MESSAGE_ENABLE_TRACE + { + gchar *hex; + + hex = qmi_utils_str_hex (&key, sizeof (key), ':'); + g_debug ("KEY: %s", hex); + g_free (hex); + + hex = qmi_utils_str_hex (&service, sizeof (service), ':'); + g_debug (" Service: %s", hex); + g_free (hex); + + hex = qmi_utils_str_hex (&client_id, sizeof (client_id), ':'); + g_debug (" Client ID: %s", hex); + g_free (hex); + + hex = qmi_utils_str_hex (&transaction_id, sizeof (transaction_id), ':'); + g_debug (" Transaction ID: %s", hex); + g_free (hex); + } +#endif /* MESSAGE_ENABLE_TRACE */ + + return key; +} + +static Transaction * +device_release_transaction (QmiDevice *self, + gpointer key) +{ + Transaction *tr = NULL; + + if (self->priv->transactions) { + tr = g_hash_table_lookup (self->priv->transactions, key); + if (tr) + /* If found, remove it from the HT */ + g_hash_table_remove (self->priv->transactions, key); + } + + return tr; +} + +typedef struct { + QmiDevice *self; + gpointer key; +} TransactionTimeoutContext; + +static void +transaction_timeout_context_free (TransactionTimeoutContext *ctx) +{ + g_slice_free (TransactionTimeoutContext, ctx); +} + +static gboolean +transaction_timed_out (TransactionTimeoutContext *ctx) +{ + Transaction *tr; + GError *error = NULL; + + tr = device_release_transaction (ctx->self, ctx->key); + tr->timeout_id = 0; + + /* Complete transaction with a timeout error */ + error = g_error_new (QMI_CORE_ERROR, + QMI_CORE_ERROR_TIMEOUT, + "Transaction timed out"); + transaction_complete_and_free (tr, NULL, error); + g_error_free (error); + + return FALSE; +} + +static void +device_store_transaction (QmiDevice *self, + Transaction *tr, + guint timeout) +{ + TransactionTimeoutContext *timeout_ctx; + gpointer key; + + if (G_UNLIKELY (!self->priv->transactions)) + self->priv->transactions = g_hash_table_new (g_direct_hash, + g_direct_equal); + + key = build_transaction_key (tr->message); + g_hash_table_insert (self->priv->transactions, key, tr); + + /* Once it gets into the HT, setup the timeout */ + timeout_ctx = g_slice_new (TransactionTimeoutContext); + timeout_ctx->self = self; + timeout_ctx->key = key; /* valid as long as the transaction is in the HT */ + tr->timeout_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, + timeout, + (GSourceFunc)transaction_timed_out, + timeout_ctx, + (GDestroyNotify)transaction_timeout_context_free); +} + +static Transaction * +device_match_transaction (QmiDevice *self, + QmiMessage *message) +{ + /* msg can be either the original message or the response */ + return device_release_transaction (self, build_transaction_key (message)); +} + +/*****************************************************************************/ + +/** + * qmi_device_get_file: + * @self: a #QmiDevice. + * + * Get the #GFile associated with this #QmiDevice. + * + * Returns: a #GFile that must be freed with g_object_unref(). + */ +GFile * +qmi_device_get_file (QmiDevice *self) +{ + GFile *file = NULL; + + g_return_val_if_fail (QMI_IS_DEVICE (self), NULL); + + g_object_get (G_OBJECT (self), + QMI_DEVICE_FILE, &file, + NULL); + return file; +} + +/** + * qmi_device_peek_file: + * @self: a #QmiDevice. + * + * Get the #GFile associated with this #QmiDevice, without increasing the reference count + * on the returned object. + * + * Returns: a #GFile. Do not free the returned object, it is owned by @self. + */ +GFile * +qmi_device_peek_file (QmiDevice *self) +{ + g_return_val_if_fail (QMI_IS_DEVICE (self), NULL); + + return self->priv->file; +} + +/** + * qmi_device_get_path: + * @self: a #QmiDevice. + * + * Get the system path of the underlying QMI device. + * + * Returns: the system path of the device. + */ +const gchar * +qmi_device_get_path (QmiDevice *self) +{ + g_return_val_if_fail (QMI_IS_DEVICE (self), NULL); + + return self->priv->path; +} + +/** + * qmi_device_get_path_display: + * @self: a #QmiDevice. + * + * Get the system path of the underlying QMI device in UTF-8. + * + * Returns: UTF-8 encoded system path of the device. + */ +const gchar * +qmi_device_get_path_display (QmiDevice *self) +{ + g_return_val_if_fail (QMI_IS_DEVICE (self), NULL); + + return self->priv->path_display; +} + +/** + * qmi_device_is_open: + * @self: a #QmiDevice. + * + * Checks whether the #QmiDevice is open for I/O. + * + * Returns: #TRUE if @self is open, #FALSE otherwise. + */ +gboolean +qmi_device_is_open (QmiDevice *self) +{ + g_return_val_if_fail (QMI_IS_DEVICE (self), FALSE); + + return !!self->priv->iochannel; +} + +/*****************************************************************************/ +/* Register/Unregister clients that want to receive indications */ + +static gpointer +build_registered_client_key (guint8 cid, + QmiService service) +{ + return GUINT_TO_POINTER (((guint8)service << 8) | cid); +} + +static gboolean +register_client (QmiDevice *self, + QmiClient *client, + GError **error) +{ + gpointer key; + + key = build_registered_client_key (qmi_client_get_cid (client), + qmi_client_get_service (client)); + /* Only add the new client if not already registered one with the same CID + * for the same service */ + if (g_hash_table_lookup (self->priv->registered_clients, key)) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "A client with CID '%u' and service '%s' is already registered", + qmi_client_get_cid (client), + qmi_service_get_string (qmi_client_get_service (client))); + return FALSE; + } + + g_hash_table_insert (self->priv->registered_clients, + key, + g_object_ref (client)); + return TRUE; +} + +static void +unregister_client (QmiDevice *self, + QmiClient *client) +{ + g_hash_table_remove (self->priv->registered_clients, + build_registered_client_key (qmi_client_get_cid (client), + qmi_client_get_service (client))); +} + +/*****************************************************************************/ +/* Allocate new client */ + +typedef struct { + QmiDevice *self; + GSimpleAsyncResult *result; + QmiService service; + GType client_type; + guint8 cid; +} AllocateClientContext; + +static void +allocate_client_context_complete_and_free (AllocateClientContext *ctx) +{ + g_simple_async_result_complete_in_idle (ctx->result); + g_object_unref (ctx->result); + g_object_unref (ctx->self); + g_slice_free (AllocateClientContext, ctx); +} + +/** + * qmi_device_allocate_client_finish: + * @self: a #QmiDevice. + * @res: a #GAsyncResult. + * @error: a #GError. + * + * Finishes an operation started with qmi_device_allocate_client(). + * + * Returns: a newly allocated #QmiClient, or #NULL if @error is set. + */ +QmiClient * +qmi_device_allocate_client_finish (QmiDevice *self, + GAsyncResult *res, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) + return NULL; + + return QMI_CLIENT (g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)))); +} + +static void +build_client_object (AllocateClientContext *ctx) +{ + QmiClient *client; + GError *error = NULL; + + /* We now have a proper CID for the client, we should be able to create it + * right away */ + client = g_object_new (ctx->client_type, + QMI_CLIENT_DEVICE, ctx->self, + QMI_CLIENT_SERVICE, ctx->service, + QMI_CLIENT_CID, ctx->cid, + NULL); + + /* Register the client to get indications */ + if (!register_client (ctx->self, client, &error)) { + g_prefix_error (&error, + "Cannot register new client with CID '%u' and service '%s'", + ctx->cid, + qmi_service_get_string (ctx->service)); + g_simple_async_result_take_error (ctx->result, error); + allocate_client_context_complete_and_free (ctx); + g_object_unref (client); + return; + } + + g_debug ("Registered '%s' client with ID '%u'", + qmi_service_get_string (ctx->service), + ctx->cid); + + /* Client created and registered, complete successfully */ + g_simple_async_result_set_op_res_gpointer (ctx->result, + client, + (GDestroyNotify)g_object_unref); + allocate_client_context_complete_and_free (ctx); +} + +static void +allocate_cid_ready (QmiClientCtl *client_ctl, + GAsyncResult *res, + AllocateClientContext *ctx) +{ + QmiMessageCtlAllocateCidOutput *output; + QmiService service; + guint8 cid; + GError *error = NULL; + + /* Check result of the async operation */ + output = qmi_client_ctl_allocate_cid_finish (client_ctl, res, &error); + if (!output) { + g_prefix_error (&error, "CID allocation failed in the CTL client: "); + g_simple_async_result_take_error (ctx->result, error); + allocate_client_context_complete_and_free (ctx); + return; + } + + /* Check result of the QMI operation */ + if (!qmi_message_ctl_allocate_cid_output_get_result (output, &error)) { + g_simple_async_result_take_error (ctx->result, error); + allocate_client_context_complete_and_free (ctx); + qmi_message_ctl_allocate_cid_output_unref (output); + return; + } + + /* Allocation info is mandatory when result is success */ + g_assert (qmi_message_ctl_allocate_cid_output_get_allocation_info (output, &service, &cid, NULL)); + + if (service != ctx->service) { + g_simple_async_result_set_error ( + ctx->result, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "CID allocation failed in the CTL client: " + "Service mismatch (requested '%s', got '%s')", + qmi_service_get_string (ctx->service), + qmi_service_get_string (service)); + allocate_client_context_complete_and_free (ctx); + qmi_message_ctl_allocate_cid_output_unref (output); + return; + } + + ctx->cid = cid; + build_client_object (ctx); + qmi_message_ctl_allocate_cid_output_unref (output); +} + +static gboolean +check_service_supported (QmiDevice *self, + QmiService service) +{ + guint i; + + /* If we didn't check supported services, just assume it is supported */ + if (!self->priv->supported_services) { + g_debug ("Assuming service '%s' is supported...", + qmi_service_get_string (service)); + return TRUE; + } + + for (i = 0; i < self->priv->supported_services->len; i++) { + QmiMessageCtlGetVersionInfoOutputServiceListService *info; + + info = &g_array_index (self->priv->supported_services, + QmiMessageCtlGetVersionInfoOutputServiceListService, + i); + + if (service == info->service) + return TRUE; + } + + return FALSE; +} + +/** + * qmi_device_allocate_client: + * @self: a #QmiDevice. + * @service: a valid #QmiService. + * @cid: a valid client ID, or #QMI_CID_NONE. + * @timeout: maximum time to wait. + * @cancellable: optional #GCancellable object, #NULL to ignore. + * @callback: a #GAsyncReadyCallback to call when the operation is finished. + * @user_data: the data to pass to callback function. + * + * Asynchronously allocates a new #QmiClient in @self. + * + * If #QMI_CID_NONE is given in @cid, a new client ID will be allocated; + * otherwise a client with the given @cid will be generated. + * + * When the operation is finished @callback will be called. You can then call + * qmi_device_allocate_client_finish() to get the result of the operation. + * + * Note: Clients for the #QMI_SERVICE_CTL cannot be created with this method; + * instead get/peek the implicit one from @self. + */ +void +qmi_device_allocate_client (QmiDevice *self, + QmiService service, + guint8 cid, + guint timeout, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + AllocateClientContext *ctx; + + g_return_if_fail (QMI_IS_DEVICE (self)); + g_return_if_fail (service != QMI_SERVICE_UNKNOWN); + + ctx = g_slice_new0 (AllocateClientContext); + ctx->self = g_object_ref (self); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + qmi_device_allocate_client); + ctx->service = service; + + /* Check if the requested service is supported by the device */ + if (!check_service_supported (self, service)) { + g_simple_async_result_set_error (ctx->result, + QMI_CORE_ERROR, + QMI_CORE_ERROR_UNSUPPORTED, + "Service '%s' not supported by the device", + qmi_service_get_string (service)); + allocate_client_context_complete_and_free (ctx); + return; + } + + switch (service) { + case QMI_SERVICE_CTL: + g_simple_async_result_set_error (ctx->result, + QMI_CORE_ERROR, + QMI_CORE_ERROR_INVALID_ARGS, + "Cannot create additional clients for the CTL service"); + allocate_client_context_complete_and_free (ctx); + return; + + case QMI_SERVICE_DMS: + ctx->client_type = QMI_TYPE_CLIENT_DMS; + break; + + case QMI_SERVICE_WDS: + ctx->client_type = QMI_TYPE_CLIENT_WDS; + break; + + default: + g_simple_async_result_set_error (ctx->result, + QMI_CORE_ERROR, + QMI_CORE_ERROR_INVALID_ARGS, + "Clients for service '%s' not yet supported", + qmi_service_get_string (service)); + allocate_client_context_complete_and_free (ctx); + return; + } + + /* Allocate a new CID for the client to be created */ + if (cid == QMI_CID_NONE) { + QmiMessageCtlAllocateCidInput *input; + + input = qmi_message_ctl_allocate_cid_input_new (); + qmi_message_ctl_allocate_cid_input_set_service (input, ctx->service, NULL); + + g_debug ("Allocating new client ID..."); + qmi_client_ctl_allocate_cid (self->priv->client_ctl, + input, + timeout, + cancellable, + (GAsyncReadyCallback)allocate_cid_ready, + ctx); + + qmi_message_ctl_allocate_cid_input_unref (input); + return; + } + + /* Reuse the given CID */ + g_debug ("Reusing client CID '%u'...", cid); + ctx->cid = cid; + build_client_object (ctx); +} + +/*****************************************************************************/ +/* Release client */ + +typedef struct { + QmiClient *client; + GSimpleAsyncResult *result; +} ReleaseClientContext; + +static void +release_client_context_complete_and_free (ReleaseClientContext *ctx) +{ + g_simple_async_result_complete_in_idle (ctx->result); + g_object_unref (ctx->result); + g_object_unref (ctx->client); + g_slice_free (ReleaseClientContext, ctx); +} + +/** + * qmi_device_release_client_finish: + * @self: a #QmiDevice. + * @res: a #GAsyncResult. + * @error: a #GError. + * + * Finishes an operation started with qmi_device_release_client(). + * + * Note that even if the release operation returns an error, the client should + * anyway be considered released, and shouldn't be used afterwards. + * + * Returns: #TRUE if successful, or #NULL if @error is set. + */ +gboolean +qmi_device_release_client_finish (QmiDevice *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void +client_ctl_release_cid_ready (QmiClientCtl *client_ctl, + GAsyncResult *res, + ReleaseClientContext *ctx) +{ + GError *error = NULL; + QmiMessageCtlReleaseCidOutput *output; + + /* Note: even if we return an error, the client is to be considered + * released! (so shouldn't be used) */ + + /* Check result of the async operation */ + output = qmi_client_ctl_release_cid_finish (client_ctl, res, &error); + if (!output) { + g_simple_async_result_take_error (ctx->result, error); + release_client_context_complete_and_free (ctx); + return; + } + + /* Check result of the QMI operation */ + if (!qmi_message_ctl_release_cid_output_get_result (output, &error)) { + g_simple_async_result_take_error (ctx->result, error); + release_client_context_complete_and_free (ctx); + qmi_message_ctl_release_cid_output_unref (output); + return; + } + + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + release_client_context_complete_and_free (ctx); + qmi_message_ctl_release_cid_output_unref (output); +} + +/** + * qmi_device_release_client: + * @self: a #QmiDevice. + * @client: the #QmiClient to release. + * @flags: mask of #QmiDeviceReleaseClientFlags specifying how the client should be released. + * @timeout: maximum time to wait. + * @cancellable: optional #GCancellable object, #NULL to ignore. + * @callback: a #GAsyncReadyCallback to call when the operation is finished. + * @user_data: the data to pass to callback function. + * + * Asynchronously releases the #QmiClient from the #QmiDevice. + * + * Once the #QmiClient has been released, it cannot be used any more to + * perform operations. + * + * + * When the operation is finished @callback will be called. You can then call + * qmi_device_release_client_finish() to get the result of the operation. + */ +void +qmi_device_release_client (QmiDevice *self, + QmiClient *client, + QmiDeviceReleaseClientFlags flags, + guint timeout, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + ReleaseClientContext *ctx; + QmiService service; + guint8 cid; + + g_return_if_fail (QMI_IS_DEVICE (self)); + g_return_if_fail (QMI_IS_CLIENT (client)); + + /* The CTL client should not have been created out of the QmiDevice */ + g_assert (qmi_client_get_service (client) != QMI_SERVICE_CTL); + + /* NOTE! The operation must not take a reference to self, or we won't be + * able to use it implicitly from our dispose() */ + + ctx = g_slice_new0 (ReleaseClientContext); + ctx->client = g_object_ref (client); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + qmi_device_release_client); + + cid = qmi_client_get_cid (client); + service = (guint8)qmi_client_get_service (client); + + /* Do not try to release an already released client */ + if (cid == QMI_CID_NONE) { + g_simple_async_result_set_error (ctx->result, + QMI_CORE_ERROR, + QMI_CORE_ERROR_INVALID_ARGS, + "Client is already released"); + release_client_context_complete_and_free (ctx); + return; + } + + /* Unregister from device */ + unregister_client (self, client); + + g_debug ("Unregistered '%s' client with ID '%u'", + qmi_service_get_string (service), + cid); + + /* Reset the contents of the client object, making it unusable */ + g_object_set (client, + QMI_CLIENT_CID, QMI_CID_NONE, + QMI_CLIENT_SERVICE, QMI_SERVICE_UNKNOWN, + QMI_CLIENT_DEVICE, NULL, + NULL); + + if (flags & QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID) { + QmiMessageCtlReleaseCidInput *input; + + /* And now, really try to release the CID */ + input = qmi_message_ctl_release_cid_input_new (); + qmi_message_ctl_release_cid_input_set_release_info (input, service,cid, NULL); + + /* And now, really try to release the CID */ + qmi_client_ctl_release_cid (self->priv->client_ctl, + input, + timeout, + cancellable, + (GAsyncReadyCallback)client_ctl_release_cid_ready, + ctx); + + qmi_message_ctl_release_cid_input_unref (input); + return; + } + + /* No need to release the CID, so just done */ + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + release_client_context_complete_and_free (ctx); + return; +} + +/*****************************************************************************/ +/* Open device */ + +typedef struct { + QmiClient *client; + QmiMessage *message; +} IdleIndicationContext; + +static gboolean +process_indication_idle (IdleIndicationContext *ctx) +{ + g_assert (ctx->client != NULL); + g_assert (ctx->message != NULL); + + qmi_client_process_indication (ctx->client, ctx->message); + + g_object_unref (ctx->client); + qmi_message_unref (ctx->message); + g_slice_free (IdleIndicationContext, ctx); + return FALSE; +} + +static void +report_indication (QmiClient *client, + QmiMessage *message) +{ + IdleIndicationContext *ctx; + + /* Setup an idle to Pass the indication down to the client */ + ctx = g_slice_new (IdleIndicationContext); + ctx->client = g_object_ref (client); + ctx->message = qmi_message_ref (message); + g_idle_add ((GSourceFunc)process_indication_idle, ctx); +} + +static void +process_message (QmiDevice *self, + QmiMessage *message) +{ + GError *error = NULL; + + /* Ensure the read message is valid */ + if (!qmi_message_check (message, &error)) { + g_warning ("Invalid QMI message received: %s", + error->message); + g_error_free (error); + return; + } + +#ifdef MESSAGE_ENABLE_TRACE + { + gchar *printable; + + printable = qmi_message_get_printable (message, ">>>>>> "); + g_debug ("[%s] Received message...\n%s", + self->priv->path_display, + printable); + g_free (printable); + } +#endif /* MESSAGE_ENABLE_TRACE */ + + if (qmi_message_is_indication (message)) { + if (qmi_message_get_client_id (message) == QMI_CID_BROADCAST) { + GHashTableIter iter; + gpointer key; + QmiClient *client; + + g_hash_table_iter_init (&iter, self->priv->registered_clients); + while (g_hash_table_iter_next (&iter, &key, (gpointer *)&client)) { + /* For broadcast messages, report them just if the service matches */ + if (qmi_message_get_service (message) == qmi_client_get_service (client)) + report_indication (client, message); + } + } else { + QmiClient *client; + + client = g_hash_table_lookup (self->priv->registered_clients, + build_registered_client_key (qmi_message_get_client_id (message), + qmi_message_get_service (message))); + if (client) + report_indication (client, message); + } + + return; + } + + if (qmi_message_is_response (message)) { + Transaction *tr; + + tr = device_match_transaction (self, message); + if (!tr) + g_debug ("[%s] No transaction matched in received message", + self->priv->path_display); + else + /* Report the reply message */ + transaction_complete_and_free (tr, message, NULL); + + return; + } + + g_debug ("[%s] Message received but it is neither an indication nor a response. Skipping it.", + self->priv->path_display); +} + +static void +parse_response (QmiDevice *self) +{ + do { + 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->response->len > 0 && + self->priv->response->data[0] != QMI_MESSAGE_QMUX_MARKER) { + /* TODO: Report fatal error */ + g_warning ("QMI framing error detected"); + return; + } + + message = qmi_message_new_from_raw (self->priv->response->data, + self->priv->response->len); + if (!message) + /* More data we need */ + return; + + /* Remove the read data from the response buffer */ + g_byte_array_remove_range (self->priv->response, + 0, + qmi_message_get_length (message)); + + /* Play with the received message */ + process_message (self, message); + + qmi_message_unref (message); + } while (self->priv->response->len > 0); +} + +static gboolean +data_available (GIOChannel *source, + GIOCondition condition, + QmiDevice *self) +{ + gsize bytes_read; + GIOStatus status; + gchar buffer[BUFFER_SIZE + 1]; + + if (condition & G_IO_HUP) { + g_debug ("[%s] unexpected port hangup!", + self->priv->path_display); + + if (self->priv->response && + self->priv->response->len) + g_byte_array_remove_range (self->priv->response, 0, self->priv->response->len); + + qmi_device_close (self, NULL); + return FALSE; + } + + if (condition & G_IO_ERR) { + if (self->priv->response && + self->priv->response->len) + g_byte_array_remove_range (self->priv->response, 0, self->priv->response->len); + return TRUE; + } + + /* If not ready yet, prepare the response with default initial size. */ + if (G_UNLIKELY (!self->priv->response)) + self->priv->response = g_byte_array_sized_new (500); + + do { + GError *error = NULL; + + status = g_io_channel_read_chars (source, + buffer, + BUFFER_SIZE, + &bytes_read, + &error); + if (status == G_IO_STATUS_ERROR) { + if (error) { + g_warning ("error reading from the IOChannel: '%s'", error->message); + g_error_free (error); + } + + /* Port is closed; we're done */ + if (self->priv->watch_id == 0) + break; + } + + /* If no bytes read, just let g_io_channel wait for more data */ + if (bytes_read == 0) + break; + + if (bytes_read > 0) + g_byte_array_append (self->priv->response, (const guint8 *)buffer, bytes_read); + + /* Try to parse what we already got */ + parse_response (self); + + /* And keep on if we were told to keep on */ + } while (bytes_read == BUFFER_SIZE || status == G_IO_STATUS_AGAIN); + + return TRUE; +} + +static gboolean +create_iochannel (QmiDevice *self, + GError **error) +{ + GError *inner_error = NULL; + guint fd; + + if (self->priv->iochannel) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_WRONG_STATE, + "Already open"); + return FALSE; + } + + g_assert (self->priv->file); + g_assert (self->priv->path); + + errno = 0; + fd = open (self->priv->path, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); + if (fd < 0) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "Cannot open device file '%s': %s", + self->priv->path_display, + strerror (errno)); + return FALSE; + } + + /* Create new GIOChannel */ + self->priv->iochannel = g_io_channel_unix_new (fd); + + /* We don't want UTF-8 encoding, we're playing with raw binary data */ + g_io_channel_set_encoding (self->priv->iochannel, NULL, NULL); + + /* We don't want to get the channel buffered */ + g_io_channel_set_buffered (self->priv->iochannel, FALSE); + + /* Let the GIOChannel own the FD */ + g_io_channel_set_close_on_unref (self->priv->iochannel, TRUE); + + /* We don't want to get blocked while writing stuff */ + if (!g_io_channel_set_flags (self->priv->iochannel, + G_IO_FLAG_NONBLOCK, + &inner_error)) { + g_prefix_error (&inner_error, "Cannot set non-blocking channel: "); + g_propagate_error (error, inner_error); + g_io_channel_shutdown (self->priv->iochannel, FALSE, NULL); + g_io_channel_unref (self->priv->iochannel); + self->priv->iochannel = NULL; + return FALSE; + } + + self->priv->watch_id = g_io_add_watch (self->priv->iochannel, + G_IO_IN | G_IO_ERR | G_IO_HUP, + (GIOFunc)data_available, + self); + + return !!self->priv->iochannel; +} + +typedef struct { + QmiDevice *self; + GSimpleAsyncResult *result; + GCancellable *cancellable; + QmiDeviceOpenFlags flags; + guint timeout; + guint version_check_retries; +} DeviceOpenContext; + +static void +device_open_context_complete_and_free (DeviceOpenContext *ctx) +{ + g_simple_async_result_complete_in_idle (ctx->result); + g_object_unref (ctx->result); + if (ctx->cancellable) + g_object_unref (ctx->cancellable); + g_object_unref (ctx->self); + g_slice_free (DeviceOpenContext, ctx); +} + +/** + * qmi_device_open_finish: + * @self: a #QmiDevice. + * @res: a #GAsyncResult. + * @error: a #GError. + * + * Finishes an asynchronous open operation started with qmi_device_open_async(). + * + * Returns: #TRUE if successful, #FALSE if @error is set. + */ +gboolean +qmi_device_open_finish (QmiDevice *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void process_open_flags (DeviceOpenContext *ctx); + +static void +sync_ready (QmiClientCtl *client_ctl, + GAsyncResult *res, + DeviceOpenContext *ctx) +{ + GError *error = NULL; + QmiMessageCtlSyncOutput *output; + + /* Check result of the async operation */ + output = qmi_client_ctl_sync_finish (client_ctl, res, &error); + if(!output) { + g_simple_async_result_take_error (ctx->result, error); + device_open_context_complete_and_free (ctx); + return; + } + + /* Check result of the QMI operation */ + if (!qmi_message_ctl_sync_output_get_result (output, &error)) { + g_simple_async_result_take_error (ctx->result, error); + device_open_context_complete_and_free (ctx); + qmi_message_ctl_sync_output_unref (output); + return; + } + + g_debug ("[%s] Sync operation finished", + ctx->self->priv->path_display); + + /* Keep on with next flags */ + process_open_flags (ctx); + qmi_message_ctl_sync_output_unref (output); +} + +static void +version_info_ready (QmiClientCtl *client_ctl, + GAsyncResult *res, + DeviceOpenContext *ctx) +{ + GArray *service_list; + QmiMessageCtlGetVersionInfoOutput *output; + GError *error = NULL; + guint i; + + /* Check result of the async operation */ + output = qmi_client_ctl_get_version_info_finish (client_ctl, res, &error); + if (!output) { + if (g_error_matches (error, QMI_CORE_ERROR, QMI_CORE_ERROR_TIMEOUT)) { + /* Update retries... */ + ctx->version_check_retries--; + /* If retries left, retry */ + if (ctx->version_check_retries > 0) { + qmi_client_ctl_get_version_info (ctx->self->priv->client_ctl, + NULL, + 1, + ctx->cancellable, + (GAsyncReadyCallback)version_info_ready, + ctx); + return; + } + + /* Otherwise, propagate the error */ + } + + g_simple_async_result_take_error (ctx->result, error); + device_open_context_complete_and_free (ctx); + return; + } + + /* Check result of the QMI operation */ + if (!qmi_message_ctl_get_version_info_output_get_result (output, &error)) { + g_simple_async_result_take_error (ctx->result, error); + device_open_context_complete_and_free (ctx); + qmi_message_ctl_get_version_info_output_unref (output); + return; + } + + /* QMI operation succeeded, we can now get the outputs */ + service_list = NULL; + qmi_message_ctl_get_version_info_output_get_service_list (output, + &service_list, + NULL); + ctx->self->priv->supported_services = g_array_ref (service_list); + + g_debug ("[%s] QMI Device supports %u services:", + ctx->self->priv->path_display, + ctx->self->priv->supported_services->len); + for (i = 0; i < ctx->self->priv->supported_services->len; i++) { + QmiMessageCtlGetVersionInfoOutputServiceListService *info; + + info = &g_array_index (ctx->self->priv->supported_services, + QmiMessageCtlGetVersionInfoOutputServiceListService, + i); + g_debug ("[%s] %s (%u.%u)", + ctx->self->priv->path_display, + qmi_service_get_string (info->service), + info->major_version, + info->minor_version); + } + + /* Keep on with next flags */ + process_open_flags (ctx); + qmi_message_ctl_get_version_info_output_unref (output); +} + +static void +process_open_flags (DeviceOpenContext *ctx) +{ + /* Query version info? */ + if (ctx->flags & QMI_DEVICE_OPEN_FLAGS_VERSION_INFO) { + ctx->flags &= ~QMI_DEVICE_OPEN_FLAGS_VERSION_INFO; + /* Setup how many times to retry... We'll retry once per second */ + ctx->version_check_retries = ctx->timeout > 0 ? ctx->timeout : 1; + g_debug ("Checking version info (%u retries)...", ctx->version_check_retries); + qmi_client_ctl_get_version_info (ctx->self->priv->client_ctl, + NULL, + 1, + ctx->cancellable, + (GAsyncReadyCallback)version_info_ready, + ctx); + return; + } + + /* Sync? */ + if (ctx->flags & QMI_DEVICE_OPEN_FLAGS_SYNC) { + g_debug ("Running sync..."); + ctx->flags &= ~QMI_DEVICE_OPEN_FLAGS_SYNC; + qmi_client_ctl_sync (ctx->self->priv->client_ctl, + NULL, + ctx->timeout, + ctx->cancellable, + (GAsyncReadyCallback)sync_ready, + ctx); + return; + } + + /* No more flags to process, done we are */ + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + device_open_context_complete_and_free (ctx); +} + +/** + * qmi_device_open: + * @self: a #QmiDevice. + * @flags: mask of #QmiDeviceOpenFlags specifying how the device should be opened. + * @timeout: maximum time, in seconds, to wait for the device to be opened. + * @cancellable: optional #GCancellable object, #NULL to ignore. + * @callback: a #GAsyncReadyCallback to call when the operation is finished. + * @user_data: the data to pass to callback function. + * + * Asynchronously opens a #QmiDevice for I/O. + * + * When the operation is finished @callback will be called. You can then call + * qmi_device_open_finish() to get the result of the operation. + */ +void +qmi_device_open (QmiDevice *self, + QmiDeviceOpenFlags flags, + guint timeout, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + DeviceOpenContext *ctx; + GError *error = NULL; + + g_return_if_fail (QMI_IS_DEVICE (self)); + + ctx = g_slice_new (DeviceOpenContext); + ctx->self = g_object_ref (self); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + qmi_device_open); + ctx->flags = flags; + ctx->timeout = timeout; + ctx->cancellable = (cancellable ? g_object_ref (cancellable) : NULL); + + if (!create_iochannel (self, &error)) { + g_prefix_error (&error, + "Cannot open QMI device: "); + g_simple_async_result_take_error (ctx->result, error); + device_open_context_complete_and_free (ctx); + return; + } + + /* Process all open flags */ + process_open_flags (ctx); +} + +/*****************************************************************************/ +/* Close channel */ + +static gboolean +destroy_iochannel (QmiDevice *self, + GError **error) +{ + GError *inner_error = NULL; + + /* Already closed? */ + if (!self->priv->iochannel) + return TRUE; + + g_io_channel_shutdown (self->priv->iochannel, TRUE, &inner_error); + + /* Failures when closing still make the device to get closed */ + g_io_channel_unref (self->priv->iochannel); + self->priv->iochannel = NULL; + + if (self->priv->watch_id) { + g_source_remove (self->priv->watch_id); + self->priv->watch_id = 0; + } + + if (self->priv->response) { + g_byte_array_unref (self->priv->response); + self->priv->response = NULL; + } + + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + + return TRUE; +} + +/** + * qmi_device_close: + * @self: a #QmiDevice + * @error: a #GError + * + * Synchronously closes a #QmiDevice, preventing any further I/O. + * + * Closing a #QmiDevice multiple times will not return an error. + * + * Returns: #TRUE if successful, #FALSE if @error is set. + */ +gboolean +qmi_device_close (QmiDevice *self, + GError **error) +{ + g_return_val_if_fail (QMI_IS_DEVICE (self), FALSE); + + if (!destroy_iochannel (self, error)) { + g_prefix_error (error, + "Cannot close QMI device: "); + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ +/* Command */ + +QmiMessage * +qmi_device_command_finish (QmiDevice *self, + GAsyncResult *res, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) + return NULL; + + return qmi_message_ref (g_simple_async_result_get_op_res_gpointer ( + G_SIMPLE_ASYNC_RESULT (res))); +} + +void +qmi_device_command (QmiDevice *self, + QmiMessage *message, + guint timeout, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GError *error = NULL; + Transaction *tr; + gconstpointer raw_message; + gsize raw_message_len; + gsize written; + GIOStatus write_status; + + g_return_if_fail (QMI_IS_DEVICE (self)); + g_return_if_fail (message != NULL); + + tr = transaction_new (self, message, callback, user_data); + + /* Device must be open */ + if (!self->priv->iochannel) { + error = g_error_new (QMI_CORE_ERROR, + QMI_CORE_ERROR_WRONG_STATE, + "Device must be open to send commands"); + transaction_complete_and_free (tr, NULL, error); + g_error_free (error); + return; + } + + /* Non-CTL services should use a proper CID */ + if (qmi_message_get_service (message) != QMI_SERVICE_CTL && + qmi_message_get_client_id (message) == 0) { + error = g_error_new (QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "Cannot send message in service '%s' without a CID", + qmi_service_get_string (qmi_message_get_service (message))); + transaction_complete_and_free (tr, NULL, error); + g_error_free (error); + return; + } + +#ifdef MESSAGE_ENABLE_TRACE + { + gchar *printable; + + printable = qmi_message_get_printable (message, "<<<<<< "); + g_debug ("[%s] Sending message...\n%s", + self->priv->path_display, + printable); + g_free (printable); + } +#endif /* MESSAGE_ENABLE_TRACE */ + + /* Get raw message */ + raw_message = qmi_message_get_raw (message, &raw_message_len, &error); + if (!raw_message) { + g_prefix_error (&error, "Cannot get raw message: "); + transaction_complete_and_free (tr, NULL, error); + g_error_free (error); + return; + } + + /* Setup context to match response */ + device_store_transaction (self, tr, timeout); + + written = 0; + write_status = G_IO_STATUS_AGAIN; + while (write_status == G_IO_STATUS_AGAIN) { + write_status = g_io_channel_write_chars (self->priv->iochannel, + raw_message, + (gssize)raw_message_len, + &written, + &error); + switch (write_status) { + case G_IO_STATUS_ERROR: + g_prefix_error (&error, "Cannot write message: "); + + /* Match transaction so that we remove it from our tracking table */ + tr = device_match_transaction (self, message); + transaction_complete_and_free (tr, NULL, error); + g_error_free (error); + return; + + case G_IO_STATUS_EOF: + /* We shouldn't get EOF when writing */ + g_assert_not_reached (); + break; + + case G_IO_STATUS_NORMAL: + /* All good, we'll exit the loop now */ + break; + + case G_IO_STATUS_AGAIN: + /* We're in a non-blocking channel and therefore we're up to receive + * EAGAIN; just retry in this case. TODO: in an idle? */ + break; + } + } + + /* Just return, we'll get response asynchronously */ +} + +/*****************************************************************************/ +/* New QMI device */ + +/** + * qmi_device_new_finish: + * @res: a #GAsyncResult. + * @error: a #GError. + * + * Finishes an operation started with qmi_device_new(). + * + * Returns: A newly created #QmiDevice, or #NULL if @error is set. + */ +QmiDevice * +qmi_device_new_finish (GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + + return (ret ? QMI_DEVICE (ret) : NULL); +} + +/** + * qmi_device_new: + * @file: a #GFile. + * @cancellable: optional #GCancellable object, #NULL to ignore. + * @callback: a #GAsyncReadyCallback to call when the initialization is finished. + * @user_data: the data to pass to callback function. + * + * Asynchronously creates a #QmiDevice object to manage @file. + * When the operation is finished, @callback will be invoked. You can then call + * qmi_device_new_finish() to get the result of the operation. + */ +void +qmi_device_new (GFile *file, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (QMI_TYPE_DEVICE, + G_PRIORITY_DEFAULT, + cancellable, + callback, + user_data, + QMI_DEVICE_FILE, file, + NULL); +} + +/*****************************************************************************/ +/* Async init */ + +typedef struct { + QmiDevice *self; + GSimpleAsyncResult *result; + GCancellable *cancellable; +} InitContext; + +static void +init_context_complete_and_free (InitContext *ctx) +{ + g_simple_async_result_complete_in_idle (ctx->result); + if (ctx->cancellable) + g_object_unref (ctx->cancellable); + g_object_unref (ctx->result); + g_object_unref (ctx->self); + g_slice_free (InitContext, ctx); +} + +static gboolean +initable_init_finish (GAsyncInitable *initable, + GAsyncResult *result, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); +} + +static void +query_info_async_ready (GFile *file, + GAsyncResult *res, + InitContext *ctx) +{ + GError *error = NULL; + GFileInfo *info; + + info = g_file_query_info_finish (file, res, &error); + if (!info) { + g_prefix_error (&error, + "Couldn't query file info: "); + g_simple_async_result_take_error (ctx->result, error); + init_context_complete_and_free (ctx); + return; + } + + /* Our QMI device must be of SPECIAL type */ + if (g_file_info_get_file_type (info) != G_FILE_TYPE_SPECIAL) { + g_simple_async_result_set_error (ctx->result, + QMI_CORE_ERROR, + QMI_CORE_ERROR_FAILED, + "Wrong file type"); + init_context_complete_and_free (ctx); + return; + } + g_object_unref (info); + + /* Create the implicit CTL client */ + ctx->self->priv->client_ctl = g_object_new (QMI_TYPE_CLIENT_CTL, + QMI_CLIENT_DEVICE, ctx->self, + QMI_CLIENT_SERVICE, QMI_SERVICE_CTL, + QMI_CLIENT_CID, QMI_CID_NONE, + NULL); + + /* Register the CTL client to get indications */ + register_client (ctx->self, + QMI_CLIENT (ctx->self->priv->client_ctl), + &error); + g_assert_no_error (error); + + /* Done we are */ + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + init_context_complete_and_free (ctx); +} + +static void +initable_init_async (GAsyncInitable *initable, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + InitContext *ctx; + + ctx = g_slice_new0 (InitContext); + ctx->self = g_object_ref (initable); + if (cancellable) + ctx->cancellable = g_object_ref (cancellable); + ctx->result = g_simple_async_result_new (G_OBJECT (initable), + callback, + user_data, + initable_init_async); + + /* We need a proper file to initialize */ + if (!ctx->self->priv->file) { + g_simple_async_result_set_error (ctx->result, + QMI_CORE_ERROR, + QMI_CORE_ERROR_INVALID_ARGS, + "Cannot initialize QMI device: No file given"); + init_context_complete_and_free (ctx); + return; + } + + /* Check the file type. Note that this is just a quick check to avoid + * creating QmiDevices pointing to a location already known not to be a QMI + * device. */ + g_file_query_info_async (ctx->self->priv->file, + G_FILE_ATTRIBUTE_STANDARD_TYPE, + G_FILE_QUERY_INFO_NONE, + G_PRIORITY_DEFAULT, + ctx->cancellable, + (GAsyncReadyCallback)query_info_async_ready, + ctx); +} + +/*****************************************************************************/ + +static void +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + QmiDevice *self = QMI_DEVICE (object); + + switch (prop_id) { + case PROP_FILE: + g_assert (self->priv->file == NULL); + self->priv->file = g_value_dup_object (value); + self->priv->path = g_file_get_path (self->priv->file); + self->priv->path_display = g_filename_display_name (self->priv->path); + break; + case PROP_CLIENT_CTL: + /* Not writable */ + g_assert_not_reached (); + 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) +{ + QmiDevice *self = QMI_DEVICE (object); + + switch (prop_id) { + case PROP_FILE: + g_value_set_object (value, self->priv->file); + break; + case PROP_CLIENT_CTL: + g_value_set_object (value, self->priv->client_ctl); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +qmi_device_init (QmiDevice *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), + QMI_TYPE_DEVICE, + QmiDevicePrivate); + + self->priv->registered_clients = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); +} + +static gboolean +foreach_warning (gpointer key, + QmiClient *client, + QmiDevice *self) +{ + g_warning ("QMI client for service '%s' with CID '%u' wasn't released", + qmi_service_get_string (qmi_client_get_service (client)), + qmi_client_get_cid (client)); + + return TRUE; +} + +static void +dispose (GObject *object) +{ + QmiDevice *self = QMI_DEVICE (object); + + g_clear_object (&self->priv->file); + + /* unregister our CTL client */ + unregister_client (self, QMI_CLIENT (self->priv->client_ctl)); + + /* If clients were left unreleased, we'll just warn about it. + * There is no point in trying to request CID releases, as the device + * itself is being disposed. */ + g_hash_table_foreach_remove (self->priv->registered_clients, + (GHRFunc)foreach_warning, + self); + + g_clear_object (&self->priv->client_ctl); + + G_OBJECT_CLASS (qmi_device_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + QmiDevice *self = QMI_DEVICE (object); + + /* Transactions keep refs to the device, so it's actually + * impossible to have any content in the HT */ + if (self->priv->transactions) { + g_assert (g_hash_table_size (self->priv->transactions) == 0); + g_hash_table_unref (self->priv->transactions); + } + + g_hash_table_unref (self->priv->registered_clients); + + if (self->priv->supported_services) + g_array_unref (self->priv->supported_services); + + g_free (self->priv->path); + g_free (self->priv->path_display); + if (self->priv->response) + g_byte_array_unref (self->priv->response); + if (self->priv->iochannel) + g_io_channel_unref (self->priv->iochannel); + + G_OBJECT_CLASS (qmi_device_parent_class)->finalize (object); +} + +static void +async_initable_iface_init (GAsyncInitableIface *iface) +{ + iface->init_async = initable_init_async; + iface->init_finish = initable_init_finish; +} + +static void +qmi_device_class_init (QmiDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (QmiDevicePrivate)); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + object_class->dispose = dispose; + + properties[PROP_FILE] = + g_param_spec_object (QMI_DEVICE_FILE, + "Device file", + "File to the underlying QMI device", + G_TYPE_FILE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_FILE, properties[PROP_FILE]); + + properties[PROP_CLIENT_CTL] = + g_param_spec_object (QMI_DEVICE_CLIENT_CTL, + "CTL client", + "Implicit CTL client", + QMI_TYPE_CLIENT_CTL, + G_PARAM_READABLE); + g_object_class_install_property (object_class, PROP_CLIENT_CTL, properties[PROP_CLIENT_CTL]); +} diff --git a/libqmi-glib/qmi-device.h b/libqmi-glib/qmi-device.h new file mode 100644 index 0000000..79272d8 --- /dev/null +++ b/libqmi-glib/qmi-device.h @@ -0,0 +1,145 @@ +/* -*- 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 Aleksander Morgado + */ + +#ifndef _LIBQMI_GLIB_QMI_DEVICE_H_ +#define _LIBQMI_GLIB_QMI_DEVICE_H_ + +#include + +#include "qmi-enums.h" +#include "qmi-message.h" +#include "qmi-client.h" + +G_BEGIN_DECLS + +#define QMI_TYPE_DEVICE (qmi_device_get_type ()) +#define QMI_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), QMI_TYPE_DEVICE, QmiDevice)) +#define QMI_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), QMI_TYPE_DEVICE, QmiDeviceClass)) +#define QMI_IS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), QMI_TYPE_DEVICE)) +#define QMI_IS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), QMI_TYPE_DEVICE)) +#define QMI_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), QMI_TYPE_DEVICE, QmiDeviceClass)) + +typedef struct _QmiDevice QmiDevice; +typedef struct _QmiDeviceClass QmiDeviceClass; +typedef struct _QmiDevicePrivate QmiDevicePrivate; + +#define QMI_DEVICE_FILE "device-file" +#define QMI_DEVICE_CLIENT_CTL "device-client-ctl" + +struct _QmiDevice { + GObject parent; + QmiDevicePrivate *priv; +}; + +struct _QmiDeviceClass { + GObjectClass parent; +}; + +GType qmi_device_get_type (void); + +void qmi_device_new (GFile *file, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +QmiDevice *qmi_device_new_finish (GAsyncResult *res, + GError **error); + +GFile *qmi_device_get_file (QmiDevice *self); +GFile *qmi_device_peek_file (QmiDevice *self); +const gchar *qmi_device_get_path (QmiDevice *self); +const gchar *qmi_device_get_path_display (QmiDevice *self); +gboolean qmi_device_is_open (QmiDevice *self); + +/** + * QmiDeviceOpenFlags: + * @QMI_DEVICE_OPEN_FLAGS_NONE: No flags. + * @QMI_DEVICE_OPEN_FLAGS_VERSION_INFO: Run version info check when opening. + * @QMI_DEVICE_OPEN_FLAGS_SYNC: Synchronize with endpoint once the device is open. Will release any previously allocated client ID. + * + * Flags to specify which actions to be performed when the device is open. + */ +typedef enum { + QMI_DEVICE_OPEN_FLAGS_NONE = 0, + QMI_DEVICE_OPEN_FLAGS_VERSION_INFO = 1 << 0, + QMI_DEVICE_OPEN_FLAGS_SYNC = 1 << 1 +} QmiDeviceOpenFlags; + +void qmi_device_open (QmiDevice *self, + QmiDeviceOpenFlags flags, + guint timeout, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean qmi_device_open_finish (QmiDevice *self, + GAsyncResult *res, + GError **error); + +gboolean qmi_device_close (QmiDevice *self, + GError **error); + +void qmi_device_allocate_client (QmiDevice *self, + QmiService service, + guint8 cid, + guint timeout, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +QmiClient *qmi_device_allocate_client_finish (QmiDevice *self, + GAsyncResult *res, + GError **error); + +/** + * QmiDeviceReleaseClientFlags: + * @QMI_DEVICE_RELEASE_CLIENT_FLAGS_NONE: No flags. + * @QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID: Release the CID when releasing the client. + * + * Flags to specify which actions to be performed when releasing the client. + */ +typedef enum { + QMI_DEVICE_RELEASE_CLIENT_FLAGS_NONE = 0, + QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID = 1 << 0 +} QmiDeviceReleaseClientFlags; + +void qmi_device_release_client (QmiDevice *self, + QmiClient *client, + QmiDeviceReleaseClientFlags flags, + guint timeout, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean qmi_device_release_client_finish (QmiDevice *self, + GAsyncResult *res, + GError **error); + +void qmi_device_command (QmiDevice *self, + QmiMessage *message, + guint timeout, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +QmiMessage *qmi_device_command_finish (QmiDevice *self, + GAsyncResult *res, + GError **error); + +G_END_DECLS + +#endif /* _LIBQMI_GLIB_QMI_DEVICE_H_ */ diff --git a/libqmi-glib/qmi-enums-dms.h b/libqmi-glib/qmi-enums-dms.h new file mode 100644 index 0000000..5103599 --- /dev/null +++ b/libqmi-glib/qmi-enums-dms.h @@ -0,0 +1,195 @@ +/* -*- 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 + */ + +#ifndef _LIBQMI_GLIB_QMI_ENUMS_DMS_H_ +#define _LIBQMI_GLIB_QMI_ENUMS_DMS_H_ + +/*****************************************************************************/ +/* Helper enums for the 'QMI DMS Get Capabilities' message */ + +/** + * QmiDmsDataServiceCapability: + * @QMI_DMS_DATA_SERVICE_CAPABILITY_NONE: No data services supported. + * @QMI_DMS_DATA_SERVICE_CAPABILITY_CS: Only CS supported. + * @QMI_DMS_DATA_SERVICE_CAPABILITY_PS: Only PS supported. + * @QMI_DMS_DATA_SERVICE_CAPABILITY_SIMULTANEOUS_CS_PS: Simultaneous CS and PS supported. + * @QMI_DMS_DATA_SERVICE_CAPABILITY_NON_SIMULTANEOUS_CS_PS: Non simultaneous CS and PS supported. + * + * Data service capability. + */ +typedef enum { + QMI_DMS_DATA_SERVICE_CAPABILITY_NONE = 0, + QMI_DMS_DATA_SERVICE_CAPABILITY_CS = 1, + QMI_DMS_DATA_SERVICE_CAPABILITY_PS = 2, + QMI_DMS_DATA_SERVICE_CAPABILITY_SIMULTANEOUS_CS_PS = 3, + QMI_DMS_DATA_SERVICE_CAPABILITY_NON_SIMULTANEOUS_CS_PS = 4 +} QmiDmsDataServiceCapability; + +/** + * QmiDmsSimCapability: + * @QMI_DMS_SIM_CAPABILITY_NOT_SUPPORTED: SIM not supported. + * @QMI_DMS_SIM_CAPABILITY_SUPPORTED: SIM is supported. + * + * SIM capability. + */ +typedef enum { + QMI_DMS_SIM_CAPABILITY_NOT_SUPPORTED = 1, + QMI_DMS_SIM_CAPABILITY_SUPPORTED = 2 +} QmiDmsSimCapability; + +/** + * QmiDmsRadioInterface: + * @QMI_DMS_RADIO_INTERFACE_CDMA20001X: CDMA2000 1x. + * @QMI_DMS_RADIO_INTERFACE_EVDO: CDMA2000 HRPD (1xEV-DO) + * @QMI_DMS_RADIO_INTERFACE_GSM: GSM. + * @QMI_DMS_RADIO_INTERFACE_UMTS: UMTS. + * @QMI_DMS_RADIO_INTERFACE_LTE: LTE. + * + * Radio interface type. + */ +typedef enum { + QMI_DMS_RADIO_INTERFACE_CDMA20001X = 1, + QMI_DMS_RADIO_INTERFACE_EVDO = 2, + QMI_DMS_RADIO_INTERFACE_GSM = 4, + QMI_DMS_RADIO_INTERFACE_UMTS = 5, + QMI_DMS_RADIO_INTERFACE_LTE = 8 +} QmiDmsRadioInterface; + + +/*****************************************************************************/ +/* Helper enums for the 'QMI DMS Get Power State' message */ + +/** + * QmiDmsPowerState: + * @QMI_DMS_POWER_STATE_EXTERNAL_SOURCE: Powered by an external source. + * @QMI_DMS_POWER_STATE_BATTERY_CONNECTED: Battery is connected. + * @QMI_DMS_POWER_STATE_BATTERY_CHARGING: Battery is currently being charged. + * @QMI_DMS_POWER_STATE_FAULT: Recognized power fault. + * + * Flags specifying the current power state. + * + * If @QMI_DMS_POWER_STATE_EXTERNAL_SOURCE is set, the device is powerered by an + * external source; otherwise it is powered by a battery. + * + * If @QMI_DMS_POWER_STATE_BATTERY_CONNECTED is set, the battery is connected; + * otherwise the battery is not connected. + * + * If @QMI_DMS_POWER_STATE_BATTERY_CHARGING is set, the battery is being charged; + * otherwise the battery is not being charged. + * + * If @QMI_DMS_POWER_STATE_FAULT is set, a power fault has been detected. + */ +typedef enum { + QMI_DMS_POWER_STATE_EXTERNAL_SOURCE = 1 << 0, + QMI_DMS_POWER_STATE_BATTERY_CONNECTED = 1 << 1, + QMI_DMS_POWER_STATE_BATTERY_CHARGING = 1 << 2, + QMI_DMS_POWER_STATE_FAULT = 1 << 3, +} QmiDmsPowerState; + + +/*****************************************************************************/ +/* Helper enums for the 'QMI DMS UIM Set PIN Protection' message */ + +/** + * QmiDmsUimPinId: + * @QMI_DMS_UIM_PIN_ID_PIN: PIN. + * @QMI_DMS_UIM_PIN_ID_PIN2: PIN2. + * + * The PIN identifier. + */ +typedef enum { + QMI_DMS_UIM_PIN_ID_PIN = 1, + QMI_DMS_UIM_PIN_ID_PIN2 = 2 +} QmiDmsUimPinId; + + +/*****************************************************************************/ +/* Helper enums for the 'QMI DMS UIM Get PIN Status' message */ + +/** + * QmiDmsPinStatus: + * @QMI_DMS_UIM_PIN_STATUS_NOT_INITIALIZED: Not initialized. + * @QMI_DMS_UIM_PIN_STATUS_ENABLED_NOT_VERIFIED: Enabled, not verified. + * @QMI_DMS_UIM_PIN_STATUS_ENABLED_VERIFIED: Enabled, verified. + * @QMI_DMS_UIM_PIN_STATUS_DISABLED: Disabled. + * @QMI_DMS_UIM_PIN_STATUS_BLOCKED: Blocked. + * @QMI_DMS_UIM_PIN_STATUS_PERMANENTLY_BLOCKED: Permanently Blocked. + * @QMI_DMS_UIM_PIN_STATUS_UNBLOCKED: Unblocked. + * @QMI_DMS_UIM_PIN_STATUS_CHANGED: Changed. + * + * The PIN status. + */ +typedef enum { + QMI_DMS_UIM_PIN_STATUS_NOT_INITIALIZED = 0, + QMI_DMS_UIM_PIN_STATUS_ENABLED_NOT_VERIFIED = 1, + QMI_DMS_UIM_PIN_STATUS_ENABLED_VERIFIED = 2, + QMI_DMS_UIM_PIN_STATUS_DISABLED = 3, + QMI_DMS_UIM_PIN_STATUS_BLOCKED = 4, + QMI_DMS_UIM_PIN_STATUS_PERMANENTLY_BLOCKED = 5, + QMI_DMS_UIM_PIN_STATUS_UNBLOCKED = 6, + QMI_DMS_UIM_PIN_STATUS_CHANGED = 7, +} QmiDmsUimPinStatus; + +/*****************************************************************************/ +/* Helper enums for the 'QMI DMS Get Operating Mode' message */ + +/** + * QmiDmsOperatingMode: + * @QMI_DMS_OPERATING_MODE_ONLINE: Device can acquire a system and make calls. + * @QMI_DMS_OPERATING_MODE_LOW_POWER: Device has temporarily disabled RF. + * @QMI_DMS_OPERATING_MODE_PERSISTENT_LOW_POWER: Device has disabled RF and state persists even after a reset. + * @QMI_DMS_OPERATING_MODE_FACTORY_TEST: Special mode for manufacturer tests. + * @QMI_DMS_OPERATING_MODE_OFFLINE: Device has deactivated RF and is partially shutdown. + * @QMI_DMS_OPERATING_MODE_RESETTING: Device is in the process of power cycling. + * @QMI_DMS_OPERATING_MODE_SHUTTING_DOWN: Device is in the process of shutting down. + * @QMI_DMS_OPERATING_MODE_MODE_ONLY_LOW_POWER: Mode-only Low Power. + * + * Operating mode of the device. + */ +typedef enum { + QMI_DMS_OPERATING_MODE_ONLINE = 0, + QMI_DMS_OPERATING_MODE_LOW_POWER = 1, + QMI_DMS_OPERATING_MODE_FACTORY_TEST = 2, + QMI_DMS_OPERATING_MODE_OFFLINE = 3, + QMI_DMS_OPERATING_MODE_RESETTING = 4, + QMI_DMS_OPERATING_MODE_SHUTTING_DOWN = 5, + QMI_DMS_OPERATING_MODE_PERSISTENT_LOW_POWER = 6, + QMI_DMS_OPERATING_MODE_MODE_ONLY_LOW_POWER = 7 +} QmiDmsOperatingMode; + +/** + * QmiDmsOfflineReason: + * @QMI_DMS_OFFLINE_REASON_HOST_IMAGE_MISCONFIGURATION: Host image misconfiguration. + * @QMI_DMS_OFFLINE_REASON_PRI_IMAGE_MISCONFIGURATION: PRI image misconfiguration. + * @QMI_DMS_OFFLINE_REASON_PRI_VERSION_INCOMPATIBLE: PRI version incompatible. + * @QMI_DMS_OFFLINE_REASON_DEVICE_MEMORY_FULL: Memory full, cannot copy PRI information. + * + * Reasons for being in Offline (@QMI_DMS_OPERATING_MODE_OFFLINE) state. + */ +typedef enum { + QMI_DMS_OFFLINE_REASON_HOST_IMAGE_MISCONFIGURATION = 1 << 0, + QMI_DMS_OFFLINE_REASON_PRI_IMAGE_MISCONFIGURATION = 1 << 1, + QMI_DMS_OFFLINE_REASON_PRI_VERSION_INCOMPATIBLE = 1 << 2, + QMI_DMS_OFFLINE_REASON_DEVICE_MEMORY_FULL = 1 << 3 +} QmiDmsOfflineReason; + +#endif /* _LIBQMI_GLIB_QMI_ENUMS_DMS_H_ */ diff --git a/libqmi-glib/qmi-enums-wds.h b/libqmi-glib/qmi-enums-wds.h new file mode 100644 index 0000000..79ab360 --- /dev/null +++ b/libqmi-glib/qmi-enums-wds.h @@ -0,0 +1,197 @@ +/* -*- 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 + */ + +#ifndef _LIBQMI_GLIB_QMI_ENUMS_WDS_H_ +#define _LIBQMI_GLIB_QMI_ENUMS_WDS_H_ + +/*****************************************************************************/ +/* Helper enums for the 'QMI WDS Get Packet Service Status' message */ + +/** + * QmiWdsConnectionStatus: + * @QMI_WDS_CONNECTION_STATUS_UNKNOWN: Unknown status. + * @QMI_WDS_CONNECTION_STATUS_DISCONNECTED: Network is disconnected + * @QMI_WDS_CONNECTION_STATUS_CONNECTED: Network is connected. + * @QMI_WDS_CONNECTION_STATUS_SUSPENDED: Network connection is suspended. + * @QMI_WDS_CONNECTION_STATUS_AUTHENTICATING: Network authentication is ongoing. + * + * Status of the network connection. + */ +typedef enum { + QMI_WDS_CONNECTION_STATUS_UNKNOWN = 0, + QMI_WDS_CONNECTION_STATUS_DISCONNECTED = 1, + QMI_WDS_CONNECTION_STATUS_CONNECTED = 2, + QMI_WDS_CONNECTION_STATUS_SUSPENDED = 3, + QMI_WDS_CONNECTION_STATUS_AUTHENTICATING = 4 +} QmiWdsConnectionStatus; + + +/*****************************************************************************/ +/* Helper enums for the 'QMI WDS Get Data Bearer Technology' message */ + +/** + * QmiWdsDataBearerTechnology: + * @QMI_WDS_DATA_BEARER_TECHNOLOGY_UNKNOWN: Unknown. + * @QMI_WDS_DATA_BEARER_TECHNOLOGY_CDMA20001X: CDMA2000 1x. + * @QMI_WDS_DATA_BEARER_TECHNOLOGY_1xEVDO: CDMA2000 HRPD 1xEV-DO. + * @QMI_WDS_DATA_BEARER_TECHNOLOGY_GSM: GSM. + * @QMI_WDS_DATA_BEARER_TECHNOLOGY_UMTS: UMTS. + * @QMI_WDS_DATA_BEARER_TECHNOLOGY_1xEVDO_REVA: CDMA2000 HRPD 1xEV-DO RevA. + * @QMI_WDS_DATA_BEARER_TECHNOLOGY_EDGE: EDGE. + * @QMI_WDS_DATA_BEARER_TECHNOLOGY_HSDPA: HSDPA and WCDMA. + * @QMI_WDS_DATA_BEARER_TECHNOLOGY_HSUPA: WCDMA and HSUPA. + * @QMI_WDS_DATA_BEARER_TECHNOLOGY_HSDPA_HSUPDA: HSDPA and HSUPA. + * @QMI_WDS_DATA_BEARER_TECHNOLOGY_LTE: LTE. + * @QMI_WDS_DATA_BEARER_TECHNOLOGY_EHRPD: CDMA2000 eHRPD. + * @QMI_WDS_DATA_BEARER_TECHNOLOGY_HSDPAPLUS: HSDPA+ and WCDMA. + * @QMI_WDS_DATA_BEARER_TECHNOLOGY_HSDPAPLUS_HSUPA: HSDPA+ and HSUPA. + * @QMI_WDS_DATA_BEARER_TECHNOLOGY_DCHSDPAPLUS: DC-HSDPA+ and WCDMA. + * @QMI_WDS_DATA_BEARER_TECHNOLOGY_DCHSDPAPLUS_HSUPA: DC-HSDPA+ and HSUPA. + * + * Data bearer technology. + */ +typedef enum { + QMI_WDS_DATA_BEARER_TECHNOLOGY_UNKNOWN = -1, + QMI_WDS_DATA_BEARER_TECHNOLOGY_CDMA20001X = 0x01, + QMI_WDS_DATA_BEARER_TECHNOLOGY_1xEVDO = 0x02, + QMI_WDS_DATA_BEARER_TECHNOLOGY_GSM = 0x03, + QMI_WDS_DATA_BEARER_TECHNOLOGY_UMTS = 0x04, + QMI_WDS_DATA_BEARER_TECHNOLOGY_1xEVDO_REVA = 0x05, + QMI_WDS_DATA_BEARER_TECHNOLOGY_EDGE = 0x06, + QMI_WDS_DATA_BEARER_TECHNOLOGY_HSDPA = 0x07, + QMI_WDS_DATA_BEARER_TECHNOLOGY_HSUPA = 0x08, + QMI_WDS_DATA_BEARER_TECHNOLOGY_HSDPA_HSUPDA = 0x09, + QMI_WDS_DATA_BEARER_TECHNOLOGY_LTE = 0x0A, + QMI_WDS_DATA_BEARER_TECHNOLOGY_EHRPD = 0x0B, + QMI_WDS_DATA_BEARER_TECHNOLOGY_HSDPAPLUS = 0x0C, + QMI_WDS_DATA_BEARER_TECHNOLOGY_HSDPAPLUS_HSUPA = 0x0D, + QMI_WDS_DATA_BEARER_TECHNOLOGY_DCHSDPAPLUS = 0x0E, + QMI_WDS_DATA_BEARER_TECHNOLOGY_DCHSDPAPLUS_HSUPA = 0x0F +} QmiWdsDataBearerTechnology; + + +/*****************************************************************************/ +/* Helper enums for the 'QMI WDS Get Current Data Bearer Technology' message */ + +/** + * QmiWdsNetworkType: + * @QMI_WDS_NETWORK_TYPE_UNKNOWN: Unknown. + * @QMI_WDS_NETWORK_TYPE_3GPP2: 3GPP2 network type. + * @QMI_WDS_NETWORK_TYPE_3GPP: 3GPP network type. + * + * Network type of the data bearer. + */ +typedef enum { + QMI_WDS_NETWORK_TYPE_UNKNOWN = 0, + QMI_WDS_NETWORK_TYPE_3GPP2 = 1, + QMI_WDS_NETWORK_TYPE_3GPP = 2 +} QmiWdsNetworkType; + +/** + * QmiWdsRat3gpp2: + * @QMI_WDS_RAT_3GPP2_UNKNOWN: Unknown, to be ignored. + * @QMI_WDS_RAT_3GPP2_CDMA1X: CDMA 1x. + * @QMI_WDS_RAT_3GPP2_EVDO_REV0: EVDO Rev0. + * @QMI_WDS_RAT_3GPP2_EVDO_REVA: EVDO RevA. + * @QMI_WDS_RAT_3GPP2_EVDO_REVB: EVDO RevB. + * @QMI_WDS_RAT_3GPP2_NULL_BEARER: No bearer. + * + * Flags specifying the 3GPP2-specific Radio Access Technology, when the data + * bearer network type is @QMI_WDS_NETWORK_TYPE_3GPP2. + */ +typedef enum { /*< underscore_name=qmi_wds_rat_3gpp2 >*/ + QMI_WDS_RAT_3GPP2_NONE = 0, + QMI_WDS_RAT_3GPP2_CDMA1X = 1 << 0, + QMI_WDS_RAT_3GPP2_EVDO_REV0 = 1 << 1, + QMI_WDS_RAT_3GPP2_EVDO_REVA = 1 << 2, + QMI_WDS_RAT_3GPP2_EVDO_REVB = 1 << 3, + QMI_WDS_RAT_3GPP2_NULL_BEARER = 1 << 15 +} QmiWdsRat3gpp2; + +/** + * QmiWdsRat3gpp: + * @QMI_WDS_RAT_3GPP_NONE: Unknown, to be ignored. + * @QMI_WDS_RAT_3GPP_WCDMA: WCDMA. + * @QMI_WDS_RAT_3GPP_GPRS: GPRS. + * @QMI_WDS_RAT_3GPP_HSDPA: HSDPA. + * @QMI_WDS_RAT_3GPP_HSUPA: HSUPA. + * @QMI_WDS_RAT_3GPP_EDGE: EDGE. + * @QMI_WDS_RAT_3GPP_LTE: LTE. + * @QMI_WDS_RAT_3GPP_HSDPAPLUS: HSDPA+. + * @QMI_WDS_RAT_3GPP_DCHSDPAPLUS: DC-HSDPA+ + * @QMI_WDS_RAT_3GPP_NULL_BEARER: No bearer. + * + * Flags specifying the 3GPP-specific Radio Access Technology, when the data + * bearer network type is @QMI_WDS_NETWORK_TYPE_3GPP. + */ +typedef enum { /*< underscore_name=qmi_wds_rat_3gpp >*/ + QMI_WDS_RAT_3GPP_NONE = 0, + QMI_WDS_RAT_3GPP_WCDMA = 1 << 0, + QMI_WDS_RAT_3GPP_GPRS = 1 << 1, + QMI_WDS_RAT_3GPP_HSDPA = 1 << 2, + QMI_WDS_RAT_3GPP_HSUPA = 1 << 3, + QMI_WDS_RAT_3GPP_EDGE = 1 << 4, + QMI_WDS_RAT_3GPP_LTE = 1 << 5, + QMI_WDS_RAT_3GPP_HSDPAPLUS = 1 << 6, + QMI_WDS_RAT_3GPP_DCHSDPAPLUS = 1 << 7, + QMI_WDS_RAT_3GPP_NULL_BEARER = 1 << 15 +} QmiWdsRat3gpp; + +/** + * QmiWdsSoCdma1x: + * @QMI_WDS_SO_CDMA1X_NONE: Unknown, to be ignored. + * @QMI_WDS_SO_CDMA1X_IS95: IS95. + * @QMI_WDS_SO_CDMA1X_IS2000: IS2000. + * @QMI_WDS_SO_CDMA1X_IS2000_REL_A: IS2000 RelA. + * + * Flags specifying the Service Option when the bearer network type is + * @QMI_WDS_NETWORK_TYPE_3GPP2 and when the Radio Access Technology mask + * contains @QMI_WDS_RAT_3GPP2_CDMA1X. + */ +typedef enum { + QMI_WDS_SO_CDMA1X_NONE = 0, + QMI_WDS_SO_CDMA1X_IS95 = 1 << 0, + QMI_WDS_SO_CDMA1X_IS2000 = 1 << 1, + QMI_WDS_SO_CDMA1X_IS2000_REL_A = 1 << 2 +} QmiWdsSoCdma1x; + +/** + * QmiWdsSoEvdoRevA: + * @QMI_WDS_SO_EVDO_REVA_NONE: Unknown, to be ignored. + * @QMI_WDS_SO_EVDO_REVA_DPA: DPA. + * @QMI_WDS_SO_EVDO_REVA_MFPA: MFPA. + * @QMI_WDS_SO_EVDO_REVA_EMPA: EMPA. + * @QMI_WDS_SO_EVDO_REVA_EMPA_EHRPD: EMPA EHRPD. + * + * Flags specifying the Service Option when the bearer network type is + * @QMI_WDS_NETWORK_TYPE_3GPP2 and when the Radio Access Technology mask + * contains @QMI_WDS_RAT_3GPP2_EVDO_REVA. + */ +typedef enum { /*< underscore_name=qmi_wds_so_evdo_reva >*/ + QMI_WDS_SO_EVDO_REVA_NONE = 0, + QMI_WDS_SO_EVDO_REVA_DPA = 1 << 0, + QMI_WDS_SO_EVDO_REVA_MFPA = 1 << 1, + QMI_WDS_SO_EVDO_REVA_EMPA = 1 << 2, + QMI_WDS_SO_EVDO_REVA_EMPA_EHRPD = 1 << 3 +} QmiWdsSoEvdoRevA; + +#endif /* _LIBQMI_GLIB_QMI_ENUMS_WDS_H_ */ diff --git a/libqmi-glib/qmi-enums.h b/libqmi-glib/qmi-enums.h new file mode 100644 index 0000000..5710eba --- /dev/null +++ b/libqmi-glib/qmi-enums.h @@ -0,0 +1,86 @@ +/* -*- 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 Aleksander Morgado + */ + +#include "qmi-enums-wds.h" + +#ifndef _LIBQMI_GLIB_QMI_ENUMS_H_ +#define _LIBQMI_GLIB_QMI_ENUMS_H_ + +typedef enum { + /* Unknown service */ + QMI_SERVICE_UNKNOWN = -1, + /* Control service */ + QMI_SERVICE_CTL = 0x00, + /* Wireless Data Service */ + QMI_SERVICE_WDS = 0x01, + /* Device Management Service */ + QMI_SERVICE_DMS = 0x02, + /* Network Access Service */ + QMI_SERVICE_NAS = 0x03, + /* Quality Of Service service */ + QMI_SERVICE_QOS = 0x04, + /* Wireless Messaging Service */ + QMI_SERVICE_WMS = 0x05, + /* Position Determination Service */ + QMI_SERVICE_PDS = 0x06, + /* Authentication service */ + QMI_SERVICE_AUTH = 0x07, + /* AT service */ + QMI_SERVICE_AT = 0x08, + /* Voice service */ + QMI_SERVICE_VOICE = 0x09, + + QMI_SERVICE_CAT2 = 0x0A, + QMI_SERVICE_UIM = 0x0B, + QMI_SERVICE_PBM = 0x0C, + QMI_SERVICE_LOC = 0x10, + QMI_SERVICE_SAR = 0x11, + QMI_SERVICE_RMTFS = 0x14, + + /* Card Application Toolkit service */ + QMI_SERVICE_CAT = 0xE0, + /* Remote Management Service */ + QMI_SERVICE_RMS = 0xE1, + /* Open Mobile Alliance device management service */ + QMI_SERVICE_OMA = 0xE2 +} QmiService; + +/*****************************************************************************/ +/* QMI Control */ + +typedef enum { + QMI_CTL_FLAG_NONE = 0, + QMI_CTL_FLAG_RESPONSE = 1 << 0, + QMI_CTL_FLAG_INDICATION = 1 << 1 +} QmiCtlFlag; + +/*****************************************************************************/ +/* QMI Services */ + +typedef enum { + QMI_SERVICE_FLAG_NONE = 0, + QMI_SERVICE_FLAG_COMPOUND = 1 << 0, + QMI_SERVICE_FLAG_RESPONSE = 1 << 1, + QMI_SERVICE_FLAG_INDICATION = 1 << 2 +} QmiServiceFlag; + +#endif /* _LIBQMI_GLIB_QMI_ENUMS_H_ */ diff --git a/libqmi-glib/qmi-errors.h b/libqmi-glib/qmi-errors.h new file mode 100644 index 0000000..cff53be --- /dev/null +++ b/libqmi-glib/qmi-errors.h @@ -0,0 +1,212 @@ +/* -*- 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 Aleksander Morgado + */ + +#ifndef _LIBQMI_GLIB_QMI_ERRORS_H_ +#define _LIBQMI_GLIB_QMI_ERRORS_H_ + +/** + * QmiCoreError: + * @QMI_CORE_ERROR_FAILED: Operation failed. + * @QMI_CORE_ERROR_WRONG_STATE: Operation cannot be executed in the current state. + * @QMI_CORE_ERROR_TIMEOUT: Operation timed out. + * @QMI_CORE_ERROR_INVALID_ARGS: Invalid arguments given. + * @QMI_CORE_ERROR_INVALID_MESSAGE: QMI message is invalid. + * @QMI_CORE_ERROR_TLV_NOT_FOUND: TLV not found. + * @QMI_CORE_ERROR_TLV_TOO_LONG: TLV is too long. + * @QMI_CORE_ERROR_UNSUPPORTED: Not supported. + * + * Common errors that may be reported by libqmi-glib. + */ +typedef enum { + QMI_CORE_ERROR_FAILED, + QMI_CORE_ERROR_WRONG_STATE, + QMI_CORE_ERROR_TIMEOUT, + QMI_CORE_ERROR_INVALID_ARGS, + QMI_CORE_ERROR_INVALID_MESSAGE, + QMI_CORE_ERROR_TLV_NOT_FOUND, + QMI_CORE_ERROR_TLV_TOO_LONG, + QMI_CORE_ERROR_UNSUPPORTED +} QmiCoreError; + +/** + * QmiProtocolError: + * @QMI_PROTOCOL_ERROR_NONE: No error. + * @QMI_PROTOCOL_ERROR_MALFORMED_MESSAGE: Malformed message. + * @QMI_PROTOCOL_ERROR_NO_MEMORY: No memory. + * @QMI_PROTOCOL_ERROR_INTERNAL: Internal. + * @QMI_PROTOCOL_ERROR_ABORTED: Aborted. + * @QMI_PROTOCOL_ERROR_CLIENT_IDS_EXHAUSTED: Client IDs exhausted. + * @QMI_PROTOCOL_ERROR_UNABORTABLE_TRANSACTION: Unabortable transaction. + * @QMI_PROTOCOL_ERROR_INVALID_CLIENT_ID: Invalid client ID. + * @QMI_PROTOCOL_ERROR_NO_THRESHOLDS_PROVIDED: No thresholds provided. + * @QMI_PROTOCOL_ERROR_INVALID_HANDLE: Invalid handle. + * @QMI_PROTOCOL_ERROR_INVALID_PROFILE: Invalid profile. + * @QMI_PROTOCOL_ERROR_INVALID_PIN_ID: Invalid PIN ID. + * @QMI_PROTOCOL_ERROR_INCORRECT_PIN: Incorrect PIN. + * @QMI_PROTOCOL_ERROR_NO_NETWORK_FOUND: No network found. + * @QMI_PROTOCOL_ERROR_CALL_FAILED: Call failed. + * @QMI_PROTOCOL_ERROR_OUT_OF_CALL: Out of call. + * @QMI_PROTOCOL_ERROR_NOT_PROVISIONED: Not provisioned. + * @QMI_PROTOCOL_ERROR_MISSING_ARGUMENT: Missing argument. + * @QMI_PROTOCOL_ERROR_ARGUMENT_TOO_LONG: Argument too long. + * @QMI_PROTOCOL_ERROR_INVALID_TRANSACTION_ID: Invalid transaction ID. + * @QMI_PROTOCOL_ERROR_DEVICE_IN_USE: Device in use. + * @QMI_PROTOCOL_ERROR_NETWORK_UNSUPPORTED: Network unsupported. + * @QMI_PROTOCOL_ERROR_DEVICE_UNSUPPORTED Device unsupported. + * @QMI_PROTOCOL_ERROR_NO_EFFECT: No effect. + * @QMI_PROTOCOL_ERROR_NO_FREE_PROFILE: No free profile. + * @QMI_PROTOCOL_ERROR_INVALID_PDP_TYPE: Invalid PDP type. + * @QMI_PROTOCOL_ERROR_INVALID_TECHNOLOGY_PREFERENCE: Invalid technology preference. + * @QMI_PROTOCOL_ERROR_INVALID_PROFILE_TYPE: Invalid profile type. + * @QMI_PROTOCOL_ERROR_INVALID_SERVICE_TYPE: Invalid service type. + * @QMI_PROTOCOL_ERROR_INVALID_REGISTER_ACTION: Invalid register action. + * @QMI_PROTOCOL_ERROR_INVALID_PS_ATTACH_ACTION: Invalid PS attach action. + * @QMI_PROTOCOL_ERROR_AUTHENTICATION_FAILED: Authentication failed. + * @QMI_PROTOCOL_ERROR_PIN_BLOCKED: PIN blocked. + * @QMI_PROTOCOL_ERROR_PIN_ALWAYS_BLOCKED: PIN always blocked. + * @QMI_PROTOCOL_ERROR_UIM_UNINITIALIZED: UIM uninitialized. + * @QMI_PROTOCOL_ERROR_MAXIMUM_QOS_REQUESTS_IN_USE: Maximum QoS requests in use. + * @QMI_PROTOCOL_ERROR_INCORRECT_FLOW_FILTER: Incorrect flow filter. + * @QMI_PROTOCOL_ERROR_NETWORK_QOS_UNAWARE: Network QoS unaware. + * @QMI_PROTOCOL_ERROR_INVALID_QOS_ID: Invalid QoS ID. + * @QMI_PROTOCOL_ERROR_QOS_UNAVAILABLE: QoS unavailable. + * @QMI_PROTOCOL_ERROR_FLOW_SUSPENDED: Flow suspended. + * @QMI_PROTOCOL_ERROR_GENERAL_ERROR: General error. + * @QMI_PROTOCOL_ERROR_UNKNOWN_ERROR: Unknown error. + * @QMI_PROTOCOL_ERROR_INVALID_ARGUMENT: Invalid argument. + * @QMI_PROTOCOL_ERROR_INVALID_INDEX: Invalid index. + * @QMI_PROTOCOL_ERROR_NO_ENTRY: No entry. + * @QMI_PROTOCOL_ERROR_DEVICE_STORAGE_FULL: Device storage full. + * @QMI_PROTOCOL_ERROR_DEVICE_NOT_READY: Device not ready. + * @QMI_PROTOCOL_ERROR_NETWORK_NOT_READY: Network not ready. + * @QMI_PROTOCOL_ERROR_WMS_CAUSE_CODE: WMS cause code. + * @QMI_PROTOCOL_ERROR_WMS_MESSAGE_NOT_SENT: WMS message not sent. + * @QMI_PROTOCOL_ERROR_WMS_MESSAGE_DELIVERY_FAILURE: WMS message delivery failure. + * @QMI_PROTOCOL_ERROR_WMS_INVALID_MESSAGE_ID: WMS invalid message ID. + * @QMI_PROTOCOL_ERROR_WMS_ENCODING: WMS encoding. + * @QMI_PROTOCOL_ERROR_AUTHENTICATION_LOCK: Authentication lock. + * @QMI_PROTOCOL_ERROR_INVALID_TRANSITION: Invalid transition. + * @QMI_PROTOCOL_ERROR_SESSION_INACTIVE: Session inactive. + * @QMI_PROTOCOL_ERROR_SESSION_INVALID: Session invalid. + * @QMI_PROTOCOL_ERROR_SESSION_OWNERSHIP: Session ownership. + * @QMI_PROTOCOL_ERROR_INSUFFICIENT_RESOURCES: Insufficient resources. + * @QMI_PROTOCOL_ERROR_DISABLED: Disabled. + * @QMI_PROTOCOL_ERROR_INVALID_OPERATION: Invalid operation. + * @QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND: Invalid QMI command. + * @QMI_PROTOCOL_ERROR_WMS_TPDU_TYPE: WMS TPDU type. + * @QMI_PROTOCOL_ERROR_WMS_SMSC_ADDRESS: WMS SMSC address. + * @QMI_PROTOCOL_ERROR_INFORMATION_UNAVAILABLE: Information unavailable. + * @QMI_PROTOCOL_ERROR_SEGMENT_TOO_LONG: Segment too long. + * @QMI_PROTOCOL_ERROR_SEGMENT_ORDER: Segment order. + * @QMI_PROTOCOL_ERROR_BUNDLING_NOT_SUPPORTED: Bundling not supported. + * @QMI_PROTOCOL_ERROR_SIM_FILE_NOT_FOUND: SIM file not found. + * @QMI_PROTOCOL_ERROR_ACCESS_DENIED: Access denied. + * @QMI_PROTOCOL_ERROR_HARDWARE_RESTRICTED: Hardware restricted. + * @QMI_PROTOCOL_ERROR_CAT_EVENT_REGISTRATION_FAILED: Event registration failed. + * @QMI_PROTOCOL_ERROR_CAT_INVALID_TERMINAL_RESPONSE: Invalid terminal response. + * @QMI_PROTOCOL_ERROR_CAT_INVALID_ENVELOPE_COMMAND: Invalid envelope command. + * @QMI_PROTOCOL_ERROR_CAT_ENVELOPE_COMMAND_BUSY: Envelope command busy. + * @QMI_PROTOCOL_ERROR_CAT_ENVELOPE_COMMAND_FAILED: Envelope command failed. + * + * QMI protocol errors. + */ +typedef enum { + QMI_PROTOCOL_ERROR_NONE = 0, + QMI_PROTOCOL_ERROR_MALFORMED_MESSAGE = 1, + QMI_PROTOCOL_ERROR_NO_MEMORY = 2, + QMI_PROTOCOL_ERROR_INTERNAL = 3, + QMI_PROTOCOL_ERROR_ABORTED = 4, + QMI_PROTOCOL_ERROR_CLIENT_IDS_EXHAUSTED = 5, + QMI_PROTOCOL_ERROR_UNABORTABLE_TRANSACTION = 6, + QMI_PROTOCOL_ERROR_INVALID_CLIENT_ID = 7, + QMI_PROTOCOL_ERROR_NO_THRESHOLDS_PROVIDED = 8, + QMI_PROTOCOL_ERROR_INVALID_HANDLE = 9, + QMI_PROTOCOL_ERROR_INVALID_PROFILE = 10, + QMI_PROTOCOL_ERROR_INVALID_PIN_ID = 11, + QMI_PROTOCOL_ERROR_INCORRECT_PIN = 12, + QMI_PROTOCOL_ERROR_NO_NETWORK_FOUND = 13, + QMI_PROTOCOL_ERROR_CALL_FAILED = 14, + QMI_PROTOCOL_ERROR_OUT_OF_CALL = 15, + QMI_PROTOCOL_ERROR_NOT_PROVISIONED = 16, + QMI_PROTOCOL_ERROR_MISSING_ARGUMENT = 17, + QMI_PROTOCOL_ERROR_ARGUMENT_TOO_LONG = 19, + QMI_PROTOCOL_ERROR_INVALID_TRANSACTION_ID = 22, + QMI_PROTOCOL_ERROR_DEVICE_IN_USE = 23, + QMI_PROTOCOL_ERROR_NETWORK_UNSUPPORTED = 24, + QMI_PROTOCOL_ERROR_DEVICE_UNSUPPORTED = 25, + QMI_PROTOCOL_ERROR_NO_EFFECT = 26, + QMI_PROTOCOL_ERROR_NO_FREE_PROFILE = 27, + QMI_PROTOCOL_ERROR_INVALID_PDP_TYPE = 28, + QMI_PROTOCOL_ERROR_INVALID_TECHNOLOGY_PREFERENCE = 29, + QMI_PROTOCOL_ERROR_INVALID_PROFILE_TYPE = 30, + QMI_PROTOCOL_ERROR_INVALID_SERVICE_TYPE = 31, + QMI_PROTOCOL_ERROR_INVALID_REGISTER_ACTION = 32, + QMI_PROTOCOL_ERROR_INVALID_PS_ATTACH_ACTION = 33, + QMI_PROTOCOL_ERROR_AUTHENTICATION_FAILED = 34, + QMI_PROTOCOL_ERROR_PIN_BLOCKED = 35, + QMI_PROTOCOL_ERROR_PIN_ALWAYS_BLOCKED = 36, + QMI_PROTOCOL_ERROR_UIM_UNINITIALIZED = 37, + QMI_PROTOCOL_ERROR_MAXIMUM_QOS_REQUESTS_IN_USE = 38, + QMI_PROTOCOL_ERROR_INCORRECT_FLOW_FILTER = 39, + QMI_PROTOCOL_ERROR_NETWORK_QOS_UNAWARE = 40, + QMI_PROTOCOL_ERROR_INVALID_QOS_ID = 41, + QMI_PROTOCOL_ERROR_QOS_UNAVAILABLE = 42, + QMI_PROTOCOL_ERROR_FLOW_SUSPENDED = 43, + QMI_PROTOCOL_ERROR_GENERAL_ERROR = 46, + QMI_PROTOCOL_ERROR_UNKNOWN_ERROR = 47, + QMI_PROTOCOL_ERROR_INVALID_ARGUMENT = 48, + QMI_PROTOCOL_ERROR_INVALID_INDEX = 49, + QMI_PROTOCOL_ERROR_NO_ENTRY = 50, + QMI_PROTOCOL_ERROR_DEVICE_STORAGE_FULL = 51, + QMI_PROTOCOL_ERROR_DEVICE_NOT_READY = 52, + QMI_PROTOCOL_ERROR_NETWORK_NOT_READY = 53, + QMI_PROTOCOL_ERROR_WMS_CAUSE_CODE = 54, + QMI_PROTOCOL_ERROR_WMS_MESSAGE_NOT_SENT = 55, + QMI_PROTOCOL_ERROR_WMS_MESSAGE_DELIVERY_FAILURE = 56, + QMI_PROTOCOL_ERROR_WMS_INVALID_MESSAGE_ID = 57, + QMI_PROTOCOL_ERROR_WMS_ENCODING = 58, + QMI_PROTOCOL_ERROR_AUTHENTICATION_LOCK = 59, + QMI_PROTOCOL_ERROR_INVALID_TRANSITION = 60, + QMI_PROTOCOL_ERROR_SESSION_INACTIVE = 65, + QMI_PROTOCOL_ERROR_SESSION_INVALID = 66, + QMI_PROTOCOL_ERROR_SESSION_OWNERSHIP = 67, + QMI_PROTOCOL_ERROR_INSUFFICIENT_RESOURCES = 68, + QMI_PROTOCOL_ERROR_DISABLED = 69, + QMI_PROTOCOL_ERROR_INVALID_OPERATION = 70, + QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND = 71, + QMI_PROTOCOL_ERROR_WMS_T_PDU_TYPE = 72, + QMI_PROTOCOL_ERROR_WMS_SMSC_ADDRESS = 73, + QMI_PROTOCOL_ERROR_INFORMATION_UNAVAILABLE = 74, + QMI_PROTOCOL_ERROR_SEGMENT_TOO_LONG = 75, + QMI_PROTOCOL_ERROR_SEGMENT_ORDER = 76, + QMI_PROTOCOL_ERROR_BUNDLING_NOT_SUPPORTED = 77, + QMI_PROTOCOL_ERROR_SIM_FILE_NOT_FOUND = 80, + QMI_PROTOCOL_ERROR_ACCESS_DENIED = 82, + QMI_PROTOCOL_ERROR_HARDWARE_RESTRICTED = 83, + QMI_PROTOCOL_ERROR_CAT_EVENT_REGISTRATION_FAILED = 61441, + QMI_PROTOCOL_ERROR_CAT_INVALID_TERMINAL_RESPONSE = 61442, + QMI_PROTOCOL_ERROR_CAT_INVALID_ENVELOPE_COMMAND = 61443, + QMI_PROTOCOL_ERROR_CAT_ENVELOPE_COMMAND_BUSY = 61444, + QMI_PROTOCOL_ERROR_CAT_ENVELOPE_COMMAND_FAILED = 61445 +} QmiProtocolError; + +#endif /* _LIBQMI_GLIB_QMI_ERRORS_H_ */ diff --git a/libqmi-glib/qmi-message.c b/libqmi-glib/qmi-message.c new file mode 100644 index 0000000..8d3abee --- /dev/null +++ b/libqmi-glib/qmi-message.c @@ -0,0 +1,707 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file of the libqmi library. + */ + +/* + * 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 Aleksander Morgado + */ + +#include +#include +#include +#include +#include + +#include "qmi-message.h" +#include "qmi-utils.h" +#include "qmi-enum-types.h" +#include "qmi-error-types.h" + +#include "qmi-ctl.h" +#include "qmi-dms.h" +#include "qmi-wds.h" + +#define PACKED __attribute__((packed)) + +struct qmux { + guint16 length; + guint8 flags; + guint8 service; + guint8 client; +} PACKED; + +struct control_header { + guint8 flags; + guint8 transaction; + guint16 message; + guint16 tlv_length; +} PACKED; + +struct service_header { + guint8 flags; + guint16 transaction; + guint16 message; + guint16 tlv_length; +} PACKED; + +struct tlv { + guint8 type; + guint16 length; + guint8 value[]; +} PACKED; + +struct control_message { + struct control_header header; + struct tlv tlv[]; +} PACKED; + +struct service_message { + struct service_header header; + struct tlv tlv[]; +} PACKED; + +struct full_message { + guint8 marker; + struct qmux qmux; + union { + struct control_message control; + struct service_message service; + } qmi; +} PACKED; + +struct _QmiMessage { + /* TODO: avoid memory split here */ + struct full_message *buf; /* buf allocated using g_malloc, not g_slice_alloc */ + gsize len; /* cached size of *buf; not part of message. */ + volatile gint ref_count; /* the ref count */ +}; + +guint16 +qmi_message_get_qmux_length (QmiMessage *self) +{ + return GUINT16_FROM_LE (self->buf->qmux.length); +} + +static inline void +set_qmux_length (QmiMessage *self, + guint16 length) +{ + self->buf->qmux.length = GUINT16_TO_LE (length); +} + +gboolean +qmi_message_is_control (QmiMessage *self) +{ + g_return_val_if_fail (self != NULL, FALSE); + + return self->buf->qmux.service == QMI_SERVICE_CTL; +} + +guint8 +qmi_message_get_qmux_flags (QmiMessage *self) +{ + g_return_val_if_fail (self != NULL, 0); + + return self->buf->qmux.flags; +} + +QmiService +qmi_message_get_service (QmiMessage *self) +{ + g_return_val_if_fail (self != NULL, QMI_SERVICE_UNKNOWN); + + return (QmiService)self->buf->qmux.service; +} + +guint8 +qmi_message_get_client_id (QmiMessage *self) +{ + g_return_val_if_fail (self != NULL, 0); + + return self->buf->qmux.client; +} + +guint8 +qmi_message_get_qmi_flags (QmiMessage *self) +{ + g_return_val_if_fail (self != NULL, 0); + + if (qmi_message_is_control (self)) + return self->buf->qmi.control.header.flags; + + return self->buf->qmi.service.header.flags; +} + +gboolean +qmi_message_is_response (QmiMessage *self) +{ + if (qmi_message_is_control (self)) { + if (self->buf->qmi.control.header.flags & QMI_CTL_FLAG_RESPONSE) + return TRUE; + } else { + if (self->buf->qmi.service.header.flags & QMI_SERVICE_FLAG_RESPONSE) + return TRUE; + } + + return FALSE; +} + +gboolean +qmi_message_is_indication (QmiMessage *self) +{ + if (qmi_message_is_control (self)) { + if (self->buf->qmi.control.header.flags & QMI_CTL_FLAG_INDICATION) + return TRUE; + } else { + if (self->buf->qmi.service.header.flags & QMI_SERVICE_FLAG_INDICATION) + return TRUE; + } + + return FALSE; +} + +guint16 +qmi_message_get_transaction_id (QmiMessage *self) +{ + g_return_val_if_fail (self != NULL, 0); + + if (qmi_message_is_control (self)) + /* note: only 1 byte for transaction in CTL message */ + return (guint16)self->buf->qmi.control.header.transaction; + + return le16toh (self->buf->qmi.service.header.transaction); +} + +guint16 +qmi_message_get_message_id (QmiMessage *self) +{ + g_return_val_if_fail (self != NULL, 0); + + if (qmi_message_is_control (self)) + return le16toh (self->buf->qmi.control.header.message); + + return le16toh (self->buf->qmi.service.header.message); +} + +gsize +qmi_message_get_length (QmiMessage *self) +{ + g_return_val_if_fail (self != NULL, 0); + + return self->len; +} + +guint16 +qmi_message_get_tlv_length (QmiMessage *self) +{ + if (qmi_message_is_control (self)) + return GUINT16_FROM_LE (self->buf->qmi.control.header.tlv_length); + + return GUINT16_FROM_LE (self->buf->qmi.service.header.tlv_length); +} + +static void +set_qmi_message_get_tlv_length (QmiMessage *self, + guint16 length) +{ + if (qmi_message_is_control (self)) + self->buf->qmi.control.header.tlv_length = GUINT16_TO_LE (length); + else + self->buf->qmi.service.header.tlv_length = GUINT16_TO_LE (length); +} + +static struct tlv * +qmi_tlv (QmiMessage *self) +{ + if (qmi_message_is_control (self)) + return self->buf->qmi.control.tlv; + + return self->buf->qmi.service.tlv; +} + +static guint8 * +qmi_end (QmiMessage *self) +{ + return (guint8 *) self->buf + self->len; +} + +static struct tlv * +tlv_next (struct tlv *tlv) +{ + return (struct tlv *)((guint8 *)tlv + sizeof(struct tlv) + le16toh (tlv->length)); +} + +static struct tlv * +qmi_tlv_first (QmiMessage *self) +{ + if (qmi_message_get_tlv_length (self)) + return qmi_tlv (self); + + return NULL; +} + +static struct tlv * +qmi_tlv_next (QmiMessage *self, + struct tlv *tlv) +{ + struct tlv *end; + struct tlv *next; + + end = (struct tlv *) qmi_end (self); + next = tlv_next (tlv); + + return (next < end ? next : NULL); +} + +/** + * Checks the validity of a QMI message. + * + * In particular, checks: + * 1. The message has space for all required headers. + * 2. The length of the buffer, the qmux length field, and the QMI tlv_length + * field are all consistent. + * 3. The TLVs in the message fit exactly in the payload size. + * + * Returns non-zero if the message is valid, zero if invalid. + */ +gboolean +qmi_message_check (QmiMessage *self, + GError **error) +{ + gsize header_length; + guint8 *end; + struct tlv *tlv; + + g_assert (self != NULL); + g_assert (self->buf != NULL); + + if (self->buf->marker != QMI_MESSAGE_QMUX_MARKER) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_INVALID_MESSAGE, + "Marker is incorrect"); + return FALSE; + } + + if (qmi_message_get_qmux_length (self) < sizeof (struct qmux)) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_INVALID_MESSAGE, + "QMUX length too short for QMUX header (%u < %" G_GSIZE_FORMAT ")", + qmi_message_get_qmux_length (self), sizeof (struct qmux)); + return FALSE; + } + + /* + * qmux length is one byte shorter than buffer length because qmux + * length does not include the qmux frame marker. + */ + if (qmi_message_get_qmux_length (self) != self->len - 1) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_INVALID_MESSAGE, + "QMUX length and buffer length don't match (%u != %" G_GSIZE_FORMAT ")", + qmi_message_get_qmux_length (self), self->len - 1); + return FALSE; + } + + header_length = sizeof (struct qmux) + (qmi_message_is_control (self) ? + sizeof (struct control_header) : + sizeof (struct service_header)); + + if (qmi_message_get_qmux_length (self) < header_length) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_INVALID_MESSAGE, + "QMUX length too short for QMI header (%u < %" G_GSIZE_FORMAT ")", + qmi_message_get_qmux_length (self), header_length); + return FALSE; + } + + if (qmi_message_get_qmux_length (self) - header_length != qmi_message_get_tlv_length (self)) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_INVALID_MESSAGE, + "QMUX length and QMI TLV lengths don't match (%u - %" G_GSIZE_FORMAT " != %u)", + qmi_message_get_qmux_length (self), header_length, qmi_message_get_tlv_length (self)); + return FALSE; + } + + end = qmi_end (self); + for (tlv = qmi_tlv (self); tlv < (struct tlv *)end; tlv = tlv_next (tlv)) { + if (tlv->value > end) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_INVALID_MESSAGE, + "TLV header runs over buffer (%p > %p)", + tlv->value, end); + return FALSE; + } + if (tlv->value + le16toh (tlv->length) > end) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_INVALID_MESSAGE, + "TLV value runs over buffer (%p + %u > %p)", + tlv->value, le16toh (tlv->length), end); + return FALSE; + } + } + + /* + * If this assert triggers, one of the if statements in the loop is wrong. + * (It shouldn't be reached on malformed QMI messages.) + */ + g_assert (tlv == (struct tlv *)end); + + return TRUE; +} + +QmiMessage * +qmi_message_new (QmiService service, + guint8 client_id, + guint16 transaction_id, + guint16 message_id) +{ + QmiMessage *self; + + /* Transaction ID in the control service is 8bit only */ + g_assert (service != QMI_SERVICE_CTL || + transaction_id <= G_MAXUINT8); + + self = g_slice_new (QmiMessage); + self->ref_count = 1; + + self->len = 1 + sizeof (struct qmux) + (service == QMI_SERVICE_CTL ? + sizeof (struct control_header) : + sizeof (struct service_header)); + + /* TODO: Allocate both the message and the buffer together */ + self->buf = g_malloc (self->len); + + self->buf->marker = QMI_MESSAGE_QMUX_MARKER; + self->buf->qmux.flags = 0; + self->buf->qmux.service = service; + self->buf->qmux.client = client_id; + set_qmux_length (self, self->len - 1); + + if (service == QMI_SERVICE_CTL) { + self->buf->qmi.control.header.flags = 0; + self->buf->qmi.control.header.transaction = (guint8)transaction_id; + self->buf->qmi.control.header.message = htole16 (message_id); + } else { + self->buf->qmi.service.header.flags = 0; + self->buf->qmi.service.header.transaction = htole16 (transaction_id); + self->buf->qmi.service.header.message = htole16 (message_id); + } + + set_qmi_message_get_tlv_length (self, 0); + + g_assert (qmi_message_check (self, NULL)); + + return self; +} + +QmiMessage * +qmi_message_ref (QmiMessage *self) +{ + g_assert (self != NULL); + + g_atomic_int_inc (&self->ref_count); + return self; +} + +void +qmi_message_unref (QmiMessage *self) +{ + g_assert (self != NULL); + + if (g_atomic_int_dec_and_test (&self->ref_count)) { + g_free (self->buf); + g_slice_free (QmiMessage, self); + } +} + +gconstpointer +qmi_message_get_raw (QmiMessage *self, + gsize *len, + GError **error) +{ + g_assert (self != NULL); + g_assert (len != NULL); + + if (!qmi_message_check (self, error)) + return NULL; + + *len = self->len; + return self->buf; +} + +gboolean +qmi_message_tlv_get (QmiMessage *self, + guint8 type, + guint16 *length, + guint8 **value, + GError **error) +{ + struct tlv *tlv; + + g_assert (self != NULL); + g_assert (self->buf != NULL); + g_assert (length != NULL); + /* note: we allow querying only for the exact length */ + + for (tlv = qmi_tlv_first (self); tlv; tlv = qmi_tlv_next (self, tlv)) { + if (tlv->type == type) { + *length = GUINT16_FROM_LE (tlv->length); + if (value) + *value = &(tlv->value[0]); + return TRUE; + } + } + + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_TLV_TOO_LONG, + "TLV not found"); + return FALSE; +} + +void +qmi_message_tlv_foreach (QmiMessage *self, + QmiMessageForeachTlvFn callback, + gpointer user_data) +{ + struct tlv *tlv; + + g_assert (self != NULL); + g_assert (callback != NULL); + + for (tlv = qmi_tlv_first (self); tlv; tlv = qmi_tlv_next (self, tlv)) { + callback (tlv->type, + (gsize)(le16toh (tlv->length)), + (gconstpointer)tlv->value, + user_data); + } +} + +gboolean +qmi_message_tlv_add (QmiMessage *self, + guint8 type, + gsize length, + gconstpointer value, + GError **error) +{ + size_t tlv_len; + struct tlv *tlv; + + g_assert (self != NULL); + g_assert ((length == 0) || value != NULL); + + /* Make sure nothing's broken to start. */ + if (!qmi_message_check (self, error)) { + g_prefix_error (error, "Invalid QMI message detected: "); + return FALSE; + } + + /* Find length of new TLV. */ + tlv_len = sizeof (struct tlv) + length; + + /* Check for overflow of message size. */ + if (qmi_message_get_qmux_length (self) + tlv_len > UINT16_MAX) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_TLV_TOO_LONG, + "TLV to add is too long"); + return FALSE; + } + + /* Resize buffer. */ + self->len += tlv_len; + self->buf = g_realloc (self->buf, self->len); + + /* Fill in new TLV. */ + tlv = (struct tlv *)(qmi_end (self) - tlv_len); + tlv->type = type; + tlv->length = htole16 (length); + if (value) + memcpy (tlv->value, value, length); + + /* Update length fields. */ + set_qmux_length (self, (guint16)(qmi_message_get_qmux_length (self) + tlv_len)); + set_qmi_message_get_tlv_length (self, (guint16)(qmi_message_get_tlv_length(self) + tlv_len)); + + /* Make sure we didn't break anything. */ + if (!qmi_message_check (self, error)) { + g_prefix_error (error, "Invalid QMI message built: "); + return FALSE; + } + + return TRUE; +} + +QmiMessage * +qmi_message_new_from_raw (const guint8 *raw, + gsize raw_len) +{ + QmiMessage *self; + gsize message_len; + + /* If we didn't even read the header, leave */ + if (raw_len < (sizeof (struct qmux) + 1)) + return NULL; + + /* We need to have read the length reported by the header. + * Otherwise, return. */ + message_len = le16toh (((struct full_message *)raw)->qmux.length); + if (raw_len < (message_len - 1)) + return NULL; + + /* Ok, so we should have all the data available already */ + self = g_slice_new (QmiMessage); + self->ref_count = 1; + self->len = message_len + 1; + self->buf = g_malloc (self->len); + memcpy (self->buf, raw, self->len); + + /* NOTE: we don't check if the message is valid here, let the caller do it */ + + return self; +} + +gchar * +qmi_message_get_tlv_printable (QmiMessage *self, + const gchar *line_prefix, + guint8 type, + gsize length, + gconstpointer value) +{ + gchar *printable; + gchar *value_hex; + + value_hex = qmi_utils_str_hex (value, length, ':'); + printable = g_strdup_printf ("%sTLV:\n" + "%s type = 0x%02x\n" + "%s length = %" G_GSIZE_FORMAT "\n" + "%s value = %s\n", + line_prefix, + line_prefix, type, + line_prefix, length, + line_prefix, value_hex); + g_free (value_hex); + return printable; +} + +static gchar * +get_generic_printable (QmiMessage *self, + const gchar *line_prefix) +{ + GString *printable; + struct tlv *tlv; + + printable = g_string_new (""); + + g_string_append_printf (printable, + "%s message = (0x%04x)\n", + line_prefix, qmi_message_get_message_id (self)); + + for (tlv = qmi_tlv_first (self); tlv; tlv = qmi_tlv_next (self, tlv)) { + gchar *printable_tlv; + + printable_tlv = qmi_message_get_tlv_printable (self, + line_prefix, + tlv->type, + tlv->length, + tlv->value); + g_string_append (printable, printable_tlv); + g_free (printable_tlv); + } + + return g_string_free (printable, FALSE); +} + +gchar * +qmi_message_get_printable (QmiMessage *self, + const gchar *line_prefix) +{ + GString *printable; + gchar *qmi_flags_str; + gchar *contents; + + if (!qmi_message_check (self, NULL)) + return NULL; + + if (!line_prefix) + line_prefix = ""; + + printable = g_string_new (""); + g_string_append_printf (printable, + "%sQMUX:\n" + "%s length = %u\n" + "%s flags = 0x%02x\n" + "%s service = \"%s\"\n" + "%s client = %u\n", + line_prefix, + line_prefix, qmi_message_get_qmux_length (self), + line_prefix, qmi_message_get_qmux_flags (self), + line_prefix, qmi_service_get_string (qmi_message_get_service (self)), + line_prefix, qmi_message_get_client_id (self)); + + if (qmi_message_get_service (self) == QMI_SERVICE_CTL) + qmi_flags_str = qmi_ctl_flag_build_string_from_mask (qmi_message_get_qmi_flags (self)); + else + qmi_flags_str = qmi_service_flag_build_string_from_mask (qmi_message_get_qmi_flags (self)); + + g_string_append_printf (printable, + "%sQMI:\n" + "%s flags = \"%s\"\n" + "%s transaction = %u\n" + "%s tlv_length = %u\n", + line_prefix, + line_prefix, qmi_flags_str, + line_prefix, qmi_message_get_transaction_id (self), + line_prefix, qmi_message_get_tlv_length (self)); + g_free (qmi_flags_str); + + contents = NULL; + switch (qmi_message_get_service (self)) { + case QMI_SERVICE_CTL: + contents = qmi_message_ctl_get_printable (self, line_prefix); + break; + case QMI_SERVICE_DMS: + contents = qmi_message_dms_get_printable (self, line_prefix); + break; + case QMI_SERVICE_WDS: + contents = qmi_message_wds_get_printable (self, line_prefix); + break; + default: + break; + } + + if (!contents) + contents = get_generic_printable (self, line_prefix); + g_string_append (printable, contents); + g_free (contents); + + return g_string_free (printable, FALSE); +} diff --git a/libqmi-glib/qmi-message.h b/libqmi-glib/qmi-message.h new file mode 100644 index 0000000..ff64a91 --- /dev/null +++ b/libqmi-glib/qmi-message.h @@ -0,0 +1,104 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* + * 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 Aleksander Morgado + */ + +#ifndef _LIBQMI_GLIB_QMI_MESSAGE_H_ +#define _LIBQMI_GLIB_QMI_MESSAGE_H_ + +#include + +#include "qmi-enums.h" + +G_BEGIN_DECLS + +#define QMI_MESSAGE_QMUX_MARKER (guint8)0x01 +typedef struct _QmiMessage QmiMessage; + +QmiMessage *qmi_message_new (QmiService service, + guint8 client_id, + guint16 transaction_id, + guint16 message_id); +QmiMessage *qmi_message_new_from_raw (const guint8 *raw, + gsize raw_len); +QmiMessage *qmi_message_ref (QmiMessage *self); +void qmi_message_unref (QmiMessage *self); + +typedef void (* QmiMessageForeachTlvFn) (guint8 type, + gsize length, + gconstpointer value, + gpointer user_data); +void qmi_message_tlv_foreach (QmiMessage *self, + QmiMessageForeachTlvFn callback, + gpointer user_data); + +gboolean qmi_message_tlv_get (QmiMessage *self, + guint8 type, + guint16 *length, + guint8 **value, + GError **error); + +gboolean qmi_message_tlv_add (QmiMessage *self, + guint8 type, + gsize length, + gconstpointer value, + GError **error); + +gconstpointer qmi_message_get_raw (QmiMessage *self, + gsize *length, + GError **error); + +guint16 qmi_message_get_qmux_length (QmiMessage *self); +guint16 qmi_message_get_tlv_length (QmiMessage *self); +gsize qmi_message_get_length (QmiMessage *self); + +gchar *qmi_message_get_printable (QmiMessage *self, + const gchar *line_prefix); + +gchar *qmi_message_get_tlv_printable (QmiMessage *self, + const gchar *line_prefix, + guint8 type, + gsize length, + gconstpointer value); + +gboolean qmi_message_check (QmiMessage *self, + GError **error); + +gboolean qmi_message_is_control (QmiMessage *self); +gboolean qmi_message_is_response (QmiMessage *self); +gboolean qmi_message_is_indication (QmiMessage *self); + +guint16 qmi_message_get_message_id (QmiMessage *self); +QmiService qmi_message_get_service (QmiMessage *self); +guint8 qmi_message_get_client_id (QmiMessage *self); +guint16 qmi_message_get_transaction_id (QmiMessage *self); +guint8 qmi_message_get_qmux_flags (QmiMessage *self); +guint8 qmi_message_get_qmi_flags (QmiMessage *self); + +G_END_DECLS + +#endif /* _LIBQMI_GLIB_QMI_MESSAGE_H_ */ diff --git a/libqmi-glib/qmi-utils.c b/libqmi-glib/qmi-utils.c new file mode 100644 index 0000000..d1e50f0 --- /dev/null +++ b/libqmi-glib/qmi-utils.c @@ -0,0 +1,312 @@ +/* -*- 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 Aleksander Morgado + */ + +#include +#include +#include + +#include "qmi-utils.h" + +gchar * +qmi_utils_str_hex (gconstpointer mem, + gsize size, + gchar delimiter) +{ + const guint8 *data = mem; + gsize i; + gsize j; + gsize new_str_length; + gchar *new_str; + + /* Get new string length. If input string has N bytes, we need: + * - 1 byte for last NUL char + * - 2N bytes for hexadecimal char representation of each byte... + * - N-1 bytes for the separator ':' + * So... a total of (1+2N+N-1) = 3N bytes are needed... */ + new_str_length = 3 * size; + + /* Allocate memory for new array and initialize contents to NUL */ + new_str = g_malloc0 (new_str_length); + + /* Print hexadecimal representation of each byte... */ + for (i = 0, j = 0; i < size; i++, j += 3) { + /* Print character in output string... */ + snprintf (&new_str[j], 3, "%02X", data[i]); + /* And if needed, add separator */ + if (i != (size - 1) ) + new_str[j + 2] = delimiter; + } + + /* Set output string */ + return new_str; +} + +void +qmi_utils_read_guint8_from_buffer (guint8 **buffer, + guint16 *buffer_size, + guint8 *out) +{ + g_assert (out != NULL); + g_assert (buffer != NULL); + g_assert (buffer_size != NULL); + g_assert (*buffer_size >= 1); + + *out = (*buffer)[0]; + + *buffer = &((*buffer)[1]); + *buffer_size = (*buffer_size) - 1; +} + +void +qmi_utils_read_gint8_from_buffer (guint8 **buffer, + guint16 *buffer_size, + gint8 *out) +{ + g_assert (out != NULL); + g_assert (buffer != NULL); + g_assert (buffer_size != NULL); + g_assert (*buffer_size >= 1); + + *out = *((gint8 *)(&((*buffer)[0]))); + + *buffer = &((*buffer)[1]); + *buffer_size = (*buffer_size) - 1; +} + +void +qmi_utils_read_guint16_from_buffer (guint8 **buffer, + guint16 *buffer_size, + guint16 *out) +{ + g_assert (out != NULL); + g_assert (buffer != NULL); + g_assert (buffer_size != NULL); + g_assert (*buffer_size >= 2); + + *out = GUINT16_FROM_LE (*((guint16 *)&((*buffer)[0]))); + + *buffer = &((*buffer)[2]); + *buffer_size = (*buffer_size) - 2; +} + +void +qmi_utils_read_gint16_from_buffer (guint8 **buffer, + guint16 *buffer_size, + gint16 *out) +{ + g_assert (out != NULL); + g_assert (buffer != NULL); + g_assert (buffer_size != NULL); + g_assert (*buffer_size >= 2); + + *out = GINT16_FROM_LE (*((guint16 *)&((*buffer)[0]))); + + *buffer = &((*buffer)[2]); + *buffer_size = (*buffer_size) - 2; +} + +void +qmi_utils_read_guint32_from_buffer (guint8 **buffer, + guint16 *buffer_size, + guint32 *out) +{ + g_assert (out != NULL); + g_assert (buffer != NULL); + g_assert (buffer_size != NULL); + g_assert (*buffer_size >= 4); + + *out = GUINT32_FROM_LE (*((guint32 *)&((*buffer)[0]))); + + *buffer = &((*buffer)[4]); + *buffer_size = (*buffer_size) - 4; +} + +void +qmi_utils_read_gint32_from_buffer (guint8 **buffer, + guint16 *buffer_size, + gint32 *out) +{ + g_assert (out != NULL); + g_assert (buffer != NULL); + g_assert (buffer_size != NULL); + g_assert (*buffer_size >= 4); + + *out = GUINT32_FROM_LE (*((guint32 *)&((*buffer)[0]))); + + *buffer = &((*buffer)[4]); + *buffer_size = (*buffer_size) - 4; +} + +void +qmi_utils_write_guint8_to_buffer (guint8 **buffer, + guint16 *buffer_size, + guint8 *in) +{ + g_assert (in != NULL); + g_assert (buffer != NULL); + g_assert (buffer_size != NULL); + g_assert (*buffer_size >= 1); + + (*buffer)[0] = *in; + + *buffer = &((*buffer)[1]); + *buffer_size = (*buffer_size) - 1; +} + +void +qmi_utils_write_gint8_to_buffer (guint8 **buffer, + guint16 *buffer_size, + gint8 *in) +{ + g_assert (in != NULL); + g_assert (buffer != NULL); + g_assert (buffer_size != NULL); + g_assert (*buffer_size >= 1); + + *((gint8 *)(&((*buffer)[0]))) = *in; + + *buffer = &((*buffer)[1]); + *buffer_size = (*buffer_size) - 1; +} + +void +qmi_utils_write_guint16_to_buffer (guint8 **buffer, + guint16 *buffer_size, + guint16 *in) +{ + g_assert (in != NULL); + g_assert (buffer != NULL); + g_assert (buffer_size != NULL); + g_assert (*buffer_size >= 2); + + *((guint16 *)(&((*buffer)[0]))) = GUINT16_TO_LE (*in); + + *buffer = &((*buffer)[2]); + *buffer_size = (*buffer_size) - 2; +} + +void +qmi_utils_write_gint16_to_buffer (guint8 **buffer, + guint16 *buffer_size, + gint16 *in) +{ + g_assert (in != NULL); + g_assert (buffer != NULL); + g_assert (buffer_size != NULL); + g_assert (*buffer_size >= 2); + + *((gint16 *)(&((*buffer)[0]))) = GINT16_TO_LE (*in); + + *buffer = &((*buffer)[2]); + *buffer_size = (*buffer_size) - 2; +} + +void +qmi_utils_write_guint32_to_buffer (guint8 **buffer, + guint16 *buffer_size, + guint32 *in) +{ + g_assert (in != NULL); + g_assert (buffer != NULL); + g_assert (buffer_size != NULL); + g_assert (*buffer_size >= 4); + + *((guint32 *)(&((*buffer)[0]))) = GUINT32_TO_LE (*in); + + *buffer = &((*buffer)[4]); + *buffer_size = (*buffer_size) - 4; +} + +void +qmi_utils_write_gint32_to_buffer (guint8 **buffer, + guint16 *buffer_size, + gint32 *in) +{ + g_assert (in != NULL); + g_assert (buffer != NULL); + g_assert (buffer_size != NULL); + g_assert (*buffer_size >= 4); + + *((gint32 *)(&((*buffer)[0]))) = GINT32_TO_LE (*in); + + *buffer = &((*buffer)[4]); + *buffer_size = (*buffer_size) - 4; +} + +void +qmi_utils_read_string_from_buffer (guint8 **buffer, + guint16 *buffer_size, + gboolean length_prefix, + gchar **out) +{ + guint16 string_length; + + g_assert (out != NULL); + g_assert (buffer != NULL); + g_assert (buffer_size != NULL); + + /* If no length prefix given, read the whole buffer into a string */ + if (!length_prefix) + string_length = *buffer_size; + else { + /* We assume the length prefix is always a guint8 */ + guint8 string_length_8; + + qmi_utils_read_guint8_from_buffer (buffer, buffer_size, &string_length_8); + string_length = string_length_8; + } + + *out = g_malloc (string_length + 1); + (*out)[string_length] = '\0'; + memcpy (*out, *buffer, string_length); + + *buffer = &((*buffer)[string_length]); + *buffer_size = (*buffer_size) - string_length; +} + +void +qmi_utils_write_string_to_buffer (guint8 **buffer, + guint16 *buffer_size, + gboolean length_prefix, + gchar **in) +{ + guint16 len; + + g_assert (in != NULL); + g_assert (buffer != NULL); + g_assert (buffer_size != NULL); + + len = (guint16) strlen (*in); + + if (length_prefix) { + guint8 len_8; + + g_warn_if_fail (len <= G_MAXUINT8); + len_8 = (guint8)len; + qmi_utils_write_guint8_to_buffer (buffer, buffer_size, &len_8); + } + + memcpy (*buffer, *in, len); + *buffer = &((*buffer)[len]); + *buffer_size = (*buffer_size) - len; +} diff --git a/libqmi-glib/qmi-utils.h b/libqmi-glib/qmi-utils.h new file mode 100644 index 0000000..7c098a0 --- /dev/null +++ b/libqmi-glib/qmi-utils.h @@ -0,0 +1,95 @@ +/* -*- 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 Aleksander Morgado + */ + +/* NOTE: this is a private non-installable header */ + +#ifndef _LIBQMI_GLIB_QMI_UTILS_H_ +#define _LIBQMI_GLIB_QMI_UTILS_H_ + +#include + +G_BEGIN_DECLS + +gchar *qmi_utils_str_hex (gconstpointer mem, + gsize size, + gchar delimiter); + +/* Reading/Writing integer variables */ + +void qmi_utils_read_guint8_from_buffer (guint8 **buffer, + guint16 *buffer_size, + guint8 *out); +void qmi_utils_read_gint8_from_buffer (guint8 **buffer, + guint16 *buffer_size, + gint8 *out); + +void qmi_utils_read_guint16_from_buffer (guint8 **buffer, + guint16 *buffer_size, + guint16 *out); +void qmi_utils_read_gint16_from_buffer (guint8 **buffer, + guint16 *buffer_size, + gint16 *out); + +void qmi_utils_read_guint32_from_buffer (guint8 **buffer, + guint16 *buffer_size, + guint32 *out); +void qmi_utils_read_gint32_from_buffer (guint8 **buffer, + guint16 *buffer_size, + gint32 *out); + +void qmi_utils_write_guint8_to_buffer (guint8 **buffer, + guint16 *buffer_size, + guint8 *in); +void qmi_utils_write_gint8_to_buffer (guint8 **buffer, + guint16 *buffer_size, + gint8 *in); + +void qmi_utils_write_guint16_to_buffer (guint8 **buffer, + guint16 *buffer_size, + guint16 *in); +void qmi_utils_write_gint16_to_buffer (guint8 **buffer, + guint16 *buffer_size, + gint16 *in); + +void qmi_utils_write_guint32_to_buffer (guint8 **buffer, + guint16 *buffer_size, + guint32 *in); +void qmi_utils_write_gint32_to_buffer (guint8 **buffer, + guint16 *buffer_size, + gint32 *in); + +/* Reading/Writing string variables */ + +void qmi_utils_read_string_from_buffer (guint8 **buffer, + guint16 *buffer_size, + gboolean length_prefix, + gchar **out); + +void qmi_utils_write_string_to_buffer (guint8 **buffer, + guint16 *buffer_size, + gboolean length_prefix, + gchar **in); + +G_END_DECLS + +#endif /* _LIBQMI_GLIB_QMI_UTILS_H_ */ diff --git a/m4/compiler-warnings.m4 b/m4/compiler-warnings.m4 index 5cc3a38..511b898 100644 --- a/m4/compiler-warnings.m4 +++ b/m4/compiler-warnings.m4 @@ -1,4 +1,4 @@ -AC_DEFUN([LIBQMI_GLIB_COMPILER_WARNINGS], +AC_DEFUN([LIBQMI_COMPILER_WARNINGS], [AC_ARG_ENABLE(more-warnings, AS_HELP_STRING([--enable-more-warnings], [Maximum compiler warnings]), set_more_warnings="$enableval",set_more_warnings=yes) diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index e5b1156..0000000 --- a/src/Makefile.am +++ /dev/null @@ -1,118 +0,0 @@ - -lib_LTLIBRARIES = libqmi-glib.la - -libqmi_glib_la_CPPFLAGS = \ - $(LIBQMI_GLIB_CFLAGS) \ - -I$(top_srcdir) \ - -I$(top_builddir) \ - -I$(top_srcdir)/src \ - -I$(top_builddir)/src - -# Error types -qmi-error-types.h: qmi-errors.h $(top_srcdir)/build-aux/templates/qmi-error-types-template.h - $(AM_V_GEN) $(GLIB_MKENUMS) \ - --fhead "#ifndef __LIBQMI_GLIB_ERROR_TYPES_H__\n#define __LIBQMI_GLIB_ERROR_TYPES_H__\n#include \"qmi-errors.h\"\n" \ - --template $(top_srcdir)/build-aux/templates/qmi-error-types-template.h \ - --ftail "#endif /* __LIBQMI_GLIB_ERROR_TYPES_H__ */\n" \ - qmi-errors.h > $@ - -qmi-error-types.c: qmi-errors.h qmi-error-types.h $(top_srcdir)/build-aux/templates/qmi-error-types-template.c - $(AM_V_GEN) $(GLIB_MKENUMS) \ - --fhead "#include \"qmi-errors.h\"\n#include \"qmi-error-types.h\"\n" \ - --template $(top_srcdir)/build-aux/templates/qmi-error-types-template.c \ - qmi-errors.h > $@ - -# Enum types -ENUMS = qmi-enums.h qmi-enums-wds.h qmi-enums-dms.h qmi-dms.h qmi-wds.h qmi-ctl.h -qmi-enum-types.h: $(ENUMS) $(top_srcdir)/build-aux/templates/qmi-enum-types-template.h qmi-ctl.stamp qmi-dms.stamp qmi-wds.stamp - $(AM_V_GEN) $(GLIB_MKENUMS) \ - --fhead "#ifndef __LIBQMI_GLIB_ENUM_TYPES_H__\n#define __LIBQMI_GLIB_ENUM_TYPES_H__\n#include \"qmi-enums.h\"\n#include \"qmi-enums-wds.h\"\n#include \"qmi-enums-dms.h\"\n#include \"qmi-ctl.h\"\n#include \"qmi-dms.h\"\n#include \"qmi-wds.h\"\n" \ - --template $(top_srcdir)/build-aux/templates/qmi-enum-types-template.h \ - --ftail "#endif /* __LIBQMI_GLIB_ENUM_TYPES_H__ */\n" \ - $(ENUMS) > $@ - -qmi-enum-types.c: $(ENUMS) qmi-enum-types.h $(top_srcdir)/build-aux/templates/qmi-enum-types-template.c - $(AM_V_GEN) $(GLIB_MKENUMS) \ - --fhead "#include \"qmi-enum-types.h\"\n" \ - --template $(top_srcdir)/build-aux/templates/qmi-enum-types-template.c \ - $(ENUMS) > $@ - -# CTL service -qmi-ctl.stamp: $(top_srcdir)/data/qmi-service-ctl.json $(top_srcdir)/build-aux/qmi-codegen/*.py $(top_srcdir)/build-aux/qmi-codegen/qmi-codegen - $(AM_V_GEN) \ - rm -f qmi-ctl.h && \ - rm -f qmi-ctl.c && \ - $(top_srcdir)/build-aux/qmi-codegen/qmi-codegen \ - --input $(top_srcdir)/data/qmi-service-ctl.json \ - --include $(top_srcdir)/data/qmi-common.json \ - --output qmi-ctl && \ - touch $@ - -# DMS service -qmi-dms.stamp: $(top_srcdir)/data/qmi-service-dms.json $(top_srcdir)/build-aux/qmi-codegen/*.py $(top_srcdir)/build-aux/qmi-codegen/qmi-codegen - $(AM_V_GEN) \ - rm -f qmi-dms.h && \ - rm -f qmi-dms.c && \ - $(top_srcdir)/build-aux/qmi-codegen/qmi-codegen \ - --input $(top_srcdir)/data/qmi-service-dms.json \ - --include $(top_srcdir)/data/qmi-common.json \ - --output qmi-dms && \ - touch $@ - -# WDS service -qmi-wds.stamp: $(top_srcdir)/data/qmi-service-wds.json $(top_srcdir)/build-aux/qmi-codegen/*.py $(top_srcdir)/build-aux/qmi-codegen/qmi-codegen - $(AM_V_GEN) \ - rm -f qmi-wds.h && \ - rm -f qmi-wds.c && \ - $(top_srcdir)/build-aux/qmi-codegen/qmi-codegen \ - --input $(top_srcdir)/data/qmi-service-wds.json \ - --include $(top_srcdir)/data/qmi-common.json \ - --output qmi-wds && \ - touch $@ - -# Additional dependencies -qmi-device.c: qmi-error-types.h qmi-enum-types.h -qmi-client.c: qmi-error-types.h qmi-enum-types.h -qmi-message.c: qmi-error-types.h qmi-enum-types.h qmi-ctl.h qmi-dms.h qmi-wds.h -qmi-ctl.h: qmi-ctl.stamp -qmi-ctl.c: qmi-error-types.h qmi-enum-types.h qmi-ctl.stamp -qmi-dms.h: qmi-dms.stamp -qmi-dms.c: qmi-error-types.h qmi-enum-types.h qmi-dms.stamp -qmi-wds.h: qmi-wds.stamp -qmi-wds.c: qmi-error-types.h qmi-enum-types.h qmi-wds.stamp - -libqmi_glib_la_SOURCES = \ - libqmi-glib.h \ - qmi-errors.h qmi-error-types.h qmi-error-types.c \ - qmi-enums-wds.h \ - qmi-enums-dms.h \ - qmi-enums.h qmi-enum-types.h qmi-enum-types.c \ - qmi-utils.h qmi-utils.c \ - qmi-message.h qmi-message.c \ - qmi-device.h qmi-device.c \ - qmi-client.h qmi-client.c - -nodist_libqmi_glib_la_SOURCES = \ - qmi-ctl.c qmi-ctl.h \ - qmi-dms.c qmi-dms.h \ - qmi-wds.c qmi-wds.h - -libqmi_glib_la_LIBADD = \ - $(LIBQMI_GLIB_LIBS) - -includedir = @includedir@/libqmi-glib -include_HEADERS = \ - libqmi-glib.h \ - qmi-errors.h qmi-error-types.h \ - qmi-enums.h qmi-enum-types.h \ - qmi-message.h \ - qmi-device.h \ - qmi-client.h \ - qmi-ctl.h \ - qmi-enums-dms.h qmi-dms.h \ - qmi-enums-wds.h qmi-wds.h - -CLEANFILES = \ - qmi-ctl.h qmi-ctl.c qmi-ctl.stamp \ - qmi-dms.h qmi-dms.c qmi-dms.stamp \ - qmi-wds.h qmi-wds.c qmi-wds.stamp diff --git a/src/libqmi-glib.h b/src/libqmi-glib.h deleted file mode 100644 index c6d6a65..0000000 --- a/src/libqmi-glib.h +++ /dev/null @@ -1,35 +0,0 @@ -/* -*- 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 Aleksander Morgado - */ - -#ifndef _LIBQMI_GLIB_H_ -#define _LIBQMI_GLIB_H_ - -#include "qmi-errors.h" -#include "qmi-error-types.h" -#include "qmi-enum-types.h" - -#include "qmi-device.h" -#include "qmi-client.h" -#include "qmi-dms.h" -#include "qmi-wds.h" - -#endif /* _LIBQMI_GLIB_H_ */ diff --git a/src/qmi-client.c b/src/qmi-client.c deleted file mode 100644 index f3e6bab..0000000 --- a/src/qmi-client.c +++ /dev/null @@ -1,265 +0,0 @@ -/* -*- 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 Aleksander Morgado - */ - -#include - -#include "qmi-error-types.h" -#include "qmi-enum-types.h" -#include "qmi-device.h" -#include "qmi-client.h" -#include "qmi-ctl.h" - -G_DEFINE_ABSTRACT_TYPE (QmiClient, qmi_client, G_TYPE_OBJECT); - -enum { - PROP_0, - PROP_DEVICE, - PROP_SERVICE, - PROP_CID, - PROP_LAST -}; - -static GParamSpec *properties[PROP_LAST]; - -struct _QmiClientPrivate { - QmiDevice *device; - QmiService service; - guint8 cid; - - guint16 transaction_id; -}; - -/*****************************************************************************/ - -/** - * qmi_client_get_device: - * @self: a #QmiClient - * - * Get the #QmiDevice associated with this #QmiClient. - * - * Returns: a #GObject that must be freed with g_object_unref(). - */ -GObject * -qmi_client_get_device (QmiClient *self) -{ - GObject *device; - - g_return_val_if_fail (QMI_IS_CLIENT (self), NULL); - - g_object_get (G_OBJECT (self), - QMI_CLIENT_DEVICE, &device, - NULL); - - return device; -} - -/** - * qmi_client_peek_device: - * @self: a #QmiClient. - * - * Get the #QmiDevice associated with this #QmiClient, without increasing the reference count - * on the returned object. - * - * Returns: a #GObject. Do not free the returned object, it is owned by @self. - */ -GObject * -qmi_client_peek_device (QmiClient *self) -{ - g_return_val_if_fail (QMI_IS_CLIENT (self), NULL); - - return G_OBJECT (self->priv->device); -} - -/** - * qmi_client_get_service: - * @self: A #QmiClient - * - * Get the service being used by this #QmiClient. - * - * Returns: a #QmiService. - */ -QmiService -qmi_client_get_service (QmiClient *self) -{ - g_return_val_if_fail (QMI_IS_CLIENT (self), QMI_SERVICE_UNKNOWN); - - return self->priv->service; -} - -/** - * qmi_client_get_cid: - * @self: A #QmiClient - * - * Get the client ID of this #QmiClient. - * - * Returns: the client ID. - */ -guint8 -qmi_client_get_cid (QmiClient *self) -{ - g_return_val_if_fail (QMI_IS_CLIENT (self), QMI_CID_NONE); - - return self->priv->cid; -} - -/** - * qmi_client_get_next_transaction_id: - * @self: A #QmiClient - * - * Acquire the next transaction ID of this #QmiClient. - * The internal transaction ID gets incremented. - * - * Returns: the next transaction ID. - */ -guint16 -qmi_client_get_next_transaction_id (QmiClient *self) -{ - guint16 next; - - g_return_val_if_fail (QMI_IS_CLIENT (self), 0); - - next = self->priv->transaction_id; - - /* Don't go further than 8bits in the CTL service */ - if ((self->priv->service == QMI_SERVICE_CTL && - self->priv->transaction_id == G_MAXUINT8) || - self->priv->transaction_id == G_MAXUINT16) - /* Reset! */ - self->priv->transaction_id = 0x01; - else - self->priv->transaction_id++; - - return next; -} - -/*****************************************************************************/ - -void -qmi_client_process_indication (QmiClient *self, - QmiMessage *message) -{ - if (QMI_CLIENT_GET_CLASS (self)->process_indication) - QMI_CLIENT_GET_CLASS (self)->process_indication (self, message); -} - -/*****************************************************************************/ - -static void -set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - QmiClient *self = QMI_CLIENT (object); - - switch (prop_id) { - case PROP_DEVICE: - /* NOTE!! We do NOT keep a reference to the device here. - * Clients are OWNED by the device */ - self->priv->device = g_value_get_object (value); - break; - case PROP_SERVICE: - self->priv->service = g_value_get_enum (value); - break; - case PROP_CID: - self->priv->cid = (guint8)g_value_get_uint (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) -{ - QmiClient *self = QMI_CLIENT (object); - - switch (prop_id) { - case PROP_DEVICE: - g_value_set_object (value, self->priv->device); - break; - case PROP_SERVICE: - g_value_set_enum (value, self->priv->service); - break; - case PROP_CID: - g_value_set_uint (value, (guint)self->priv->cid); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -qmi_client_init (QmiClient *self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), - QMI_TYPE_CLIENT, - QmiClientPrivate); - - /* Defaults */ - self->priv->service = QMI_SERVICE_UNKNOWN; - self->priv->transaction_id = 0x01; - self->priv->cid = QMI_CID_NONE; -} - -static void -qmi_client_class_init (QmiClientClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (object_class, sizeof (QmiClientPrivate)); - - object_class->get_property = get_property; - object_class->set_property = set_property; - - properties[PROP_DEVICE] = - g_param_spec_object (QMI_CLIENT_DEVICE, - "Device", - "The QMI device", - QMI_TYPE_DEVICE, - G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_DEVICE, properties[PROP_DEVICE]); - - properties[PROP_SERVICE] = - g_param_spec_enum (QMI_CLIENT_SERVICE, - "Service", - "QMI service this client is using", - QMI_TYPE_SERVICE, - QMI_SERVICE_UNKNOWN, - G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_SERVICE, properties[PROP_SERVICE]); - - properties[PROP_CID] = - g_param_spec_uint (QMI_CLIENT_CID, - "Client ID", - "ID of the client registered into the QMI device", - 0, - G_MAXUINT8, - QMI_CID_NONE, - G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_CID, properties[PROP_CID]); -} diff --git a/src/qmi-client.h b/src/qmi-client.h deleted file mode 100644 index 828211e..0000000 --- a/src/qmi-client.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -*- 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 Aleksander Morgado - */ - -#ifndef _LIBQMI_GLIB_QMI_CLIENT_H_ -#define _LIBQMI_GLIB_QMI_CLIENT_H_ - -#include - -#include "qmi-enums.h" -#include "qmi-message.h" - -G_BEGIN_DECLS - -#define QMI_CID_NONE 0x00 -#define QMI_CID_BROADCAST 0xFF - -#define QMI_TYPE_CLIENT (qmi_client_get_type ()) -#define QMI_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), QMI_TYPE_CLIENT, QmiClient)) -#define QMI_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), QMI_TYPE_CLIENT, QmiClientClass)) -#define QMI_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), QMI_TYPE_CLIENT)) -#define QMI_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), QMI_TYPE_CLIENT)) -#define QMI_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), QMI_TYPE_CLIENT, QmiClientClass)) - -typedef struct _QmiClient QmiClient; -typedef struct _QmiClientClass QmiClientClass; -typedef struct _QmiClientPrivate QmiClientPrivate; - -#define QMI_CLIENT_DEVICE "client-device" -#define QMI_CLIENT_SERVICE "client-service" -#define QMI_CLIENT_CID "client-cid" - -struct _QmiClient { - GObject parent; - QmiClientPrivate *priv; -}; - -struct _QmiClientClass { - GObjectClass parent; - - /* Virtual method to get indications processed */ - void (* process_indication) (QmiClient *self, - QmiMessage *message); -}; - -GType qmi_client_get_type (void); - -GObject *qmi_client_get_device (QmiClient *self); -GObject *qmi_client_peek_device (QmiClient *self); -QmiService qmi_client_get_service (QmiClient *self); -guint8 qmi_client_get_cid (QmiClient *self); - -guint16 qmi_client_get_next_transaction_id (QmiClient *self); - -/* not part of the public API */ -void qmi_client_process_indication (QmiClient *self, - QmiMessage *message); - -G_END_DECLS - -#endif /* _LIBQMI_GLIB_QMI_CLIENT_H_ */ diff --git a/src/qmi-device.c b/src/qmi-device.c deleted file mode 100644 index 170795d..0000000 --- a/src/qmi-device.c +++ /dev/null @@ -1,1812 +0,0 @@ -/* -*- 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 Aleksander Morgado - */ - -#include -#include -#include -#include -#include -#include - -#include "qmi-device.h" -#include "qmi-message.h" -#include "qmi-ctl.h" -#include "qmi-dms.h" -#include "qmi-wds.h" -#include "qmi-utils.h" -#include "qmi-error-types.h" -#include "qmi-enum-types.h" - -static void async_initable_iface_init (GAsyncInitableIface *iface); - -G_DEFINE_TYPE_EXTENDED (QmiDevice, qmi_device, G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)) - -enum { - PROP_0, - PROP_FILE, - PROP_CLIENT_CTL, - PROP_LAST -}; - -static GParamSpec *properties[PROP_LAST]; - -struct _QmiDevicePrivate { - /* File */ - GFile *file; - gchar *path; - gchar *path_display; - - /* Implicit CTL client */ - QmiClientCtl *client_ctl; - - /* Supported services */ - GArray *supported_services; - - /* I/O channel, set when the file is open */ - GIOChannel *iochannel; - guint watch_id; - GByteArray *response; - - /* HT to keep track of ongoing transactions */ - GHashTable *transactions; - - /* HT of clients that want to get indications */ - GHashTable *registered_clients; -}; - -#define BUFFER_SIZE 2048 - -/*****************************************************************************/ -/* Message transactions (private) */ - -typedef struct { - QmiMessage *message; - GSimpleAsyncResult *result; - guint timeout_id; -} Transaction; - -static Transaction * -transaction_new (QmiDevice *self, - QmiMessage *message, - GAsyncReadyCallback callback, - gpointer user_data) -{ - Transaction *tr; - - tr = g_slice_new0 (Transaction); - tr->message = qmi_message_ref (message); - tr->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - transaction_new); - - return tr; -} - -static void -transaction_complete_and_free (Transaction *tr, - QmiMessage *reply, - const GError *error) -{ - g_assert (reply != NULL || error != NULL); - - if (tr->timeout_id) - g_source_remove (tr->timeout_id); - - if (reply) - g_simple_async_result_set_op_res_gpointer (tr->result, - qmi_message_ref (reply), - (GDestroyNotify)qmi_message_unref); - else - g_simple_async_result_set_from_error (tr->result, error); - - g_simple_async_result_complete_in_idle (tr->result); - g_object_unref (tr->result); - qmi_message_unref (tr->message); - g_slice_free (Transaction, tr); -} - -static inline gpointer -build_transaction_key (QmiMessage *message) -{ - gpointer key; - guint8 service; - guint8 client_id; - guint16 transaction_id; - - service = (guint8)qmi_message_get_service (message); - client_id = qmi_message_get_client_id (message); - transaction_id = qmi_message_get_transaction_id (message); - - /* We're putting a 32 bit value into a gpointer */ - key = GUINT_TO_POINTER ((((service << 8) | client_id) << 16) | transaction_id); - -#ifdef MESSAGE_ENABLE_TRACE - { - gchar *hex; - - hex = qmi_utils_str_hex (&key, sizeof (key), ':'); - g_debug ("KEY: %s", hex); - g_free (hex); - - hex = qmi_utils_str_hex (&service, sizeof (service), ':'); - g_debug (" Service: %s", hex); - g_free (hex); - - hex = qmi_utils_str_hex (&client_id, sizeof (client_id), ':'); - g_debug (" Client ID: %s", hex); - g_free (hex); - - hex = qmi_utils_str_hex (&transaction_id, sizeof (transaction_id), ':'); - g_debug (" Transaction ID: %s", hex); - g_free (hex); - } -#endif /* MESSAGE_ENABLE_TRACE */ - - return key; -} - -static Transaction * -device_release_transaction (QmiDevice *self, - gpointer key) -{ - Transaction *tr = NULL; - - if (self->priv->transactions) { - tr = g_hash_table_lookup (self->priv->transactions, key); - if (tr) - /* If found, remove it from the HT */ - g_hash_table_remove (self->priv->transactions, key); - } - - return tr; -} - -typedef struct { - QmiDevice *self; - gpointer key; -} TransactionTimeoutContext; - -static void -transaction_timeout_context_free (TransactionTimeoutContext *ctx) -{ - g_slice_free (TransactionTimeoutContext, ctx); -} - -static gboolean -transaction_timed_out (TransactionTimeoutContext *ctx) -{ - Transaction *tr; - GError *error = NULL; - - tr = device_release_transaction (ctx->self, ctx->key); - tr->timeout_id = 0; - - /* Complete transaction with a timeout error */ - error = g_error_new (QMI_CORE_ERROR, - QMI_CORE_ERROR_TIMEOUT, - "Transaction timed out"); - transaction_complete_and_free (tr, NULL, error); - g_error_free (error); - - return FALSE; -} - -static void -device_store_transaction (QmiDevice *self, - Transaction *tr, - guint timeout) -{ - TransactionTimeoutContext *timeout_ctx; - gpointer key; - - if (G_UNLIKELY (!self->priv->transactions)) - self->priv->transactions = g_hash_table_new (g_direct_hash, - g_direct_equal); - - key = build_transaction_key (tr->message); - g_hash_table_insert (self->priv->transactions, key, tr); - - /* Once it gets into the HT, setup the timeout */ - timeout_ctx = g_slice_new (TransactionTimeoutContext); - timeout_ctx->self = self; - timeout_ctx->key = key; /* valid as long as the transaction is in the HT */ - tr->timeout_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, - timeout, - (GSourceFunc)transaction_timed_out, - timeout_ctx, - (GDestroyNotify)transaction_timeout_context_free); -} - -static Transaction * -device_match_transaction (QmiDevice *self, - QmiMessage *message) -{ - /* msg can be either the original message or the response */ - return device_release_transaction (self, build_transaction_key (message)); -} - -/*****************************************************************************/ - -/** - * qmi_device_get_file: - * @self: a #QmiDevice. - * - * Get the #GFile associated with this #QmiDevice. - * - * Returns: a #GFile that must be freed with g_object_unref(). - */ -GFile * -qmi_device_get_file (QmiDevice *self) -{ - GFile *file = NULL; - - g_return_val_if_fail (QMI_IS_DEVICE (self), NULL); - - g_object_get (G_OBJECT (self), - QMI_DEVICE_FILE, &file, - NULL); - return file; -} - -/** - * qmi_device_peek_file: - * @self: a #QmiDevice. - * - * Get the #GFile associated with this #QmiDevice, without increasing the reference count - * on the returned object. - * - * Returns: a #GFile. Do not free the returned object, it is owned by @self. - */ -GFile * -qmi_device_peek_file (QmiDevice *self) -{ - g_return_val_if_fail (QMI_IS_DEVICE (self), NULL); - - return self->priv->file; -} - -/** - * qmi_device_get_path: - * @self: a #QmiDevice. - * - * Get the system path of the underlying QMI device. - * - * Returns: the system path of the device. - */ -const gchar * -qmi_device_get_path (QmiDevice *self) -{ - g_return_val_if_fail (QMI_IS_DEVICE (self), NULL); - - return self->priv->path; -} - -/** - * qmi_device_get_path_display: - * @self: a #QmiDevice. - * - * Get the system path of the underlying QMI device in UTF-8. - * - * Returns: UTF-8 encoded system path of the device. - */ -const gchar * -qmi_device_get_path_display (QmiDevice *self) -{ - g_return_val_if_fail (QMI_IS_DEVICE (self), NULL); - - return self->priv->path_display; -} - -/** - * qmi_device_is_open: - * @self: a #QmiDevice. - * - * Checks whether the #QmiDevice is open for I/O. - * - * Returns: #TRUE if @self is open, #FALSE otherwise. - */ -gboolean -qmi_device_is_open (QmiDevice *self) -{ - g_return_val_if_fail (QMI_IS_DEVICE (self), FALSE); - - return !!self->priv->iochannel; -} - -/*****************************************************************************/ -/* Register/Unregister clients that want to receive indications */ - -static gpointer -build_registered_client_key (guint8 cid, - QmiService service) -{ - return GUINT_TO_POINTER (((guint8)service << 8) | cid); -} - -static gboolean -register_client (QmiDevice *self, - QmiClient *client, - GError **error) -{ - gpointer key; - - key = build_registered_client_key (qmi_client_get_cid (client), - qmi_client_get_service (client)); - /* Only add the new client if not already registered one with the same CID - * for the same service */ - if (g_hash_table_lookup (self->priv->registered_clients, key)) { - g_set_error (error, - QMI_CORE_ERROR, - QMI_CORE_ERROR_FAILED, - "A client with CID '%u' and service '%s' is already registered", - qmi_client_get_cid (client), - qmi_service_get_string (qmi_client_get_service (client))); - return FALSE; - } - - g_hash_table_insert (self->priv->registered_clients, - key, - g_object_ref (client)); - return TRUE; -} - -static void -unregister_client (QmiDevice *self, - QmiClient *client) -{ - g_hash_table_remove (self->priv->registered_clients, - build_registered_client_key (qmi_client_get_cid (client), - qmi_client_get_service (client))); -} - -/*****************************************************************************/ -/* Allocate new client */ - -typedef struct { - QmiDevice *self; - GSimpleAsyncResult *result; - QmiService service; - GType client_type; - guint8 cid; -} AllocateClientContext; - -static void -allocate_client_context_complete_and_free (AllocateClientContext *ctx) -{ - g_simple_async_result_complete_in_idle (ctx->result); - g_object_unref (ctx->result); - g_object_unref (ctx->self); - g_slice_free (AllocateClientContext, ctx); -} - -/** - * qmi_device_allocate_client_finish: - * @self: a #QmiDevice. - * @res: a #GAsyncResult. - * @error: a #GError. - * - * Finishes an operation started with qmi_device_allocate_client(). - * - * Returns: a newly allocated #QmiClient, or #NULL if @error is set. - */ -QmiClient * -qmi_device_allocate_client_finish (QmiDevice *self, - GAsyncResult *res, - GError **error) -{ - if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) - return NULL; - - return QMI_CLIENT (g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)))); -} - -static void -build_client_object (AllocateClientContext *ctx) -{ - QmiClient *client; - GError *error = NULL; - - /* We now have a proper CID for the client, we should be able to create it - * right away */ - client = g_object_new (ctx->client_type, - QMI_CLIENT_DEVICE, ctx->self, - QMI_CLIENT_SERVICE, ctx->service, - QMI_CLIENT_CID, ctx->cid, - NULL); - - /* Register the client to get indications */ - if (!register_client (ctx->self, client, &error)) { - g_prefix_error (&error, - "Cannot register new client with CID '%u' and service '%s'", - ctx->cid, - qmi_service_get_string (ctx->service)); - g_simple_async_result_take_error (ctx->result, error); - allocate_client_context_complete_and_free (ctx); - g_object_unref (client); - return; - } - - g_debug ("Registered '%s' client with ID '%u'", - qmi_service_get_string (ctx->service), - ctx->cid); - - /* Client created and registered, complete successfully */ - g_simple_async_result_set_op_res_gpointer (ctx->result, - client, - (GDestroyNotify)g_object_unref); - allocate_client_context_complete_and_free (ctx); -} - -static void -allocate_cid_ready (QmiClientCtl *client_ctl, - GAsyncResult *res, - AllocateClientContext *ctx) -{ - QmiMessageCtlAllocateCidOutput *output; - QmiService service; - guint8 cid; - GError *error = NULL; - - /* Check result of the async operation */ - output = qmi_client_ctl_allocate_cid_finish (client_ctl, res, &error); - if (!output) { - g_prefix_error (&error, "CID allocation failed in the CTL client: "); - g_simple_async_result_take_error (ctx->result, error); - allocate_client_context_complete_and_free (ctx); - return; - } - - /* Check result of the QMI operation */ - if (!qmi_message_ctl_allocate_cid_output_get_result (output, &error)) { - g_simple_async_result_take_error (ctx->result, error); - allocate_client_context_complete_and_free (ctx); - qmi_message_ctl_allocate_cid_output_unref (output); - return; - } - - /* Allocation info is mandatory when result is success */ - g_assert (qmi_message_ctl_allocate_cid_output_get_allocation_info (output, &service, &cid, NULL)); - - if (service != ctx->service) { - g_simple_async_result_set_error ( - ctx->result, - QMI_CORE_ERROR, - QMI_CORE_ERROR_FAILED, - "CID allocation failed in the CTL client: " - "Service mismatch (requested '%s', got '%s')", - qmi_service_get_string (ctx->service), - qmi_service_get_string (service)); - allocate_client_context_complete_and_free (ctx); - qmi_message_ctl_allocate_cid_output_unref (output); - return; - } - - ctx->cid = cid; - build_client_object (ctx); - qmi_message_ctl_allocate_cid_output_unref (output); -} - -static gboolean -check_service_supported (QmiDevice *self, - QmiService service) -{ - guint i; - - /* If we didn't check supported services, just assume it is supported */ - if (!self->priv->supported_services) { - g_debug ("Assuming service '%s' is supported...", - qmi_service_get_string (service)); - return TRUE; - } - - for (i = 0; i < self->priv->supported_services->len; i++) { - QmiMessageCtlGetVersionInfoOutputServiceListService *info; - - info = &g_array_index (self->priv->supported_services, - QmiMessageCtlGetVersionInfoOutputServiceListService, - i); - - if (service == info->service) - return TRUE; - } - - return FALSE; -} - -/** - * qmi_device_allocate_client: - * @self: a #QmiDevice. - * @service: a valid #QmiService. - * @cid: a valid client ID, or #QMI_CID_NONE. - * @timeout: maximum time to wait. - * @cancellable: optional #GCancellable object, #NULL to ignore. - * @callback: a #GAsyncReadyCallback to call when the operation is finished. - * @user_data: the data to pass to callback function. - * - * Asynchronously allocates a new #QmiClient in @self. - * - * If #QMI_CID_NONE is given in @cid, a new client ID will be allocated; - * otherwise a client with the given @cid will be generated. - * - * When the operation is finished @callback will be called. You can then call - * qmi_device_allocate_client_finish() to get the result of the operation. - * - * Note: Clients for the #QMI_SERVICE_CTL cannot be created with this method; - * instead get/peek the implicit one from @self. - */ -void -qmi_device_allocate_client (QmiDevice *self, - QmiService service, - guint8 cid, - guint timeout, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - AllocateClientContext *ctx; - - g_return_if_fail (QMI_IS_DEVICE (self)); - g_return_if_fail (service != QMI_SERVICE_UNKNOWN); - - ctx = g_slice_new0 (AllocateClientContext); - ctx->self = g_object_ref (self); - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - qmi_device_allocate_client); - ctx->service = service; - - /* Check if the requested service is supported by the device */ - if (!check_service_supported (self, service)) { - g_simple_async_result_set_error (ctx->result, - QMI_CORE_ERROR, - QMI_CORE_ERROR_UNSUPPORTED, - "Service '%s' not supported by the device", - qmi_service_get_string (service)); - allocate_client_context_complete_and_free (ctx); - return; - } - - switch (service) { - case QMI_SERVICE_CTL: - g_simple_async_result_set_error (ctx->result, - QMI_CORE_ERROR, - QMI_CORE_ERROR_INVALID_ARGS, - "Cannot create additional clients for the CTL service"); - allocate_client_context_complete_and_free (ctx); - return; - - case QMI_SERVICE_DMS: - ctx->client_type = QMI_TYPE_CLIENT_DMS; - break; - - case QMI_SERVICE_WDS: - ctx->client_type = QMI_TYPE_CLIENT_WDS; - break; - - default: - g_simple_async_result_set_error (ctx->result, - QMI_CORE_ERROR, - QMI_CORE_ERROR_INVALID_ARGS, - "Clients for service '%s' not yet supported", - qmi_service_get_string (service)); - allocate_client_context_complete_and_free (ctx); - return; - } - - /* Allocate a new CID for the client to be created */ - if (cid == QMI_CID_NONE) { - QmiMessageCtlAllocateCidInput *input; - - input = qmi_message_ctl_allocate_cid_input_new (); - qmi_message_ctl_allocate_cid_input_set_service (input, ctx->service, NULL); - - g_debug ("Allocating new client ID..."); - qmi_client_ctl_allocate_cid (self->priv->client_ctl, - input, - timeout, - cancellable, - (GAsyncReadyCallback)allocate_cid_ready, - ctx); - - qmi_message_ctl_allocate_cid_input_unref (input); - return; - } - - /* Reuse the given CID */ - g_debug ("Reusing client CID '%u'...", cid); - ctx->cid = cid; - build_client_object (ctx); -} - -/*****************************************************************************/ -/* Release client */ - -typedef struct { - QmiClient *client; - GSimpleAsyncResult *result; -} ReleaseClientContext; - -static void -release_client_context_complete_and_free (ReleaseClientContext *ctx) -{ - g_simple_async_result_complete_in_idle (ctx->result); - g_object_unref (ctx->result); - g_object_unref (ctx->client); - g_slice_free (ReleaseClientContext, ctx); -} - -/** - * qmi_device_release_client_finish: - * @self: a #QmiDevice. - * @res: a #GAsyncResult. - * @error: a #GError. - * - * Finishes an operation started with qmi_device_release_client(). - * - * Note that even if the release operation returns an error, the client should - * anyway be considered released, and shouldn't be used afterwards. - * - * Returns: #TRUE if successful, or #NULL if @error is set. - */ -gboolean -qmi_device_release_client_finish (QmiDevice *self, - GAsyncResult *res, - GError **error) -{ - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); -} - -static void -client_ctl_release_cid_ready (QmiClientCtl *client_ctl, - GAsyncResult *res, - ReleaseClientContext *ctx) -{ - GError *error = NULL; - QmiMessageCtlReleaseCidOutput *output; - - /* Note: even if we return an error, the client is to be considered - * released! (so shouldn't be used) */ - - /* Check result of the async operation */ - output = qmi_client_ctl_release_cid_finish (client_ctl, res, &error); - if (!output) { - g_simple_async_result_take_error (ctx->result, error); - release_client_context_complete_and_free (ctx); - return; - } - - /* Check result of the QMI operation */ - if (!qmi_message_ctl_release_cid_output_get_result (output, &error)) { - g_simple_async_result_take_error (ctx->result, error); - release_client_context_complete_and_free (ctx); - qmi_message_ctl_release_cid_output_unref (output); - return; - } - - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - release_client_context_complete_and_free (ctx); - qmi_message_ctl_release_cid_output_unref (output); -} - -/** - * qmi_device_release_client: - * @self: a #QmiDevice. - * @client: the #QmiClient to release. - * @flags: mask of #QmiDeviceReleaseClientFlags specifying how the client should be released. - * @timeout: maximum time to wait. - * @cancellable: optional #GCancellable object, #NULL to ignore. - * @callback: a #GAsyncReadyCallback to call when the operation is finished. - * @user_data: the data to pass to callback function. - * - * Asynchronously releases the #QmiClient from the #QmiDevice. - * - * Once the #QmiClient has been released, it cannot be used any more to - * perform operations. - * - * - * When the operation is finished @callback will be called. You can then call - * qmi_device_release_client_finish() to get the result of the operation. - */ -void -qmi_device_release_client (QmiDevice *self, - QmiClient *client, - QmiDeviceReleaseClientFlags flags, - guint timeout, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ReleaseClientContext *ctx; - QmiService service; - guint8 cid; - - g_return_if_fail (QMI_IS_DEVICE (self)); - g_return_if_fail (QMI_IS_CLIENT (client)); - - /* The CTL client should not have been created out of the QmiDevice */ - g_assert (qmi_client_get_service (client) != QMI_SERVICE_CTL); - - /* NOTE! The operation must not take a reference to self, or we won't be - * able to use it implicitly from our dispose() */ - - ctx = g_slice_new0 (ReleaseClientContext); - ctx->client = g_object_ref (client); - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - qmi_device_release_client); - - cid = qmi_client_get_cid (client); - service = (guint8)qmi_client_get_service (client); - - /* Do not try to release an already released client */ - if (cid == QMI_CID_NONE) { - g_simple_async_result_set_error (ctx->result, - QMI_CORE_ERROR, - QMI_CORE_ERROR_INVALID_ARGS, - "Client is already released"); - release_client_context_complete_and_free (ctx); - return; - } - - /* Unregister from device */ - unregister_client (self, client); - - g_debug ("Unregistered '%s' client with ID '%u'", - qmi_service_get_string (service), - cid); - - /* Reset the contents of the client object, making it unusable */ - g_object_set (client, - QMI_CLIENT_CID, QMI_CID_NONE, - QMI_CLIENT_SERVICE, QMI_SERVICE_UNKNOWN, - QMI_CLIENT_DEVICE, NULL, - NULL); - - if (flags & QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID) { - QmiMessageCtlReleaseCidInput *input; - - /* And now, really try to release the CID */ - input = qmi_message_ctl_release_cid_input_new (); - qmi_message_ctl_release_cid_input_set_release_info (input, service,cid, NULL); - - /* And now, really try to release the CID */ - qmi_client_ctl_release_cid (self->priv->client_ctl, - input, - timeout, - cancellable, - (GAsyncReadyCallback)client_ctl_release_cid_ready, - ctx); - - qmi_message_ctl_release_cid_input_unref (input); - return; - } - - /* No need to release the CID, so just done */ - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - release_client_context_complete_and_free (ctx); - return; -} - -/*****************************************************************************/ -/* Open device */ - -typedef struct { - QmiClient *client; - QmiMessage *message; -} IdleIndicationContext; - -static gboolean -process_indication_idle (IdleIndicationContext *ctx) -{ - g_assert (ctx->client != NULL); - g_assert (ctx->message != NULL); - - qmi_client_process_indication (ctx->client, ctx->message); - - g_object_unref (ctx->client); - qmi_message_unref (ctx->message); - g_slice_free (IdleIndicationContext, ctx); - return FALSE; -} - -static void -report_indication (QmiClient *client, - QmiMessage *message) -{ - IdleIndicationContext *ctx; - - /* Setup an idle to Pass the indication down to the client */ - ctx = g_slice_new (IdleIndicationContext); - ctx->client = g_object_ref (client); - ctx->message = qmi_message_ref (message); - g_idle_add ((GSourceFunc)process_indication_idle, ctx); -} - -static void -process_message (QmiDevice *self, - QmiMessage *message) -{ - GError *error = NULL; - - /* Ensure the read message is valid */ - if (!qmi_message_check (message, &error)) { - g_warning ("Invalid QMI message received: %s", - error->message); - g_error_free (error); - return; - } - -#ifdef MESSAGE_ENABLE_TRACE - { - gchar *printable; - - printable = qmi_message_get_printable (message, ">>>>>> "); - g_debug ("[%s] Received message...\n%s", - self->priv->path_display, - printable); - g_free (printable); - } -#endif /* MESSAGE_ENABLE_TRACE */ - - if (qmi_message_is_indication (message)) { - if (qmi_message_get_client_id (message) == QMI_CID_BROADCAST) { - GHashTableIter iter; - gpointer key; - QmiClient *client; - - g_hash_table_iter_init (&iter, self->priv->registered_clients); - while (g_hash_table_iter_next (&iter, &key, (gpointer *)&client)) { - /* For broadcast messages, report them just if the service matches */ - if (qmi_message_get_service (message) == qmi_client_get_service (client)) - report_indication (client, message); - } - } else { - QmiClient *client; - - client = g_hash_table_lookup (self->priv->registered_clients, - build_registered_client_key (qmi_message_get_client_id (message), - qmi_message_get_service (message))); - if (client) - report_indication (client, message); - } - - return; - } - - if (qmi_message_is_response (message)) { - Transaction *tr; - - tr = device_match_transaction (self, message); - if (!tr) - g_debug ("[%s] No transaction matched in received message", - self->priv->path_display); - else - /* Report the reply message */ - transaction_complete_and_free (tr, message, NULL); - - return; - } - - g_debug ("[%s] Message received but it is neither an indication nor a response. Skipping it.", - self->priv->path_display); -} - -static void -parse_response (QmiDevice *self) -{ - do { - 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->response->len > 0 && - self->priv->response->data[0] != QMI_MESSAGE_QMUX_MARKER) { - /* TODO: Report fatal error */ - g_warning ("QMI framing error detected"); - return; - } - - message = qmi_message_new_from_raw (self->priv->response->data, - self->priv->response->len); - if (!message) - /* More data we need */ - return; - - /* Remove the read data from the response buffer */ - g_byte_array_remove_range (self->priv->response, - 0, - qmi_message_get_length (message)); - - /* Play with the received message */ - process_message (self, message); - - qmi_message_unref (message); - } while (self->priv->response->len > 0); -} - -static gboolean -data_available (GIOChannel *source, - GIOCondition condition, - QmiDevice *self) -{ - gsize bytes_read; - GIOStatus status; - gchar buffer[BUFFER_SIZE + 1]; - - if (condition & G_IO_HUP) { - g_debug ("[%s] unexpected port hangup!", - self->priv->path_display); - - if (self->priv->response && - self->priv->response->len) - g_byte_array_remove_range (self->priv->response, 0, self->priv->response->len); - - qmi_device_close (self, NULL); - return FALSE; - } - - if (condition & G_IO_ERR) { - if (self->priv->response && - self->priv->response->len) - g_byte_array_remove_range (self->priv->response, 0, self->priv->response->len); - return TRUE; - } - - /* If not ready yet, prepare the response with default initial size. */ - if (G_UNLIKELY (!self->priv->response)) - self->priv->response = g_byte_array_sized_new (500); - - do { - GError *error = NULL; - - status = g_io_channel_read_chars (source, - buffer, - BUFFER_SIZE, - &bytes_read, - &error); - if (status == G_IO_STATUS_ERROR) { - if (error) { - g_warning ("error reading from the IOChannel: '%s'", error->message); - g_error_free (error); - } - - /* Port is closed; we're done */ - if (self->priv->watch_id == 0) - break; - } - - /* If no bytes read, just let g_io_channel wait for more data */ - if (bytes_read == 0) - break; - - if (bytes_read > 0) - g_byte_array_append (self->priv->response, (const guint8 *)buffer, bytes_read); - - /* Try to parse what we already got */ - parse_response (self); - - /* And keep on if we were told to keep on */ - } while (bytes_read == BUFFER_SIZE || status == G_IO_STATUS_AGAIN); - - return TRUE; -} - -static gboolean -create_iochannel (QmiDevice *self, - GError **error) -{ - GError *inner_error = NULL; - guint fd; - - if (self->priv->iochannel) { - g_set_error (error, - QMI_CORE_ERROR, - QMI_CORE_ERROR_WRONG_STATE, - "Already open"); - return FALSE; - } - - g_assert (self->priv->file); - g_assert (self->priv->path); - - errno = 0; - fd = open (self->priv->path, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); - if (fd < 0) { - g_set_error (error, - QMI_CORE_ERROR, - QMI_CORE_ERROR_FAILED, - "Cannot open device file '%s': %s", - self->priv->path_display, - strerror (errno)); - return FALSE; - } - - /* Create new GIOChannel */ - self->priv->iochannel = g_io_channel_unix_new (fd); - - /* We don't want UTF-8 encoding, we're playing with raw binary data */ - g_io_channel_set_encoding (self->priv->iochannel, NULL, NULL); - - /* We don't want to get the channel buffered */ - g_io_channel_set_buffered (self->priv->iochannel, FALSE); - - /* Let the GIOChannel own the FD */ - g_io_channel_set_close_on_unref (self->priv->iochannel, TRUE); - - /* We don't want to get blocked while writing stuff */ - if (!g_io_channel_set_flags (self->priv->iochannel, - G_IO_FLAG_NONBLOCK, - &inner_error)) { - g_prefix_error (&inner_error, "Cannot set non-blocking channel: "); - g_propagate_error (error, inner_error); - g_io_channel_shutdown (self->priv->iochannel, FALSE, NULL); - g_io_channel_unref (self->priv->iochannel); - self->priv->iochannel = NULL; - return FALSE; - } - - self->priv->watch_id = g_io_add_watch (self->priv->iochannel, - G_IO_IN | G_IO_ERR | G_IO_HUP, - (GIOFunc)data_available, - self); - - return !!self->priv->iochannel; -} - -typedef struct { - QmiDevice *self; - GSimpleAsyncResult *result; - GCancellable *cancellable; - QmiDeviceOpenFlags flags; - guint timeout; - guint version_check_retries; -} DeviceOpenContext; - -static void -device_open_context_complete_and_free (DeviceOpenContext *ctx) -{ - g_simple_async_result_complete_in_idle (ctx->result); - g_object_unref (ctx->result); - if (ctx->cancellable) - g_object_unref (ctx->cancellable); - g_object_unref (ctx->self); - g_slice_free (DeviceOpenContext, ctx); -} - -/** - * qmi_device_open_finish: - * @self: a #QmiDevice. - * @res: a #GAsyncResult. - * @error: a #GError. - * - * Finishes an asynchronous open operation started with qmi_device_open_async(). - * - * Returns: #TRUE if successful, #FALSE if @error is set. - */ -gboolean -qmi_device_open_finish (QmiDevice *self, - GAsyncResult *res, - GError **error) -{ - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); -} - -static void process_open_flags (DeviceOpenContext *ctx); - -static void -sync_ready (QmiClientCtl *client_ctl, - GAsyncResult *res, - DeviceOpenContext *ctx) -{ - GError *error = NULL; - QmiMessageCtlSyncOutput *output; - - /* Check result of the async operation */ - output = qmi_client_ctl_sync_finish (client_ctl, res, &error); - if(!output) { - g_simple_async_result_take_error (ctx->result, error); - device_open_context_complete_and_free (ctx); - return; - } - - /* Check result of the QMI operation */ - if (!qmi_message_ctl_sync_output_get_result (output, &error)) { - g_simple_async_result_take_error (ctx->result, error); - device_open_context_complete_and_free (ctx); - qmi_message_ctl_sync_output_unref (output); - return; - } - - g_debug ("[%s] Sync operation finished", - ctx->self->priv->path_display); - - /* Keep on with next flags */ - process_open_flags (ctx); - qmi_message_ctl_sync_output_unref (output); -} - -static void -version_info_ready (QmiClientCtl *client_ctl, - GAsyncResult *res, - DeviceOpenContext *ctx) -{ - GArray *service_list; - QmiMessageCtlGetVersionInfoOutput *output; - GError *error = NULL; - guint i; - - /* Check result of the async operation */ - output = qmi_client_ctl_get_version_info_finish (client_ctl, res, &error); - if (!output) { - if (g_error_matches (error, QMI_CORE_ERROR, QMI_CORE_ERROR_TIMEOUT)) { - /* Update retries... */ - ctx->version_check_retries--; - /* If retries left, retry */ - if (ctx->version_check_retries > 0) { - qmi_client_ctl_get_version_info (ctx->self->priv->client_ctl, - NULL, - 1, - ctx->cancellable, - (GAsyncReadyCallback)version_info_ready, - ctx); - return; - } - - /* Otherwise, propagate the error */ - } - - g_simple_async_result_take_error (ctx->result, error); - device_open_context_complete_and_free (ctx); - return; - } - - /* Check result of the QMI operation */ - if (!qmi_message_ctl_get_version_info_output_get_result (output, &error)) { - g_simple_async_result_take_error (ctx->result, error); - device_open_context_complete_and_free (ctx); - qmi_message_ctl_get_version_info_output_unref (output); - return; - } - - /* QMI operation succeeded, we can now get the outputs */ - service_list = NULL; - qmi_message_ctl_get_version_info_output_get_service_list (output, - &service_list, - NULL); - ctx->self->priv->supported_services = g_array_ref (service_list); - - g_debug ("[%s] QMI Device supports %u services:", - ctx->self->priv->path_display, - ctx->self->priv->supported_services->len); - for (i = 0; i < ctx->self->priv->supported_services->len; i++) { - QmiMessageCtlGetVersionInfoOutputServiceListService *info; - - info = &g_array_index (ctx->self->priv->supported_services, - QmiMessageCtlGetVersionInfoOutputServiceListService, - i); - g_debug ("[%s] %s (%u.%u)", - ctx->self->priv->path_display, - qmi_service_get_string (info->service), - info->major_version, - info->minor_version); - } - - /* Keep on with next flags */ - process_open_flags (ctx); - qmi_message_ctl_get_version_info_output_unref (output); -} - -static void -process_open_flags (DeviceOpenContext *ctx) -{ - /* Query version info? */ - if (ctx->flags & QMI_DEVICE_OPEN_FLAGS_VERSION_INFO) { - ctx->flags &= ~QMI_DEVICE_OPEN_FLAGS_VERSION_INFO; - /* Setup how many times to retry... We'll retry once per second */ - ctx->version_check_retries = ctx->timeout > 0 ? ctx->timeout : 1; - g_debug ("Checking version info (%u retries)...", ctx->version_check_retries); - qmi_client_ctl_get_version_info (ctx->self->priv->client_ctl, - NULL, - 1, - ctx->cancellable, - (GAsyncReadyCallback)version_info_ready, - ctx); - return; - } - - /* Sync? */ - if (ctx->flags & QMI_DEVICE_OPEN_FLAGS_SYNC) { - g_debug ("Running sync..."); - ctx->flags &= ~QMI_DEVICE_OPEN_FLAGS_SYNC; - qmi_client_ctl_sync (ctx->self->priv->client_ctl, - NULL, - ctx->timeout, - ctx->cancellable, - (GAsyncReadyCallback)sync_ready, - ctx); - return; - } - - /* No more flags to process, done we are */ - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - device_open_context_complete_and_free (ctx); -} - -/** - * qmi_device_open: - * @self: a #QmiDevice. - * @flags: mask of #QmiDeviceOpenFlags specifying how the device should be opened. - * @timeout: maximum time, in seconds, to wait for the device to be opened. - * @cancellable: optional #GCancellable object, #NULL to ignore. - * @callback: a #GAsyncReadyCallback to call when the operation is finished. - * @user_data: the data to pass to callback function. - * - * Asynchronously opens a #QmiDevice for I/O. - * - * When the operation is finished @callback will be called. You can then call - * qmi_device_open_finish() to get the result of the operation. - */ -void -qmi_device_open (QmiDevice *self, - QmiDeviceOpenFlags flags, - guint timeout, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - DeviceOpenContext *ctx; - GError *error = NULL; - - g_return_if_fail (QMI_IS_DEVICE (self)); - - ctx = g_slice_new (DeviceOpenContext); - ctx->self = g_object_ref (self); - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - qmi_device_open); - ctx->flags = flags; - ctx->timeout = timeout; - ctx->cancellable = (cancellable ? g_object_ref (cancellable) : NULL); - - if (!create_iochannel (self, &error)) { - g_prefix_error (&error, - "Cannot open QMI device: "); - g_simple_async_result_take_error (ctx->result, error); - device_open_context_complete_and_free (ctx); - return; - } - - /* Process all open flags */ - process_open_flags (ctx); -} - -/*****************************************************************************/ -/* Close channel */ - -static gboolean -destroy_iochannel (QmiDevice *self, - GError **error) -{ - GError *inner_error = NULL; - - /* Already closed? */ - if (!self->priv->iochannel) - return TRUE; - - g_io_channel_shutdown (self->priv->iochannel, TRUE, &inner_error); - - /* Failures when closing still make the device to get closed */ - g_io_channel_unref (self->priv->iochannel); - self->priv->iochannel = NULL; - - if (self->priv->watch_id) { - g_source_remove (self->priv->watch_id); - self->priv->watch_id = 0; - } - - if (self->priv->response) { - g_byte_array_unref (self->priv->response); - self->priv->response = NULL; - } - - if (inner_error) { - g_propagate_error (error, inner_error); - return FALSE; - } - - return TRUE; -} - -/** - * qmi_device_close: - * @self: a #QmiDevice - * @error: a #GError - * - * Synchronously closes a #QmiDevice, preventing any further I/O. - * - * Closing a #QmiDevice multiple times will not return an error. - * - * Returns: #TRUE if successful, #FALSE if @error is set. - */ -gboolean -qmi_device_close (QmiDevice *self, - GError **error) -{ - g_return_val_if_fail (QMI_IS_DEVICE (self), FALSE); - - if (!destroy_iochannel (self, error)) { - g_prefix_error (error, - "Cannot close QMI device: "); - return FALSE; - } - - return TRUE; -} - -/*****************************************************************************/ -/* Command */ - -QmiMessage * -qmi_device_command_finish (QmiDevice *self, - GAsyncResult *res, - GError **error) -{ - if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) - return NULL; - - return qmi_message_ref (g_simple_async_result_get_op_res_gpointer ( - G_SIMPLE_ASYNC_RESULT (res))); -} - -void -qmi_device_command (QmiDevice *self, - QmiMessage *message, - guint timeout, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GError *error = NULL; - Transaction *tr; - gconstpointer raw_message; - gsize raw_message_len; - gsize written; - GIOStatus write_status; - - g_return_if_fail (QMI_IS_DEVICE (self)); - g_return_if_fail (message != NULL); - - tr = transaction_new (self, message, callback, user_data); - - /* Device must be open */ - if (!self->priv->iochannel) { - error = g_error_new (QMI_CORE_ERROR, - QMI_CORE_ERROR_WRONG_STATE, - "Device must be open to send commands"); - transaction_complete_and_free (tr, NULL, error); - g_error_free (error); - return; - } - - /* Non-CTL services should use a proper CID */ - if (qmi_message_get_service (message) != QMI_SERVICE_CTL && - qmi_message_get_client_id (message) == 0) { - error = g_error_new (QMI_CORE_ERROR, - QMI_CORE_ERROR_FAILED, - "Cannot send message in service '%s' without a CID", - qmi_service_get_string (qmi_message_get_service (message))); - transaction_complete_and_free (tr, NULL, error); - g_error_free (error); - return; - } - -#ifdef MESSAGE_ENABLE_TRACE - { - gchar *printable; - - printable = qmi_message_get_printable (message, "<<<<<< "); - g_debug ("[%s] Sending message...\n%s", - self->priv->path_display, - printable); - g_free (printable); - } -#endif /* MESSAGE_ENABLE_TRACE */ - - /* Get raw message */ - raw_message = qmi_message_get_raw (message, &raw_message_len, &error); - if (!raw_message) { - g_prefix_error (&error, "Cannot get raw message: "); - transaction_complete_and_free (tr, NULL, error); - g_error_free (error); - return; - } - - /* Setup context to match response */ - device_store_transaction (self, tr, timeout); - - written = 0; - write_status = G_IO_STATUS_AGAIN; - while (write_status == G_IO_STATUS_AGAIN) { - write_status = g_io_channel_write_chars (self->priv->iochannel, - raw_message, - (gssize)raw_message_len, - &written, - &error); - switch (write_status) { - case G_IO_STATUS_ERROR: - g_prefix_error (&error, "Cannot write message: "); - - /* Match transaction so that we remove it from our tracking table */ - tr = device_match_transaction (self, message); - transaction_complete_and_free (tr, NULL, error); - g_error_free (error); - return; - - case G_IO_STATUS_EOF: - /* We shouldn't get EOF when writing */ - g_assert_not_reached (); - break; - - case G_IO_STATUS_NORMAL: - /* All good, we'll exit the loop now */ - break; - - case G_IO_STATUS_AGAIN: - /* We're in a non-blocking channel and therefore we're up to receive - * EAGAIN; just retry in this case. TODO: in an idle? */ - break; - } - } - - /* Just return, we'll get response asynchronously */ -} - -/*****************************************************************************/ -/* New QMI device */ - -/** - * qmi_device_new_finish: - * @res: a #GAsyncResult. - * @error: a #GError. - * - * Finishes an operation started with qmi_device_new(). - * - * Returns: A newly created #QmiDevice, or #NULL if @error is set. - */ -QmiDevice * -qmi_device_new_finish (GAsyncResult *res, - GError **error) -{ - GObject *ret; - GObject *source_object; - - source_object = g_async_result_get_source_object (res); - ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); - g_object_unref (source_object); - - return (ret ? QMI_DEVICE (ret) : NULL); -} - -/** - * qmi_device_new: - * @file: a #GFile. - * @cancellable: optional #GCancellable object, #NULL to ignore. - * @callback: a #GAsyncReadyCallback to call when the initialization is finished. - * @user_data: the data to pass to callback function. - * - * Asynchronously creates a #QmiDevice object to manage @file. - * When the operation is finished, @callback will be invoked. You can then call - * qmi_device_new_finish() to get the result of the operation. - */ -void -qmi_device_new (GFile *file, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_async_initable_new_async (QMI_TYPE_DEVICE, - G_PRIORITY_DEFAULT, - cancellable, - callback, - user_data, - QMI_DEVICE_FILE, file, - NULL); -} - -/*****************************************************************************/ -/* Async init */ - -typedef struct { - QmiDevice *self; - GSimpleAsyncResult *result; - GCancellable *cancellable; -} InitContext; - -static void -init_context_complete_and_free (InitContext *ctx) -{ - g_simple_async_result_complete_in_idle (ctx->result); - if (ctx->cancellable) - g_object_unref (ctx->cancellable); - g_object_unref (ctx->result); - g_object_unref (ctx->self); - g_slice_free (InitContext, ctx); -} - -static gboolean -initable_init_finish (GAsyncInitable *initable, - GAsyncResult *result, - GError **error) -{ - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); -} - -static void -query_info_async_ready (GFile *file, - GAsyncResult *res, - InitContext *ctx) -{ - GError *error = NULL; - GFileInfo *info; - - info = g_file_query_info_finish (file, res, &error); - if (!info) { - g_prefix_error (&error, - "Couldn't query file info: "); - g_simple_async_result_take_error (ctx->result, error); - init_context_complete_and_free (ctx); - return; - } - - /* Our QMI device must be of SPECIAL type */ - if (g_file_info_get_file_type (info) != G_FILE_TYPE_SPECIAL) { - g_simple_async_result_set_error (ctx->result, - QMI_CORE_ERROR, - QMI_CORE_ERROR_FAILED, - "Wrong file type"); - init_context_complete_and_free (ctx); - return; - } - g_object_unref (info); - - /* Create the implicit CTL client */ - ctx->self->priv->client_ctl = g_object_new (QMI_TYPE_CLIENT_CTL, - QMI_CLIENT_DEVICE, ctx->self, - QMI_CLIENT_SERVICE, QMI_SERVICE_CTL, - QMI_CLIENT_CID, QMI_CID_NONE, - NULL); - - /* Register the CTL client to get indications */ - register_client (ctx->self, - QMI_CLIENT (ctx->self->priv->client_ctl), - &error); - g_assert_no_error (error); - - /* Done we are */ - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - init_context_complete_and_free (ctx); -} - -static void -initable_init_async (GAsyncInitable *initable, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - InitContext *ctx; - - ctx = g_slice_new0 (InitContext); - ctx->self = g_object_ref (initable); - if (cancellable) - ctx->cancellable = g_object_ref (cancellable); - ctx->result = g_simple_async_result_new (G_OBJECT (initable), - callback, - user_data, - initable_init_async); - - /* We need a proper file to initialize */ - if (!ctx->self->priv->file) { - g_simple_async_result_set_error (ctx->result, - QMI_CORE_ERROR, - QMI_CORE_ERROR_INVALID_ARGS, - "Cannot initialize QMI device: No file given"); - init_context_complete_and_free (ctx); - return; - } - - /* Check the file type. Note that this is just a quick check to avoid - * creating QmiDevices pointing to a location already known not to be a QMI - * device. */ - g_file_query_info_async (ctx->self->priv->file, - G_FILE_ATTRIBUTE_STANDARD_TYPE, - G_FILE_QUERY_INFO_NONE, - G_PRIORITY_DEFAULT, - ctx->cancellable, - (GAsyncReadyCallback)query_info_async_ready, - ctx); -} - -/*****************************************************************************/ - -static void -set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - QmiDevice *self = QMI_DEVICE (object); - - switch (prop_id) { - case PROP_FILE: - g_assert (self->priv->file == NULL); - self->priv->file = g_value_dup_object (value); - self->priv->path = g_file_get_path (self->priv->file); - self->priv->path_display = g_filename_display_name (self->priv->path); - break; - case PROP_CLIENT_CTL: - /* Not writable */ - g_assert_not_reached (); - 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) -{ - QmiDevice *self = QMI_DEVICE (object); - - switch (prop_id) { - case PROP_FILE: - g_value_set_object (value, self->priv->file); - break; - case PROP_CLIENT_CTL: - g_value_set_object (value, self->priv->client_ctl); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -qmi_device_init (QmiDevice *self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), - QMI_TYPE_DEVICE, - QmiDevicePrivate); - - self->priv->registered_clients = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - g_object_unref); -} - -static gboolean -foreach_warning (gpointer key, - QmiClient *client, - QmiDevice *self) -{ - g_warning ("QMI client for service '%s' with CID '%u' wasn't released", - qmi_service_get_string (qmi_client_get_service (client)), - qmi_client_get_cid (client)); - - return TRUE; -} - -static void -dispose (GObject *object) -{ - QmiDevice *self = QMI_DEVICE (object); - - g_clear_object (&self->priv->file); - - /* unregister our CTL client */ - unregister_client (self, QMI_CLIENT (self->priv->client_ctl)); - - /* If clients were left unreleased, we'll just warn about it. - * There is no point in trying to request CID releases, as the device - * itself is being disposed. */ - g_hash_table_foreach_remove (self->priv->registered_clients, - (GHRFunc)foreach_warning, - self); - - g_clear_object (&self->priv->client_ctl); - - G_OBJECT_CLASS (qmi_device_parent_class)->dispose (object); -} - -static void -finalize (GObject *object) -{ - QmiDevice *self = QMI_DEVICE (object); - - /* Transactions keep refs to the device, so it's actually - * impossible to have any content in the HT */ - if (self->priv->transactions) { - g_assert (g_hash_table_size (self->priv->transactions) == 0); - g_hash_table_unref (self->priv->transactions); - } - - g_hash_table_unref (self->priv->registered_clients); - - if (self->priv->supported_services) - g_array_unref (self->priv->supported_services); - - g_free (self->priv->path); - g_free (self->priv->path_display); - if (self->priv->response) - g_byte_array_unref (self->priv->response); - if (self->priv->iochannel) - g_io_channel_unref (self->priv->iochannel); - - G_OBJECT_CLASS (qmi_device_parent_class)->finalize (object); -} - -static void -async_initable_iface_init (GAsyncInitableIface *iface) -{ - iface->init_async = initable_init_async; - iface->init_finish = initable_init_finish; -} - -static void -qmi_device_class_init (QmiDeviceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (object_class, sizeof (QmiDevicePrivate)); - - object_class->get_property = get_property; - object_class->set_property = set_property; - object_class->finalize = finalize; - object_class->dispose = dispose; - - properties[PROP_FILE] = - g_param_spec_object (QMI_DEVICE_FILE, - "Device file", - "File to the underlying QMI device", - G_TYPE_FILE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); - g_object_class_install_property (object_class, PROP_FILE, properties[PROP_FILE]); - - properties[PROP_CLIENT_CTL] = - g_param_spec_object (QMI_DEVICE_CLIENT_CTL, - "CTL client", - "Implicit CTL client", - QMI_TYPE_CLIENT_CTL, - G_PARAM_READABLE); - g_object_class_install_property (object_class, PROP_CLIENT_CTL, properties[PROP_CLIENT_CTL]); -} diff --git a/src/qmi-device.h b/src/qmi-device.h deleted file mode 100644 index 79272d8..0000000 --- a/src/qmi-device.h +++ /dev/null @@ -1,145 +0,0 @@ -/* -*- 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 Aleksander Morgado - */ - -#ifndef _LIBQMI_GLIB_QMI_DEVICE_H_ -#define _LIBQMI_GLIB_QMI_DEVICE_H_ - -#include - -#include "qmi-enums.h" -#include "qmi-message.h" -#include "qmi-client.h" - -G_BEGIN_DECLS - -#define QMI_TYPE_DEVICE (qmi_device_get_type ()) -#define QMI_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), QMI_TYPE_DEVICE, QmiDevice)) -#define QMI_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), QMI_TYPE_DEVICE, QmiDeviceClass)) -#define QMI_IS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), QMI_TYPE_DEVICE)) -#define QMI_IS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), QMI_TYPE_DEVICE)) -#define QMI_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), QMI_TYPE_DEVICE, QmiDeviceClass)) - -typedef struct _QmiDevice QmiDevice; -typedef struct _QmiDeviceClass QmiDeviceClass; -typedef struct _QmiDevicePrivate QmiDevicePrivate; - -#define QMI_DEVICE_FILE "device-file" -#define QMI_DEVICE_CLIENT_CTL "device-client-ctl" - -struct _QmiDevice { - GObject parent; - QmiDevicePrivate *priv; -}; - -struct _QmiDeviceClass { - GObjectClass parent; -}; - -GType qmi_device_get_type (void); - -void qmi_device_new (GFile *file, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -QmiDevice *qmi_device_new_finish (GAsyncResult *res, - GError **error); - -GFile *qmi_device_get_file (QmiDevice *self); -GFile *qmi_device_peek_file (QmiDevice *self); -const gchar *qmi_device_get_path (QmiDevice *self); -const gchar *qmi_device_get_path_display (QmiDevice *self); -gboolean qmi_device_is_open (QmiDevice *self); - -/** - * QmiDeviceOpenFlags: - * @QMI_DEVICE_OPEN_FLAGS_NONE: No flags. - * @QMI_DEVICE_OPEN_FLAGS_VERSION_INFO: Run version info check when opening. - * @QMI_DEVICE_OPEN_FLAGS_SYNC: Synchronize with endpoint once the device is open. Will release any previously allocated client ID. - * - * Flags to specify which actions to be performed when the device is open. - */ -typedef enum { - QMI_DEVICE_OPEN_FLAGS_NONE = 0, - QMI_DEVICE_OPEN_FLAGS_VERSION_INFO = 1 << 0, - QMI_DEVICE_OPEN_FLAGS_SYNC = 1 << 1 -} QmiDeviceOpenFlags; - -void qmi_device_open (QmiDevice *self, - QmiDeviceOpenFlags flags, - guint timeout, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean qmi_device_open_finish (QmiDevice *self, - GAsyncResult *res, - GError **error); - -gboolean qmi_device_close (QmiDevice *self, - GError **error); - -void qmi_device_allocate_client (QmiDevice *self, - QmiService service, - guint8 cid, - guint timeout, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -QmiClient *qmi_device_allocate_client_finish (QmiDevice *self, - GAsyncResult *res, - GError **error); - -/** - * QmiDeviceReleaseClientFlags: - * @QMI_DEVICE_RELEASE_CLIENT_FLAGS_NONE: No flags. - * @QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID: Release the CID when releasing the client. - * - * Flags to specify which actions to be performed when releasing the client. - */ -typedef enum { - QMI_DEVICE_RELEASE_CLIENT_FLAGS_NONE = 0, - QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID = 1 << 0 -} QmiDeviceReleaseClientFlags; - -void qmi_device_release_client (QmiDevice *self, - QmiClient *client, - QmiDeviceReleaseClientFlags flags, - guint timeout, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean qmi_device_release_client_finish (QmiDevice *self, - GAsyncResult *res, - GError **error); - -void qmi_device_command (QmiDevice *self, - QmiMessage *message, - guint timeout, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -QmiMessage *qmi_device_command_finish (QmiDevice *self, - GAsyncResult *res, - GError **error); - -G_END_DECLS - -#endif /* _LIBQMI_GLIB_QMI_DEVICE_H_ */ diff --git a/src/qmi-enums-dms.h b/src/qmi-enums-dms.h deleted file mode 100644 index 5103599..0000000 --- a/src/qmi-enums-dms.h +++ /dev/null @@ -1,195 +0,0 @@ -/* -*- 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 - */ - -#ifndef _LIBQMI_GLIB_QMI_ENUMS_DMS_H_ -#define _LIBQMI_GLIB_QMI_ENUMS_DMS_H_ - -/*****************************************************************************/ -/* Helper enums for the 'QMI DMS Get Capabilities' message */ - -/** - * QmiDmsDataServiceCapability: - * @QMI_DMS_DATA_SERVICE_CAPABILITY_NONE: No data services supported. - * @QMI_DMS_DATA_SERVICE_CAPABILITY_CS: Only CS supported. - * @QMI_DMS_DATA_SERVICE_CAPABILITY_PS: Only PS supported. - * @QMI_DMS_DATA_SERVICE_CAPABILITY_SIMULTANEOUS_CS_PS: Simultaneous CS and PS supported. - * @QMI_DMS_DATA_SERVICE_CAPABILITY_NON_SIMULTANEOUS_CS_PS: Non simultaneous CS and PS supported. - * - * Data service capability. - */ -typedef enum { - QMI_DMS_DATA_SERVICE_CAPABILITY_NONE = 0, - QMI_DMS_DATA_SERVICE_CAPABILITY_CS = 1, - QMI_DMS_DATA_SERVICE_CAPABILITY_PS = 2, - QMI_DMS_DATA_SERVICE_CAPABILITY_SIMULTANEOUS_CS_PS = 3, - QMI_DMS_DATA_SERVICE_CAPABILITY_NON_SIMULTANEOUS_CS_PS = 4 -} QmiDmsDataServiceCapability; - -/** - * QmiDmsSimCapability: - * @QMI_DMS_SIM_CAPABILITY_NOT_SUPPORTED: SIM not supported. - * @QMI_DMS_SIM_CAPABILITY_SUPPORTED: SIM is supported. - * - * SIM capability. - */ -typedef enum { - QMI_DMS_SIM_CAPABILITY_NOT_SUPPORTED = 1, - QMI_DMS_SIM_CAPABILITY_SUPPORTED = 2 -} QmiDmsSimCapability; - -/** - * QmiDmsRadioInterface: - * @QMI_DMS_RADIO_INTERFACE_CDMA20001X: CDMA2000 1x. - * @QMI_DMS_RADIO_INTERFACE_EVDO: CDMA2000 HRPD (1xEV-DO) - * @QMI_DMS_RADIO_INTERFACE_GSM: GSM. - * @QMI_DMS_RADIO_INTERFACE_UMTS: UMTS. - * @QMI_DMS_RADIO_INTERFACE_LTE: LTE. - * - * Radio interface type. - */ -typedef enum { - QMI_DMS_RADIO_INTERFACE_CDMA20001X = 1, - QMI_DMS_RADIO_INTERFACE_EVDO = 2, - QMI_DMS_RADIO_INTERFACE_GSM = 4, - QMI_DMS_RADIO_INTERFACE_UMTS = 5, - QMI_DMS_RADIO_INTERFACE_LTE = 8 -} QmiDmsRadioInterface; - - -/*****************************************************************************/ -/* Helper enums for the 'QMI DMS Get Power State' message */ - -/** - * QmiDmsPowerState: - * @QMI_DMS_POWER_STATE_EXTERNAL_SOURCE: Powered by an external source. - * @QMI_DMS_POWER_STATE_BATTERY_CONNECTED: Battery is connected. - * @QMI_DMS_POWER_STATE_BATTERY_CHARGING: Battery is currently being charged. - * @QMI_DMS_POWER_STATE_FAULT: Recognized power fault. - * - * Flags specifying the current power state. - * - * If @QMI_DMS_POWER_STATE_EXTERNAL_SOURCE is set, the device is powerered by an - * external source; otherwise it is powered by a battery. - * - * If @QMI_DMS_POWER_STATE_BATTERY_CONNECTED is set, the battery is connected; - * otherwise the battery is not connected. - * - * If @QMI_DMS_POWER_STATE_BATTERY_CHARGING is set, the battery is being charged; - * otherwise the battery is not being charged. - * - * If @QMI_DMS_POWER_STATE_FAULT is set, a power fault has been detected. - */ -typedef enum { - QMI_DMS_POWER_STATE_EXTERNAL_SOURCE = 1 << 0, - QMI_DMS_POWER_STATE_BATTERY_CONNECTED = 1 << 1, - QMI_DMS_POWER_STATE_BATTERY_CHARGING = 1 << 2, - QMI_DMS_POWER_STATE_FAULT = 1 << 3, -} QmiDmsPowerState; - - -/*****************************************************************************/ -/* Helper enums for the 'QMI DMS UIM Set PIN Protection' message */ - -/** - * QmiDmsUimPinId: - * @QMI_DMS_UIM_PIN_ID_PIN: PIN. - * @QMI_DMS_UIM_PIN_ID_PIN2: PIN2. - * - * The PIN identifier. - */ -typedef enum { - QMI_DMS_UIM_PIN_ID_PIN = 1, - QMI_DMS_UIM_PIN_ID_PIN2 = 2 -} QmiDmsUimPinId; - - -/*****************************************************************************/ -/* Helper enums for the 'QMI DMS UIM Get PIN Status' message */ - -/** - * QmiDmsPinStatus: - * @QMI_DMS_UIM_PIN_STATUS_NOT_INITIALIZED: Not initialized. - * @QMI_DMS_UIM_PIN_STATUS_ENABLED_NOT_VERIFIED: Enabled, not verified. - * @QMI_DMS_UIM_PIN_STATUS_ENABLED_VERIFIED: Enabled, verified. - * @QMI_DMS_UIM_PIN_STATUS_DISABLED: Disabled. - * @QMI_DMS_UIM_PIN_STATUS_BLOCKED: Blocked. - * @QMI_DMS_UIM_PIN_STATUS_PERMANENTLY_BLOCKED: Permanently Blocked. - * @QMI_DMS_UIM_PIN_STATUS_UNBLOCKED: Unblocked. - * @QMI_DMS_UIM_PIN_STATUS_CHANGED: Changed. - * - * The PIN status. - */ -typedef enum { - QMI_DMS_UIM_PIN_STATUS_NOT_INITIALIZED = 0, - QMI_DMS_UIM_PIN_STATUS_ENABLED_NOT_VERIFIED = 1, - QMI_DMS_UIM_PIN_STATUS_ENABLED_VERIFIED = 2, - QMI_DMS_UIM_PIN_STATUS_DISABLED = 3, - QMI_DMS_UIM_PIN_STATUS_BLOCKED = 4, - QMI_DMS_UIM_PIN_STATUS_PERMANENTLY_BLOCKED = 5, - QMI_DMS_UIM_PIN_STATUS_UNBLOCKED = 6, - QMI_DMS_UIM_PIN_STATUS_CHANGED = 7, -} QmiDmsUimPinStatus; - -/*****************************************************************************/ -/* Helper enums for the 'QMI DMS Get Operating Mode' message */ - -/** - * QmiDmsOperatingMode: - * @QMI_DMS_OPERATING_MODE_ONLINE: Device can acquire a system and make calls. - * @QMI_DMS_OPERATING_MODE_LOW_POWER: Device has temporarily disabled RF. - * @QMI_DMS_OPERATING_MODE_PERSISTENT_LOW_POWER: Device has disabled RF and state persists even after a reset. - * @QMI_DMS_OPERATING_MODE_FACTORY_TEST: Special mode for manufacturer tests. - * @QMI_DMS_OPERATING_MODE_OFFLINE: Device has deactivated RF and is partially shutdown. - * @QMI_DMS_OPERATING_MODE_RESETTING: Device is in the process of power cycling. - * @QMI_DMS_OPERATING_MODE_SHUTTING_DOWN: Device is in the process of shutting down. - * @QMI_DMS_OPERATING_MODE_MODE_ONLY_LOW_POWER: Mode-only Low Power. - * - * Operating mode of the device. - */ -typedef enum { - QMI_DMS_OPERATING_MODE_ONLINE = 0, - QMI_DMS_OPERATING_MODE_LOW_POWER = 1, - QMI_DMS_OPERATING_MODE_FACTORY_TEST = 2, - QMI_DMS_OPERATING_MODE_OFFLINE = 3, - QMI_DMS_OPERATING_MODE_RESETTING = 4, - QMI_DMS_OPERATING_MODE_SHUTTING_DOWN = 5, - QMI_DMS_OPERATING_MODE_PERSISTENT_LOW_POWER = 6, - QMI_DMS_OPERATING_MODE_MODE_ONLY_LOW_POWER = 7 -} QmiDmsOperatingMode; - -/** - * QmiDmsOfflineReason: - * @QMI_DMS_OFFLINE_REASON_HOST_IMAGE_MISCONFIGURATION: Host image misconfiguration. - * @QMI_DMS_OFFLINE_REASON_PRI_IMAGE_MISCONFIGURATION: PRI image misconfiguration. - * @QMI_DMS_OFFLINE_REASON_PRI_VERSION_INCOMPATIBLE: PRI version incompatible. - * @QMI_DMS_OFFLINE_REASON_DEVICE_MEMORY_FULL: Memory full, cannot copy PRI information. - * - * Reasons for being in Offline (@QMI_DMS_OPERATING_MODE_OFFLINE) state. - */ -typedef enum { - QMI_DMS_OFFLINE_REASON_HOST_IMAGE_MISCONFIGURATION = 1 << 0, - QMI_DMS_OFFLINE_REASON_PRI_IMAGE_MISCONFIGURATION = 1 << 1, - QMI_DMS_OFFLINE_REASON_PRI_VERSION_INCOMPATIBLE = 1 << 2, - QMI_DMS_OFFLINE_REASON_DEVICE_MEMORY_FULL = 1 << 3 -} QmiDmsOfflineReason; - -#endif /* _LIBQMI_GLIB_QMI_ENUMS_DMS_H_ */ diff --git a/src/qmi-enums-wds.h b/src/qmi-enums-wds.h deleted file mode 100644 index 79ab360..0000000 --- a/src/qmi-enums-wds.h +++ /dev/null @@ -1,197 +0,0 @@ -/* -*- 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 - */ - -#ifndef _LIBQMI_GLIB_QMI_ENUMS_WDS_H_ -#define _LIBQMI_GLIB_QMI_ENUMS_WDS_H_ - -/*****************************************************************************/ -/* Helper enums for the 'QMI WDS Get Packet Service Status' message */ - -/** - * QmiWdsConnectionStatus: - * @QMI_WDS_CONNECTION_STATUS_UNKNOWN: Unknown status. - * @QMI_WDS_CONNECTION_STATUS_DISCONNECTED: Network is disconnected - * @QMI_WDS_CONNECTION_STATUS_CONNECTED: Network is connected. - * @QMI_WDS_CONNECTION_STATUS_SUSPENDED: Network connection is suspended. - * @QMI_WDS_CONNECTION_STATUS_AUTHENTICATING: Network authentication is ongoing. - * - * Status of the network connection. - */ -typedef enum { - QMI_WDS_CONNECTION_STATUS_UNKNOWN = 0, - QMI_WDS_CONNECTION_STATUS_DISCONNECTED = 1, - QMI_WDS_CONNECTION_STATUS_CONNECTED = 2, - QMI_WDS_CONNECTION_STATUS_SUSPENDED = 3, - QMI_WDS_CONNECTION_STATUS_AUTHENTICATING = 4 -} QmiWdsConnectionStatus; - - -/*****************************************************************************/ -/* Helper enums for the 'QMI WDS Get Data Bearer Technology' message */ - -/** - * QmiWdsDataBearerTechnology: - * @QMI_WDS_DATA_BEARER_TECHNOLOGY_UNKNOWN: Unknown. - * @QMI_WDS_DATA_BEARER_TECHNOLOGY_CDMA20001X: CDMA2000 1x. - * @QMI_WDS_DATA_BEARER_TECHNOLOGY_1xEVDO: CDMA2000 HRPD 1xEV-DO. - * @QMI_WDS_DATA_BEARER_TECHNOLOGY_GSM: GSM. - * @QMI_WDS_DATA_BEARER_TECHNOLOGY_UMTS: UMTS. - * @QMI_WDS_DATA_BEARER_TECHNOLOGY_1xEVDO_REVA: CDMA2000 HRPD 1xEV-DO RevA. - * @QMI_WDS_DATA_BEARER_TECHNOLOGY_EDGE: EDGE. - * @QMI_WDS_DATA_BEARER_TECHNOLOGY_HSDPA: HSDPA and WCDMA. - * @QMI_WDS_DATA_BEARER_TECHNOLOGY_HSUPA: WCDMA and HSUPA. - * @QMI_WDS_DATA_BEARER_TECHNOLOGY_HSDPA_HSUPDA: HSDPA and HSUPA. - * @QMI_WDS_DATA_BEARER_TECHNOLOGY_LTE: LTE. - * @QMI_WDS_DATA_BEARER_TECHNOLOGY_EHRPD: CDMA2000 eHRPD. - * @QMI_WDS_DATA_BEARER_TECHNOLOGY_HSDPAPLUS: HSDPA+ and WCDMA. - * @QMI_WDS_DATA_BEARER_TECHNOLOGY_HSDPAPLUS_HSUPA: HSDPA+ and HSUPA. - * @QMI_WDS_DATA_BEARER_TECHNOLOGY_DCHSDPAPLUS: DC-HSDPA+ and WCDMA. - * @QMI_WDS_DATA_BEARER_TECHNOLOGY_DCHSDPAPLUS_HSUPA: DC-HSDPA+ and HSUPA. - * - * Data bearer technology. - */ -typedef enum { - QMI_WDS_DATA_BEARER_TECHNOLOGY_UNKNOWN = -1, - QMI_WDS_DATA_BEARER_TECHNOLOGY_CDMA20001X = 0x01, - QMI_WDS_DATA_BEARER_TECHNOLOGY_1xEVDO = 0x02, - QMI_WDS_DATA_BEARER_TECHNOLOGY_GSM = 0x03, - QMI_WDS_DATA_BEARER_TECHNOLOGY_UMTS = 0x04, - QMI_WDS_DATA_BEARER_TECHNOLOGY_1xEVDO_REVA = 0x05, - QMI_WDS_DATA_BEARER_TECHNOLOGY_EDGE = 0x06, - QMI_WDS_DATA_BEARER_TECHNOLOGY_HSDPA = 0x07, - QMI_WDS_DATA_BEARER_TECHNOLOGY_HSUPA = 0x08, - QMI_WDS_DATA_BEARER_TECHNOLOGY_HSDPA_HSUPDA = 0x09, - QMI_WDS_DATA_BEARER_TECHNOLOGY_LTE = 0x0A, - QMI_WDS_DATA_BEARER_TECHNOLOGY_EHRPD = 0x0B, - QMI_WDS_DATA_BEARER_TECHNOLOGY_HSDPAPLUS = 0x0C, - QMI_WDS_DATA_BEARER_TECHNOLOGY_HSDPAPLUS_HSUPA = 0x0D, - QMI_WDS_DATA_BEARER_TECHNOLOGY_DCHSDPAPLUS = 0x0E, - QMI_WDS_DATA_BEARER_TECHNOLOGY_DCHSDPAPLUS_HSUPA = 0x0F -} QmiWdsDataBearerTechnology; - - -/*****************************************************************************/ -/* Helper enums for the 'QMI WDS Get Current Data Bearer Technology' message */ - -/** - * QmiWdsNetworkType: - * @QMI_WDS_NETWORK_TYPE_UNKNOWN: Unknown. - * @QMI_WDS_NETWORK_TYPE_3GPP2: 3GPP2 network type. - * @QMI_WDS_NETWORK_TYPE_3GPP: 3GPP network type. - * - * Network type of the data bearer. - */ -typedef enum { - QMI_WDS_NETWORK_TYPE_UNKNOWN = 0, - QMI_WDS_NETWORK_TYPE_3GPP2 = 1, - QMI_WDS_NETWORK_TYPE_3GPP = 2 -} QmiWdsNetworkType; - -/** - * QmiWdsRat3gpp2: - * @QMI_WDS_RAT_3GPP2_UNKNOWN: Unknown, to be ignored. - * @QMI_WDS_RAT_3GPP2_CDMA1X: CDMA 1x. - * @QMI_WDS_RAT_3GPP2_EVDO_REV0: EVDO Rev0. - * @QMI_WDS_RAT_3GPP2_EVDO_REVA: EVDO RevA. - * @QMI_WDS_RAT_3GPP2_EVDO_REVB: EVDO RevB. - * @QMI_WDS_RAT_3GPP2_NULL_BEARER: No bearer. - * - * Flags specifying the 3GPP2-specific Radio Access Technology, when the data - * bearer network type is @QMI_WDS_NETWORK_TYPE_3GPP2. - */ -typedef enum { /*< underscore_name=qmi_wds_rat_3gpp2 >*/ - QMI_WDS_RAT_3GPP2_NONE = 0, - QMI_WDS_RAT_3GPP2_CDMA1X = 1 << 0, - QMI_WDS_RAT_3GPP2_EVDO_REV0 = 1 << 1, - QMI_WDS_RAT_3GPP2_EVDO_REVA = 1 << 2, - QMI_WDS_RAT_3GPP2_EVDO_REVB = 1 << 3, - QMI_WDS_RAT_3GPP2_NULL_BEARER = 1 << 15 -} QmiWdsRat3gpp2; - -/** - * QmiWdsRat3gpp: - * @QMI_WDS_RAT_3GPP_NONE: Unknown, to be ignored. - * @QMI_WDS_RAT_3GPP_WCDMA: WCDMA. - * @QMI_WDS_RAT_3GPP_GPRS: GPRS. - * @QMI_WDS_RAT_3GPP_HSDPA: HSDPA. - * @QMI_WDS_RAT_3GPP_HSUPA: HSUPA. - * @QMI_WDS_RAT_3GPP_EDGE: EDGE. - * @QMI_WDS_RAT_3GPP_LTE: LTE. - * @QMI_WDS_RAT_3GPP_HSDPAPLUS: HSDPA+. - * @QMI_WDS_RAT_3GPP_DCHSDPAPLUS: DC-HSDPA+ - * @QMI_WDS_RAT_3GPP_NULL_BEARER: No bearer. - * - * Flags specifying the 3GPP-specific Radio Access Technology, when the data - * bearer network type is @QMI_WDS_NETWORK_TYPE_3GPP. - */ -typedef enum { /*< underscore_name=qmi_wds_rat_3gpp >*/ - QMI_WDS_RAT_3GPP_NONE = 0, - QMI_WDS_RAT_3GPP_WCDMA = 1 << 0, - QMI_WDS_RAT_3GPP_GPRS = 1 << 1, - QMI_WDS_RAT_3GPP_HSDPA = 1 << 2, - QMI_WDS_RAT_3GPP_HSUPA = 1 << 3, - QMI_WDS_RAT_3GPP_EDGE = 1 << 4, - QMI_WDS_RAT_3GPP_LTE = 1 << 5, - QMI_WDS_RAT_3GPP_HSDPAPLUS = 1 << 6, - QMI_WDS_RAT_3GPP_DCHSDPAPLUS = 1 << 7, - QMI_WDS_RAT_3GPP_NULL_BEARER = 1 << 15 -} QmiWdsRat3gpp; - -/** - * QmiWdsSoCdma1x: - * @QMI_WDS_SO_CDMA1X_NONE: Unknown, to be ignored. - * @QMI_WDS_SO_CDMA1X_IS95: IS95. - * @QMI_WDS_SO_CDMA1X_IS2000: IS2000. - * @QMI_WDS_SO_CDMA1X_IS2000_REL_A: IS2000 RelA. - * - * Flags specifying the Service Option when the bearer network type is - * @QMI_WDS_NETWORK_TYPE_3GPP2 and when the Radio Access Technology mask - * contains @QMI_WDS_RAT_3GPP2_CDMA1X. - */ -typedef enum { - QMI_WDS_SO_CDMA1X_NONE = 0, - QMI_WDS_SO_CDMA1X_IS95 = 1 << 0, - QMI_WDS_SO_CDMA1X_IS2000 = 1 << 1, - QMI_WDS_SO_CDMA1X_IS2000_REL_A = 1 << 2 -} QmiWdsSoCdma1x; - -/** - * QmiWdsSoEvdoRevA: - * @QMI_WDS_SO_EVDO_REVA_NONE: Unknown, to be ignored. - * @QMI_WDS_SO_EVDO_REVA_DPA: DPA. - * @QMI_WDS_SO_EVDO_REVA_MFPA: MFPA. - * @QMI_WDS_SO_EVDO_REVA_EMPA: EMPA. - * @QMI_WDS_SO_EVDO_REVA_EMPA_EHRPD: EMPA EHRPD. - * - * Flags specifying the Service Option when the bearer network type is - * @QMI_WDS_NETWORK_TYPE_3GPP2 and when the Radio Access Technology mask - * contains @QMI_WDS_RAT_3GPP2_EVDO_REVA. - */ -typedef enum { /*< underscore_name=qmi_wds_so_evdo_reva >*/ - QMI_WDS_SO_EVDO_REVA_NONE = 0, - QMI_WDS_SO_EVDO_REVA_DPA = 1 << 0, - QMI_WDS_SO_EVDO_REVA_MFPA = 1 << 1, - QMI_WDS_SO_EVDO_REVA_EMPA = 1 << 2, - QMI_WDS_SO_EVDO_REVA_EMPA_EHRPD = 1 << 3 -} QmiWdsSoEvdoRevA; - -#endif /* _LIBQMI_GLIB_QMI_ENUMS_WDS_H_ */ diff --git a/src/qmi-enums.h b/src/qmi-enums.h deleted file mode 100644 index 5710eba..0000000 --- a/src/qmi-enums.h +++ /dev/null @@ -1,86 +0,0 @@ -/* -*- 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 Aleksander Morgado - */ - -#include "qmi-enums-wds.h" - -#ifndef _LIBQMI_GLIB_QMI_ENUMS_H_ -#define _LIBQMI_GLIB_QMI_ENUMS_H_ - -typedef enum { - /* Unknown service */ - QMI_SERVICE_UNKNOWN = -1, - /* Control service */ - QMI_SERVICE_CTL = 0x00, - /* Wireless Data Service */ - QMI_SERVICE_WDS = 0x01, - /* Device Management Service */ - QMI_SERVICE_DMS = 0x02, - /* Network Access Service */ - QMI_SERVICE_NAS = 0x03, - /* Quality Of Service service */ - QMI_SERVICE_QOS = 0x04, - /* Wireless Messaging Service */ - QMI_SERVICE_WMS = 0x05, - /* Position Determination Service */ - QMI_SERVICE_PDS = 0x06, - /* Authentication service */ - QMI_SERVICE_AUTH = 0x07, - /* AT service */ - QMI_SERVICE_AT = 0x08, - /* Voice service */ - QMI_SERVICE_VOICE = 0x09, - - QMI_SERVICE_CAT2 = 0x0A, - QMI_SERVICE_UIM = 0x0B, - QMI_SERVICE_PBM = 0x0C, - QMI_SERVICE_LOC = 0x10, - QMI_SERVICE_SAR = 0x11, - QMI_SERVICE_RMTFS = 0x14, - - /* Card Application Toolkit service */ - QMI_SERVICE_CAT = 0xE0, - /* Remote Management Service */ - QMI_SERVICE_RMS = 0xE1, - /* Open Mobile Alliance device management service */ - QMI_SERVICE_OMA = 0xE2 -} QmiService; - -/*****************************************************************************/ -/* QMI Control */ - -typedef enum { - QMI_CTL_FLAG_NONE = 0, - QMI_CTL_FLAG_RESPONSE = 1 << 0, - QMI_CTL_FLAG_INDICATION = 1 << 1 -} QmiCtlFlag; - -/*****************************************************************************/ -/* QMI Services */ - -typedef enum { - QMI_SERVICE_FLAG_NONE = 0, - QMI_SERVICE_FLAG_COMPOUND = 1 << 0, - QMI_SERVICE_FLAG_RESPONSE = 1 << 1, - QMI_SERVICE_FLAG_INDICATION = 1 << 2 -} QmiServiceFlag; - -#endif /* _LIBQMI_GLIB_QMI_ENUMS_H_ */ diff --git a/src/qmi-errors.h b/src/qmi-errors.h deleted file mode 100644 index cff53be..0000000 --- a/src/qmi-errors.h +++ /dev/null @@ -1,212 +0,0 @@ -/* -*- 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 Aleksander Morgado - */ - -#ifndef _LIBQMI_GLIB_QMI_ERRORS_H_ -#define _LIBQMI_GLIB_QMI_ERRORS_H_ - -/** - * QmiCoreError: - * @QMI_CORE_ERROR_FAILED: Operation failed. - * @QMI_CORE_ERROR_WRONG_STATE: Operation cannot be executed in the current state. - * @QMI_CORE_ERROR_TIMEOUT: Operation timed out. - * @QMI_CORE_ERROR_INVALID_ARGS: Invalid arguments given. - * @QMI_CORE_ERROR_INVALID_MESSAGE: QMI message is invalid. - * @QMI_CORE_ERROR_TLV_NOT_FOUND: TLV not found. - * @QMI_CORE_ERROR_TLV_TOO_LONG: TLV is too long. - * @QMI_CORE_ERROR_UNSUPPORTED: Not supported. - * - * Common errors that may be reported by libqmi-glib. - */ -typedef enum { - QMI_CORE_ERROR_FAILED, - QMI_CORE_ERROR_WRONG_STATE, - QMI_CORE_ERROR_TIMEOUT, - QMI_CORE_ERROR_INVALID_ARGS, - QMI_CORE_ERROR_INVALID_MESSAGE, - QMI_CORE_ERROR_TLV_NOT_FOUND, - QMI_CORE_ERROR_TLV_TOO_LONG, - QMI_CORE_ERROR_UNSUPPORTED -} QmiCoreError; - -/** - * QmiProtocolError: - * @QMI_PROTOCOL_ERROR_NONE: No error. - * @QMI_PROTOCOL_ERROR_MALFORMED_MESSAGE: Malformed message. - * @QMI_PROTOCOL_ERROR_NO_MEMORY: No memory. - * @QMI_PROTOCOL_ERROR_INTERNAL: Internal. - * @QMI_PROTOCOL_ERROR_ABORTED: Aborted. - * @QMI_PROTOCOL_ERROR_CLIENT_IDS_EXHAUSTED: Client IDs exhausted. - * @QMI_PROTOCOL_ERROR_UNABORTABLE_TRANSACTION: Unabortable transaction. - * @QMI_PROTOCOL_ERROR_INVALID_CLIENT_ID: Invalid client ID. - * @QMI_PROTOCOL_ERROR_NO_THRESHOLDS_PROVIDED: No thresholds provided. - * @QMI_PROTOCOL_ERROR_INVALID_HANDLE: Invalid handle. - * @QMI_PROTOCOL_ERROR_INVALID_PROFILE: Invalid profile. - * @QMI_PROTOCOL_ERROR_INVALID_PIN_ID: Invalid PIN ID. - * @QMI_PROTOCOL_ERROR_INCORRECT_PIN: Incorrect PIN. - * @QMI_PROTOCOL_ERROR_NO_NETWORK_FOUND: No network found. - * @QMI_PROTOCOL_ERROR_CALL_FAILED: Call failed. - * @QMI_PROTOCOL_ERROR_OUT_OF_CALL: Out of call. - * @QMI_PROTOCOL_ERROR_NOT_PROVISIONED: Not provisioned. - * @QMI_PROTOCOL_ERROR_MISSING_ARGUMENT: Missing argument. - * @QMI_PROTOCOL_ERROR_ARGUMENT_TOO_LONG: Argument too long. - * @QMI_PROTOCOL_ERROR_INVALID_TRANSACTION_ID: Invalid transaction ID. - * @QMI_PROTOCOL_ERROR_DEVICE_IN_USE: Device in use. - * @QMI_PROTOCOL_ERROR_NETWORK_UNSUPPORTED: Network unsupported. - * @QMI_PROTOCOL_ERROR_DEVICE_UNSUPPORTED Device unsupported. - * @QMI_PROTOCOL_ERROR_NO_EFFECT: No effect. - * @QMI_PROTOCOL_ERROR_NO_FREE_PROFILE: No free profile. - * @QMI_PROTOCOL_ERROR_INVALID_PDP_TYPE: Invalid PDP type. - * @QMI_PROTOCOL_ERROR_INVALID_TECHNOLOGY_PREFERENCE: Invalid technology preference. - * @QMI_PROTOCOL_ERROR_INVALID_PROFILE_TYPE: Invalid profile type. - * @QMI_PROTOCOL_ERROR_INVALID_SERVICE_TYPE: Invalid service type. - * @QMI_PROTOCOL_ERROR_INVALID_REGISTER_ACTION: Invalid register action. - * @QMI_PROTOCOL_ERROR_INVALID_PS_ATTACH_ACTION: Invalid PS attach action. - * @QMI_PROTOCOL_ERROR_AUTHENTICATION_FAILED: Authentication failed. - * @QMI_PROTOCOL_ERROR_PIN_BLOCKED: PIN blocked. - * @QMI_PROTOCOL_ERROR_PIN_ALWAYS_BLOCKED: PIN always blocked. - * @QMI_PROTOCOL_ERROR_UIM_UNINITIALIZED: UIM uninitialized. - * @QMI_PROTOCOL_ERROR_MAXIMUM_QOS_REQUESTS_IN_USE: Maximum QoS requests in use. - * @QMI_PROTOCOL_ERROR_INCORRECT_FLOW_FILTER: Incorrect flow filter. - * @QMI_PROTOCOL_ERROR_NETWORK_QOS_UNAWARE: Network QoS unaware. - * @QMI_PROTOCOL_ERROR_INVALID_QOS_ID: Invalid QoS ID. - * @QMI_PROTOCOL_ERROR_QOS_UNAVAILABLE: QoS unavailable. - * @QMI_PROTOCOL_ERROR_FLOW_SUSPENDED: Flow suspended. - * @QMI_PROTOCOL_ERROR_GENERAL_ERROR: General error. - * @QMI_PROTOCOL_ERROR_UNKNOWN_ERROR: Unknown error. - * @QMI_PROTOCOL_ERROR_INVALID_ARGUMENT: Invalid argument. - * @QMI_PROTOCOL_ERROR_INVALID_INDEX: Invalid index. - * @QMI_PROTOCOL_ERROR_NO_ENTRY: No entry. - * @QMI_PROTOCOL_ERROR_DEVICE_STORAGE_FULL: Device storage full. - * @QMI_PROTOCOL_ERROR_DEVICE_NOT_READY: Device not ready. - * @QMI_PROTOCOL_ERROR_NETWORK_NOT_READY: Network not ready. - * @QMI_PROTOCOL_ERROR_WMS_CAUSE_CODE: WMS cause code. - * @QMI_PROTOCOL_ERROR_WMS_MESSAGE_NOT_SENT: WMS message not sent. - * @QMI_PROTOCOL_ERROR_WMS_MESSAGE_DELIVERY_FAILURE: WMS message delivery failure. - * @QMI_PROTOCOL_ERROR_WMS_INVALID_MESSAGE_ID: WMS invalid message ID. - * @QMI_PROTOCOL_ERROR_WMS_ENCODING: WMS encoding. - * @QMI_PROTOCOL_ERROR_AUTHENTICATION_LOCK: Authentication lock. - * @QMI_PROTOCOL_ERROR_INVALID_TRANSITION: Invalid transition. - * @QMI_PROTOCOL_ERROR_SESSION_INACTIVE: Session inactive. - * @QMI_PROTOCOL_ERROR_SESSION_INVALID: Session invalid. - * @QMI_PROTOCOL_ERROR_SESSION_OWNERSHIP: Session ownership. - * @QMI_PROTOCOL_ERROR_INSUFFICIENT_RESOURCES: Insufficient resources. - * @QMI_PROTOCOL_ERROR_DISABLED: Disabled. - * @QMI_PROTOCOL_ERROR_INVALID_OPERATION: Invalid operation. - * @QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND: Invalid QMI command. - * @QMI_PROTOCOL_ERROR_WMS_TPDU_TYPE: WMS TPDU type. - * @QMI_PROTOCOL_ERROR_WMS_SMSC_ADDRESS: WMS SMSC address. - * @QMI_PROTOCOL_ERROR_INFORMATION_UNAVAILABLE: Information unavailable. - * @QMI_PROTOCOL_ERROR_SEGMENT_TOO_LONG: Segment too long. - * @QMI_PROTOCOL_ERROR_SEGMENT_ORDER: Segment order. - * @QMI_PROTOCOL_ERROR_BUNDLING_NOT_SUPPORTED: Bundling not supported. - * @QMI_PROTOCOL_ERROR_SIM_FILE_NOT_FOUND: SIM file not found. - * @QMI_PROTOCOL_ERROR_ACCESS_DENIED: Access denied. - * @QMI_PROTOCOL_ERROR_HARDWARE_RESTRICTED: Hardware restricted. - * @QMI_PROTOCOL_ERROR_CAT_EVENT_REGISTRATION_FAILED: Event registration failed. - * @QMI_PROTOCOL_ERROR_CAT_INVALID_TERMINAL_RESPONSE: Invalid terminal response. - * @QMI_PROTOCOL_ERROR_CAT_INVALID_ENVELOPE_COMMAND: Invalid envelope command. - * @QMI_PROTOCOL_ERROR_CAT_ENVELOPE_COMMAND_BUSY: Envelope command busy. - * @QMI_PROTOCOL_ERROR_CAT_ENVELOPE_COMMAND_FAILED: Envelope command failed. - * - * QMI protocol errors. - */ -typedef enum { - QMI_PROTOCOL_ERROR_NONE = 0, - QMI_PROTOCOL_ERROR_MALFORMED_MESSAGE = 1, - QMI_PROTOCOL_ERROR_NO_MEMORY = 2, - QMI_PROTOCOL_ERROR_INTERNAL = 3, - QMI_PROTOCOL_ERROR_ABORTED = 4, - QMI_PROTOCOL_ERROR_CLIENT_IDS_EXHAUSTED = 5, - QMI_PROTOCOL_ERROR_UNABORTABLE_TRANSACTION = 6, - QMI_PROTOCOL_ERROR_INVALID_CLIENT_ID = 7, - QMI_PROTOCOL_ERROR_NO_THRESHOLDS_PROVIDED = 8, - QMI_PROTOCOL_ERROR_INVALID_HANDLE = 9, - QMI_PROTOCOL_ERROR_INVALID_PROFILE = 10, - QMI_PROTOCOL_ERROR_INVALID_PIN_ID = 11, - QMI_PROTOCOL_ERROR_INCORRECT_PIN = 12, - QMI_PROTOCOL_ERROR_NO_NETWORK_FOUND = 13, - QMI_PROTOCOL_ERROR_CALL_FAILED = 14, - QMI_PROTOCOL_ERROR_OUT_OF_CALL = 15, - QMI_PROTOCOL_ERROR_NOT_PROVISIONED = 16, - QMI_PROTOCOL_ERROR_MISSING_ARGUMENT = 17, - QMI_PROTOCOL_ERROR_ARGUMENT_TOO_LONG = 19, - QMI_PROTOCOL_ERROR_INVALID_TRANSACTION_ID = 22, - QMI_PROTOCOL_ERROR_DEVICE_IN_USE = 23, - QMI_PROTOCOL_ERROR_NETWORK_UNSUPPORTED = 24, - QMI_PROTOCOL_ERROR_DEVICE_UNSUPPORTED = 25, - QMI_PROTOCOL_ERROR_NO_EFFECT = 26, - QMI_PROTOCOL_ERROR_NO_FREE_PROFILE = 27, - QMI_PROTOCOL_ERROR_INVALID_PDP_TYPE = 28, - QMI_PROTOCOL_ERROR_INVALID_TECHNOLOGY_PREFERENCE = 29, - QMI_PROTOCOL_ERROR_INVALID_PROFILE_TYPE = 30, - QMI_PROTOCOL_ERROR_INVALID_SERVICE_TYPE = 31, - QMI_PROTOCOL_ERROR_INVALID_REGISTER_ACTION = 32, - QMI_PROTOCOL_ERROR_INVALID_PS_ATTACH_ACTION = 33, - QMI_PROTOCOL_ERROR_AUTHENTICATION_FAILED = 34, - QMI_PROTOCOL_ERROR_PIN_BLOCKED = 35, - QMI_PROTOCOL_ERROR_PIN_ALWAYS_BLOCKED = 36, - QMI_PROTOCOL_ERROR_UIM_UNINITIALIZED = 37, - QMI_PROTOCOL_ERROR_MAXIMUM_QOS_REQUESTS_IN_USE = 38, - QMI_PROTOCOL_ERROR_INCORRECT_FLOW_FILTER = 39, - QMI_PROTOCOL_ERROR_NETWORK_QOS_UNAWARE = 40, - QMI_PROTOCOL_ERROR_INVALID_QOS_ID = 41, - QMI_PROTOCOL_ERROR_QOS_UNAVAILABLE = 42, - QMI_PROTOCOL_ERROR_FLOW_SUSPENDED = 43, - QMI_PROTOCOL_ERROR_GENERAL_ERROR = 46, - QMI_PROTOCOL_ERROR_UNKNOWN_ERROR = 47, - QMI_PROTOCOL_ERROR_INVALID_ARGUMENT = 48, - QMI_PROTOCOL_ERROR_INVALID_INDEX = 49, - QMI_PROTOCOL_ERROR_NO_ENTRY = 50, - QMI_PROTOCOL_ERROR_DEVICE_STORAGE_FULL = 51, - QMI_PROTOCOL_ERROR_DEVICE_NOT_READY = 52, - QMI_PROTOCOL_ERROR_NETWORK_NOT_READY = 53, - QMI_PROTOCOL_ERROR_WMS_CAUSE_CODE = 54, - QMI_PROTOCOL_ERROR_WMS_MESSAGE_NOT_SENT = 55, - QMI_PROTOCOL_ERROR_WMS_MESSAGE_DELIVERY_FAILURE = 56, - QMI_PROTOCOL_ERROR_WMS_INVALID_MESSAGE_ID = 57, - QMI_PROTOCOL_ERROR_WMS_ENCODING = 58, - QMI_PROTOCOL_ERROR_AUTHENTICATION_LOCK = 59, - QMI_PROTOCOL_ERROR_INVALID_TRANSITION = 60, - QMI_PROTOCOL_ERROR_SESSION_INACTIVE = 65, - QMI_PROTOCOL_ERROR_SESSION_INVALID = 66, - QMI_PROTOCOL_ERROR_SESSION_OWNERSHIP = 67, - QMI_PROTOCOL_ERROR_INSUFFICIENT_RESOURCES = 68, - QMI_PROTOCOL_ERROR_DISABLED = 69, - QMI_PROTOCOL_ERROR_INVALID_OPERATION = 70, - QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND = 71, - QMI_PROTOCOL_ERROR_WMS_T_PDU_TYPE = 72, - QMI_PROTOCOL_ERROR_WMS_SMSC_ADDRESS = 73, - QMI_PROTOCOL_ERROR_INFORMATION_UNAVAILABLE = 74, - QMI_PROTOCOL_ERROR_SEGMENT_TOO_LONG = 75, - QMI_PROTOCOL_ERROR_SEGMENT_ORDER = 76, - QMI_PROTOCOL_ERROR_BUNDLING_NOT_SUPPORTED = 77, - QMI_PROTOCOL_ERROR_SIM_FILE_NOT_FOUND = 80, - QMI_PROTOCOL_ERROR_ACCESS_DENIED = 82, - QMI_PROTOCOL_ERROR_HARDWARE_RESTRICTED = 83, - QMI_PROTOCOL_ERROR_CAT_EVENT_REGISTRATION_FAILED = 61441, - QMI_PROTOCOL_ERROR_CAT_INVALID_TERMINAL_RESPONSE = 61442, - QMI_PROTOCOL_ERROR_CAT_INVALID_ENVELOPE_COMMAND = 61443, - QMI_PROTOCOL_ERROR_CAT_ENVELOPE_COMMAND_BUSY = 61444, - QMI_PROTOCOL_ERROR_CAT_ENVELOPE_COMMAND_FAILED = 61445 -} QmiProtocolError; - -#endif /* _LIBQMI_GLIB_QMI_ERRORS_H_ */ diff --git a/src/qmi-message.c b/src/qmi-message.c deleted file mode 100644 index 8d3abee..0000000 --- a/src/qmi-message.c +++ /dev/null @@ -1,707 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file of the libqmi library. - */ - -/* - * 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 Aleksander Morgado - */ - -#include -#include -#include -#include -#include - -#include "qmi-message.h" -#include "qmi-utils.h" -#include "qmi-enum-types.h" -#include "qmi-error-types.h" - -#include "qmi-ctl.h" -#include "qmi-dms.h" -#include "qmi-wds.h" - -#define PACKED __attribute__((packed)) - -struct qmux { - guint16 length; - guint8 flags; - guint8 service; - guint8 client; -} PACKED; - -struct control_header { - guint8 flags; - guint8 transaction; - guint16 message; - guint16 tlv_length; -} PACKED; - -struct service_header { - guint8 flags; - guint16 transaction; - guint16 message; - guint16 tlv_length; -} PACKED; - -struct tlv { - guint8 type; - guint16 length; - guint8 value[]; -} PACKED; - -struct control_message { - struct control_header header; - struct tlv tlv[]; -} PACKED; - -struct service_message { - struct service_header header; - struct tlv tlv[]; -} PACKED; - -struct full_message { - guint8 marker; - struct qmux qmux; - union { - struct control_message control; - struct service_message service; - } qmi; -} PACKED; - -struct _QmiMessage { - /* TODO: avoid memory split here */ - struct full_message *buf; /* buf allocated using g_malloc, not g_slice_alloc */ - gsize len; /* cached size of *buf; not part of message. */ - volatile gint ref_count; /* the ref count */ -}; - -guint16 -qmi_message_get_qmux_length (QmiMessage *self) -{ - return GUINT16_FROM_LE (self->buf->qmux.length); -} - -static inline void -set_qmux_length (QmiMessage *self, - guint16 length) -{ - self->buf->qmux.length = GUINT16_TO_LE (length); -} - -gboolean -qmi_message_is_control (QmiMessage *self) -{ - g_return_val_if_fail (self != NULL, FALSE); - - return self->buf->qmux.service == QMI_SERVICE_CTL; -} - -guint8 -qmi_message_get_qmux_flags (QmiMessage *self) -{ - g_return_val_if_fail (self != NULL, 0); - - return self->buf->qmux.flags; -} - -QmiService -qmi_message_get_service (QmiMessage *self) -{ - g_return_val_if_fail (self != NULL, QMI_SERVICE_UNKNOWN); - - return (QmiService)self->buf->qmux.service; -} - -guint8 -qmi_message_get_client_id (QmiMessage *self) -{ - g_return_val_if_fail (self != NULL, 0); - - return self->buf->qmux.client; -} - -guint8 -qmi_message_get_qmi_flags (QmiMessage *self) -{ - g_return_val_if_fail (self != NULL, 0); - - if (qmi_message_is_control (self)) - return self->buf->qmi.control.header.flags; - - return self->buf->qmi.service.header.flags; -} - -gboolean -qmi_message_is_response (QmiMessage *self) -{ - if (qmi_message_is_control (self)) { - if (self->buf->qmi.control.header.flags & QMI_CTL_FLAG_RESPONSE) - return TRUE; - } else { - if (self->buf->qmi.service.header.flags & QMI_SERVICE_FLAG_RESPONSE) - return TRUE; - } - - return FALSE; -} - -gboolean -qmi_message_is_indication (QmiMessage *self) -{ - if (qmi_message_is_control (self)) { - if (self->buf->qmi.control.header.flags & QMI_CTL_FLAG_INDICATION) - return TRUE; - } else { - if (self->buf->qmi.service.header.flags & QMI_SERVICE_FLAG_INDICATION) - return TRUE; - } - - return FALSE; -} - -guint16 -qmi_message_get_transaction_id (QmiMessage *self) -{ - g_return_val_if_fail (self != NULL, 0); - - if (qmi_message_is_control (self)) - /* note: only 1 byte for transaction in CTL message */ - return (guint16)self->buf->qmi.control.header.transaction; - - return le16toh (self->buf->qmi.service.header.transaction); -} - -guint16 -qmi_message_get_message_id (QmiMessage *self) -{ - g_return_val_if_fail (self != NULL, 0); - - if (qmi_message_is_control (self)) - return le16toh (self->buf->qmi.control.header.message); - - return le16toh (self->buf->qmi.service.header.message); -} - -gsize -qmi_message_get_length (QmiMessage *self) -{ - g_return_val_if_fail (self != NULL, 0); - - return self->len; -} - -guint16 -qmi_message_get_tlv_length (QmiMessage *self) -{ - if (qmi_message_is_control (self)) - return GUINT16_FROM_LE (self->buf->qmi.control.header.tlv_length); - - return GUINT16_FROM_LE (self->buf->qmi.service.header.tlv_length); -} - -static void -set_qmi_message_get_tlv_length (QmiMessage *self, - guint16 length) -{ - if (qmi_message_is_control (self)) - self->buf->qmi.control.header.tlv_length = GUINT16_TO_LE (length); - else - self->buf->qmi.service.header.tlv_length = GUINT16_TO_LE (length); -} - -static struct tlv * -qmi_tlv (QmiMessage *self) -{ - if (qmi_message_is_control (self)) - return self->buf->qmi.control.tlv; - - return self->buf->qmi.service.tlv; -} - -static guint8 * -qmi_end (QmiMessage *self) -{ - return (guint8 *) self->buf + self->len; -} - -static struct tlv * -tlv_next (struct tlv *tlv) -{ - return (struct tlv *)((guint8 *)tlv + sizeof(struct tlv) + le16toh (tlv->length)); -} - -static struct tlv * -qmi_tlv_first (QmiMessage *self) -{ - if (qmi_message_get_tlv_length (self)) - return qmi_tlv (self); - - return NULL; -} - -static struct tlv * -qmi_tlv_next (QmiMessage *self, - struct tlv *tlv) -{ - struct tlv *end; - struct tlv *next; - - end = (struct tlv *) qmi_end (self); - next = tlv_next (tlv); - - return (next < end ? next : NULL); -} - -/** - * Checks the validity of a QMI message. - * - * In particular, checks: - * 1. The message has space for all required headers. - * 2. The length of the buffer, the qmux length field, and the QMI tlv_length - * field are all consistent. - * 3. The TLVs in the message fit exactly in the payload size. - * - * Returns non-zero if the message is valid, zero if invalid. - */ -gboolean -qmi_message_check (QmiMessage *self, - GError **error) -{ - gsize header_length; - guint8 *end; - struct tlv *tlv; - - g_assert (self != NULL); - g_assert (self->buf != NULL); - - if (self->buf->marker != QMI_MESSAGE_QMUX_MARKER) { - g_set_error (error, - QMI_CORE_ERROR, - QMI_CORE_ERROR_INVALID_MESSAGE, - "Marker is incorrect"); - return FALSE; - } - - if (qmi_message_get_qmux_length (self) < sizeof (struct qmux)) { - g_set_error (error, - QMI_CORE_ERROR, - QMI_CORE_ERROR_INVALID_MESSAGE, - "QMUX length too short for QMUX header (%u < %" G_GSIZE_FORMAT ")", - qmi_message_get_qmux_length (self), sizeof (struct qmux)); - return FALSE; - } - - /* - * qmux length is one byte shorter than buffer length because qmux - * length does not include the qmux frame marker. - */ - if (qmi_message_get_qmux_length (self) != self->len - 1) { - g_set_error (error, - QMI_CORE_ERROR, - QMI_CORE_ERROR_INVALID_MESSAGE, - "QMUX length and buffer length don't match (%u != %" G_GSIZE_FORMAT ")", - qmi_message_get_qmux_length (self), self->len - 1); - return FALSE; - } - - header_length = sizeof (struct qmux) + (qmi_message_is_control (self) ? - sizeof (struct control_header) : - sizeof (struct service_header)); - - if (qmi_message_get_qmux_length (self) < header_length) { - g_set_error (error, - QMI_CORE_ERROR, - QMI_CORE_ERROR_INVALID_MESSAGE, - "QMUX length too short for QMI header (%u < %" G_GSIZE_FORMAT ")", - qmi_message_get_qmux_length (self), header_length); - return FALSE; - } - - if (qmi_message_get_qmux_length (self) - header_length != qmi_message_get_tlv_length (self)) { - g_set_error (error, - QMI_CORE_ERROR, - QMI_CORE_ERROR_INVALID_MESSAGE, - "QMUX length and QMI TLV lengths don't match (%u - %" G_GSIZE_FORMAT " != %u)", - qmi_message_get_qmux_length (self), header_length, qmi_message_get_tlv_length (self)); - return FALSE; - } - - end = qmi_end (self); - for (tlv = qmi_tlv (self); tlv < (struct tlv *)end; tlv = tlv_next (tlv)) { - if (tlv->value > end) { - g_set_error (error, - QMI_CORE_ERROR, - QMI_CORE_ERROR_INVALID_MESSAGE, - "TLV header runs over buffer (%p > %p)", - tlv->value, end); - return FALSE; - } - if (tlv->value + le16toh (tlv->length) > end) { - g_set_error (error, - QMI_CORE_ERROR, - QMI_CORE_ERROR_INVALID_MESSAGE, - "TLV value runs over buffer (%p + %u > %p)", - tlv->value, le16toh (tlv->length), end); - return FALSE; - } - } - - /* - * If this assert triggers, one of the if statements in the loop is wrong. - * (It shouldn't be reached on malformed QMI messages.) - */ - g_assert (tlv == (struct tlv *)end); - - return TRUE; -} - -QmiMessage * -qmi_message_new (QmiService service, - guint8 client_id, - guint16 transaction_id, - guint16 message_id) -{ - QmiMessage *self; - - /* Transaction ID in the control service is 8bit only */ - g_assert (service != QMI_SERVICE_CTL || - transaction_id <= G_MAXUINT8); - - self = g_slice_new (QmiMessage); - self->ref_count = 1; - - self->len = 1 + sizeof (struct qmux) + (service == QMI_SERVICE_CTL ? - sizeof (struct control_header) : - sizeof (struct service_header)); - - /* TODO: Allocate both the message and the buffer together */ - self->buf = g_malloc (self->len); - - self->buf->marker = QMI_MESSAGE_QMUX_MARKER; - self->buf->qmux.flags = 0; - self->buf->qmux.service = service; - self->buf->qmux.client = client_id; - set_qmux_length (self, self->len - 1); - - if (service == QMI_SERVICE_CTL) { - self->buf->qmi.control.header.flags = 0; - self->buf->qmi.control.header.transaction = (guint8)transaction_id; - self->buf->qmi.control.header.message = htole16 (message_id); - } else { - self->buf->qmi.service.header.flags = 0; - self->buf->qmi.service.header.transaction = htole16 (transaction_id); - self->buf->qmi.service.header.message = htole16 (message_id); - } - - set_qmi_message_get_tlv_length (self, 0); - - g_assert (qmi_message_check (self, NULL)); - - return self; -} - -QmiMessage * -qmi_message_ref (QmiMessage *self) -{ - g_assert (self != NULL); - - g_atomic_int_inc (&self->ref_count); - return self; -} - -void -qmi_message_unref (QmiMessage *self) -{ - g_assert (self != NULL); - - if (g_atomic_int_dec_and_test (&self->ref_count)) { - g_free (self->buf); - g_slice_free (QmiMessage, self); - } -} - -gconstpointer -qmi_message_get_raw (QmiMessage *self, - gsize *len, - GError **error) -{ - g_assert (self != NULL); - g_assert (len != NULL); - - if (!qmi_message_check (self, error)) - return NULL; - - *len = self->len; - return self->buf; -} - -gboolean -qmi_message_tlv_get (QmiMessage *self, - guint8 type, - guint16 *length, - guint8 **value, - GError **error) -{ - struct tlv *tlv; - - g_assert (self != NULL); - g_assert (self->buf != NULL); - g_assert (length != NULL); - /* note: we allow querying only for the exact length */ - - for (tlv = qmi_tlv_first (self); tlv; tlv = qmi_tlv_next (self, tlv)) { - if (tlv->type == type) { - *length = GUINT16_FROM_LE (tlv->length); - if (value) - *value = &(tlv->value[0]); - return TRUE; - } - } - - g_set_error (error, - QMI_CORE_ERROR, - QMI_CORE_ERROR_TLV_TOO_LONG, - "TLV not found"); - return FALSE; -} - -void -qmi_message_tlv_foreach (QmiMessage *self, - QmiMessageForeachTlvFn callback, - gpointer user_data) -{ - struct tlv *tlv; - - g_assert (self != NULL); - g_assert (callback != NULL); - - for (tlv = qmi_tlv_first (self); tlv; tlv = qmi_tlv_next (self, tlv)) { - callback (tlv->type, - (gsize)(le16toh (tlv->length)), - (gconstpointer)tlv->value, - user_data); - } -} - -gboolean -qmi_message_tlv_add (QmiMessage *self, - guint8 type, - gsize length, - gconstpointer value, - GError **error) -{ - size_t tlv_len; - struct tlv *tlv; - - g_assert (self != NULL); - g_assert ((length == 0) || value != NULL); - - /* Make sure nothing's broken to start. */ - if (!qmi_message_check (self, error)) { - g_prefix_error (error, "Invalid QMI message detected: "); - return FALSE; - } - - /* Find length of new TLV. */ - tlv_len = sizeof (struct tlv) + length; - - /* Check for overflow of message size. */ - if (qmi_message_get_qmux_length (self) + tlv_len > UINT16_MAX) { - g_set_error (error, - QMI_CORE_ERROR, - QMI_CORE_ERROR_TLV_TOO_LONG, - "TLV to add is too long"); - return FALSE; - } - - /* Resize buffer. */ - self->len += tlv_len; - self->buf = g_realloc (self->buf, self->len); - - /* Fill in new TLV. */ - tlv = (struct tlv *)(qmi_end (self) - tlv_len); - tlv->type = type; - tlv->length = htole16 (length); - if (value) - memcpy (tlv->value, value, length); - - /* Update length fields. */ - set_qmux_length (self, (guint16)(qmi_message_get_qmux_length (self) + tlv_len)); - set_qmi_message_get_tlv_length (self, (guint16)(qmi_message_get_tlv_length(self) + tlv_len)); - - /* Make sure we didn't break anything. */ - if (!qmi_message_check (self, error)) { - g_prefix_error (error, "Invalid QMI message built: "); - return FALSE; - } - - return TRUE; -} - -QmiMessage * -qmi_message_new_from_raw (const guint8 *raw, - gsize raw_len) -{ - QmiMessage *self; - gsize message_len; - - /* If we didn't even read the header, leave */ - if (raw_len < (sizeof (struct qmux) + 1)) - return NULL; - - /* We need to have read the length reported by the header. - * Otherwise, return. */ - message_len = le16toh (((struct full_message *)raw)->qmux.length); - if (raw_len < (message_len - 1)) - return NULL; - - /* Ok, so we should have all the data available already */ - self = g_slice_new (QmiMessage); - self->ref_count = 1; - self->len = message_len + 1; - self->buf = g_malloc (self->len); - memcpy (self->buf, raw, self->len); - - /* NOTE: we don't check if the message is valid here, let the caller do it */ - - return self; -} - -gchar * -qmi_message_get_tlv_printable (QmiMessage *self, - const gchar *line_prefix, - guint8 type, - gsize length, - gconstpointer value) -{ - gchar *printable; - gchar *value_hex; - - value_hex = qmi_utils_str_hex (value, length, ':'); - printable = g_strdup_printf ("%sTLV:\n" - "%s type = 0x%02x\n" - "%s length = %" G_GSIZE_FORMAT "\n" - "%s value = %s\n", - line_prefix, - line_prefix, type, - line_prefix, length, - line_prefix, value_hex); - g_free (value_hex); - return printable; -} - -static gchar * -get_generic_printable (QmiMessage *self, - const gchar *line_prefix) -{ - GString *printable; - struct tlv *tlv; - - printable = g_string_new (""); - - g_string_append_printf (printable, - "%s message = (0x%04x)\n", - line_prefix, qmi_message_get_message_id (self)); - - for (tlv = qmi_tlv_first (self); tlv; tlv = qmi_tlv_next (self, tlv)) { - gchar *printable_tlv; - - printable_tlv = qmi_message_get_tlv_printable (self, - line_prefix, - tlv->type, - tlv->length, - tlv->value); - g_string_append (printable, printable_tlv); - g_free (printable_tlv); - } - - return g_string_free (printable, FALSE); -} - -gchar * -qmi_message_get_printable (QmiMessage *self, - const gchar *line_prefix) -{ - GString *printable; - gchar *qmi_flags_str; - gchar *contents; - - if (!qmi_message_check (self, NULL)) - return NULL; - - if (!line_prefix) - line_prefix = ""; - - printable = g_string_new (""); - g_string_append_printf (printable, - "%sQMUX:\n" - "%s length = %u\n" - "%s flags = 0x%02x\n" - "%s service = \"%s\"\n" - "%s client = %u\n", - line_prefix, - line_prefix, qmi_message_get_qmux_length (self), - line_prefix, qmi_message_get_qmux_flags (self), - line_prefix, qmi_service_get_string (qmi_message_get_service (self)), - line_prefix, qmi_message_get_client_id (self)); - - if (qmi_message_get_service (self) == QMI_SERVICE_CTL) - qmi_flags_str = qmi_ctl_flag_build_string_from_mask (qmi_message_get_qmi_flags (self)); - else - qmi_flags_str = qmi_service_flag_build_string_from_mask (qmi_message_get_qmi_flags (self)); - - g_string_append_printf (printable, - "%sQMI:\n" - "%s flags = \"%s\"\n" - "%s transaction = %u\n" - "%s tlv_length = %u\n", - line_prefix, - line_prefix, qmi_flags_str, - line_prefix, qmi_message_get_transaction_id (self), - line_prefix, qmi_message_get_tlv_length (self)); - g_free (qmi_flags_str); - - contents = NULL; - switch (qmi_message_get_service (self)) { - case QMI_SERVICE_CTL: - contents = qmi_message_ctl_get_printable (self, line_prefix); - break; - case QMI_SERVICE_DMS: - contents = qmi_message_dms_get_printable (self, line_prefix); - break; - case QMI_SERVICE_WDS: - contents = qmi_message_wds_get_printable (self, line_prefix); - break; - default: - break; - } - - if (!contents) - contents = get_generic_printable (self, line_prefix); - g_string_append (printable, contents); - g_free (contents); - - return g_string_free (printable, FALSE); -} diff --git a/src/qmi-message.h b/src/qmi-message.h deleted file mode 100644 index ff64a91..0000000 --- a/src/qmi-message.h +++ /dev/null @@ -1,104 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/* - * 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 Aleksander Morgado - */ - -#ifndef _LIBQMI_GLIB_QMI_MESSAGE_H_ -#define _LIBQMI_GLIB_QMI_MESSAGE_H_ - -#include - -#include "qmi-enums.h" - -G_BEGIN_DECLS - -#define QMI_MESSAGE_QMUX_MARKER (guint8)0x01 -typedef struct _QmiMessage QmiMessage; - -QmiMessage *qmi_message_new (QmiService service, - guint8 client_id, - guint16 transaction_id, - guint16 message_id); -QmiMessage *qmi_message_new_from_raw (const guint8 *raw, - gsize raw_len); -QmiMessage *qmi_message_ref (QmiMessage *self); -void qmi_message_unref (QmiMessage *self); - -typedef void (* QmiMessageForeachTlvFn) (guint8 type, - gsize length, - gconstpointer value, - gpointer user_data); -void qmi_message_tlv_foreach (QmiMessage *self, - QmiMessageForeachTlvFn callback, - gpointer user_data); - -gboolean qmi_message_tlv_get (QmiMessage *self, - guint8 type, - guint16 *length, - guint8 **value, - GError **error); - -gboolean qmi_message_tlv_add (QmiMessage *self, - guint8 type, - gsize length, - gconstpointer value, - GError **error); - -gconstpointer qmi_message_get_raw (QmiMessage *self, - gsize *length, - GError **error); - -guint16 qmi_message_get_qmux_length (QmiMessage *self); -guint16 qmi_message_get_tlv_length (QmiMessage *self); -gsize qmi_message_get_length (QmiMessage *self); - -gchar *qmi_message_get_printable (QmiMessage *self, - const gchar *line_prefix); - -gchar *qmi_message_get_tlv_printable (QmiMessage *self, - const gchar *line_prefix, - guint8 type, - gsize length, - gconstpointer value); - -gboolean qmi_message_check (QmiMessage *self, - GError **error); - -gboolean qmi_message_is_control (QmiMessage *self); -gboolean qmi_message_is_response (QmiMessage *self); -gboolean qmi_message_is_indication (QmiMessage *self); - -guint16 qmi_message_get_message_id (QmiMessage *self); -QmiService qmi_message_get_service (QmiMessage *self); -guint8 qmi_message_get_client_id (QmiMessage *self); -guint16 qmi_message_get_transaction_id (QmiMessage *self); -guint8 qmi_message_get_qmux_flags (QmiMessage *self); -guint8 qmi_message_get_qmi_flags (QmiMessage *self); - -G_END_DECLS - -#endif /* _LIBQMI_GLIB_QMI_MESSAGE_H_ */ diff --git a/src/qmi-utils.c b/src/qmi-utils.c deleted file mode 100644 index d1e50f0..0000000 --- a/src/qmi-utils.c +++ /dev/null @@ -1,312 +0,0 @@ -/* -*- 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 Aleksander Morgado - */ - -#include -#include -#include - -#include "qmi-utils.h" - -gchar * -qmi_utils_str_hex (gconstpointer mem, - gsize size, - gchar delimiter) -{ - const guint8 *data = mem; - gsize i; - gsize j; - gsize new_str_length; - gchar *new_str; - - /* Get new string length. If input string has N bytes, we need: - * - 1 byte for last NUL char - * - 2N bytes for hexadecimal char representation of each byte... - * - N-1 bytes for the separator ':' - * So... a total of (1+2N+N-1) = 3N bytes are needed... */ - new_str_length = 3 * size; - - /* Allocate memory for new array and initialize contents to NUL */ - new_str = g_malloc0 (new_str_length); - - /* Print hexadecimal representation of each byte... */ - for (i = 0, j = 0; i < size; i++, j += 3) { - /* Print character in output string... */ - snprintf (&new_str[j], 3, "%02X", data[i]); - /* And if needed, add separator */ - if (i != (size - 1) ) - new_str[j + 2] = delimiter; - } - - /* Set output string */ - return new_str; -} - -void -qmi_utils_read_guint8_from_buffer (guint8 **buffer, - guint16 *buffer_size, - guint8 *out) -{ - g_assert (out != NULL); - g_assert (buffer != NULL); - g_assert (buffer_size != NULL); - g_assert (*buffer_size >= 1); - - *out = (*buffer)[0]; - - *buffer = &((*buffer)[1]); - *buffer_size = (*buffer_size) - 1; -} - -void -qmi_utils_read_gint8_from_buffer (guint8 **buffer, - guint16 *buffer_size, - gint8 *out) -{ - g_assert (out != NULL); - g_assert (buffer != NULL); - g_assert (buffer_size != NULL); - g_assert (*buffer_size >= 1); - - *out = *((gint8 *)(&((*buffer)[0]))); - - *buffer = &((*buffer)[1]); - *buffer_size = (*buffer_size) - 1; -} - -void -qmi_utils_read_guint16_from_buffer (guint8 **buffer, - guint16 *buffer_size, - guint16 *out) -{ - g_assert (out != NULL); - g_assert (buffer != NULL); - g_assert (buffer_size != NULL); - g_assert (*buffer_size >= 2); - - *out = GUINT16_FROM_LE (*((guint16 *)&((*buffer)[0]))); - - *buffer = &((*buffer)[2]); - *buffer_size = (*buffer_size) - 2; -} - -void -qmi_utils_read_gint16_from_buffer (guint8 **buffer, - guint16 *buffer_size, - gint16 *out) -{ - g_assert (out != NULL); - g_assert (buffer != NULL); - g_assert (buffer_size != NULL); - g_assert (*buffer_size >= 2); - - *out = GINT16_FROM_LE (*((guint16 *)&((*buffer)[0]))); - - *buffer = &((*buffer)[2]); - *buffer_size = (*buffer_size) - 2; -} - -void -qmi_utils_read_guint32_from_buffer (guint8 **buffer, - guint16 *buffer_size, - guint32 *out) -{ - g_assert (out != NULL); - g_assert (buffer != NULL); - g_assert (buffer_size != NULL); - g_assert (*buffer_size >= 4); - - *out = GUINT32_FROM_LE (*((guint32 *)&((*buffer)[0]))); - - *buffer = &((*buffer)[4]); - *buffer_size = (*buffer_size) - 4; -} - -void -qmi_utils_read_gint32_from_buffer (guint8 **buffer, - guint16 *buffer_size, - gint32 *out) -{ - g_assert (out != NULL); - g_assert (buffer != NULL); - g_assert (buffer_size != NULL); - g_assert (*buffer_size >= 4); - - *out = GUINT32_FROM_LE (*((guint32 *)&((*buffer)[0]))); - - *buffer = &((*buffer)[4]); - *buffer_size = (*buffer_size) - 4; -} - -void -qmi_utils_write_guint8_to_buffer (guint8 **buffer, - guint16 *buffer_size, - guint8 *in) -{ - g_assert (in != NULL); - g_assert (buffer != NULL); - g_assert (buffer_size != NULL); - g_assert (*buffer_size >= 1); - - (*buffer)[0] = *in; - - *buffer = &((*buffer)[1]); - *buffer_size = (*buffer_size) - 1; -} - -void -qmi_utils_write_gint8_to_buffer (guint8 **buffer, - guint16 *buffer_size, - gint8 *in) -{ - g_assert (in != NULL); - g_assert (buffer != NULL); - g_assert (buffer_size != NULL); - g_assert (*buffer_size >= 1); - - *((gint8 *)(&((*buffer)[0]))) = *in; - - *buffer = &((*buffer)[1]); - *buffer_size = (*buffer_size) - 1; -} - -void -qmi_utils_write_guint16_to_buffer (guint8 **buffer, - guint16 *buffer_size, - guint16 *in) -{ - g_assert (in != NULL); - g_assert (buffer != NULL); - g_assert (buffer_size != NULL); - g_assert (*buffer_size >= 2); - - *((guint16 *)(&((*buffer)[0]))) = GUINT16_TO_LE (*in); - - *buffer = &((*buffer)[2]); - *buffer_size = (*buffer_size) - 2; -} - -void -qmi_utils_write_gint16_to_buffer (guint8 **buffer, - guint16 *buffer_size, - gint16 *in) -{ - g_assert (in != NULL); - g_assert (buffer != NULL); - g_assert (buffer_size != NULL); - g_assert (*buffer_size >= 2); - - *((gint16 *)(&((*buffer)[0]))) = GINT16_TO_LE (*in); - - *buffer = &((*buffer)[2]); - *buffer_size = (*buffer_size) - 2; -} - -void -qmi_utils_write_guint32_to_buffer (guint8 **buffer, - guint16 *buffer_size, - guint32 *in) -{ - g_assert (in != NULL); - g_assert (buffer != NULL); - g_assert (buffer_size != NULL); - g_assert (*buffer_size >= 4); - - *((guint32 *)(&((*buffer)[0]))) = GUINT32_TO_LE (*in); - - *buffer = &((*buffer)[4]); - *buffer_size = (*buffer_size) - 4; -} - -void -qmi_utils_write_gint32_to_buffer (guint8 **buffer, - guint16 *buffer_size, - gint32 *in) -{ - g_assert (in != NULL); - g_assert (buffer != NULL); - g_assert (buffer_size != NULL); - g_assert (*buffer_size >= 4); - - *((gint32 *)(&((*buffer)[0]))) = GINT32_TO_LE (*in); - - *buffer = &((*buffer)[4]); - *buffer_size = (*buffer_size) - 4; -} - -void -qmi_utils_read_string_from_buffer (guint8 **buffer, - guint16 *buffer_size, - gboolean length_prefix, - gchar **out) -{ - guint16 string_length; - - g_assert (out != NULL); - g_assert (buffer != NULL); - g_assert (buffer_size != NULL); - - /* If no length prefix given, read the whole buffer into a string */ - if (!length_prefix) - string_length = *buffer_size; - else { - /* We assume the length prefix is always a guint8 */ - guint8 string_length_8; - - qmi_utils_read_guint8_from_buffer (buffer, buffer_size, &string_length_8); - string_length = string_length_8; - } - - *out = g_malloc (string_length + 1); - (*out)[string_length] = '\0'; - memcpy (*out, *buffer, string_length); - - *buffer = &((*buffer)[string_length]); - *buffer_size = (*buffer_size) - string_length; -} - -void -qmi_utils_write_string_to_buffer (guint8 **buffer, - guint16 *buffer_size, - gboolean length_prefix, - gchar **in) -{ - guint16 len; - - g_assert (in != NULL); - g_assert (buffer != NULL); - g_assert (buffer_size != NULL); - - len = (guint16) strlen (*in); - - if (length_prefix) { - guint8 len_8; - - g_warn_if_fail (len <= G_MAXUINT8); - len_8 = (guint8)len; - qmi_utils_write_guint8_to_buffer (buffer, buffer_size, &len_8); - } - - memcpy (*buffer, *in, len); - *buffer = &((*buffer)[len]); - *buffer_size = (*buffer_size) - len; -} diff --git a/src/qmi-utils.h b/src/qmi-utils.h deleted file mode 100644 index 7c098a0..0000000 --- a/src/qmi-utils.h +++ /dev/null @@ -1,95 +0,0 @@ -/* -*- 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 Aleksander Morgado - */ - -/* NOTE: this is a private non-installable header */ - -#ifndef _LIBQMI_GLIB_QMI_UTILS_H_ -#define _LIBQMI_GLIB_QMI_UTILS_H_ - -#include - -G_BEGIN_DECLS - -gchar *qmi_utils_str_hex (gconstpointer mem, - gsize size, - gchar delimiter); - -/* Reading/Writing integer variables */ - -void qmi_utils_read_guint8_from_buffer (guint8 **buffer, - guint16 *buffer_size, - guint8 *out); -void qmi_utils_read_gint8_from_buffer (guint8 **buffer, - guint16 *buffer_size, - gint8 *out); - -void qmi_utils_read_guint16_from_buffer (guint8 **buffer, - guint16 *buffer_size, - guint16 *out); -void qmi_utils_read_gint16_from_buffer (guint8 **buffer, - guint16 *buffer_size, - gint16 *out); - -void qmi_utils_read_guint32_from_buffer (guint8 **buffer, - guint16 *buffer_size, - guint32 *out); -void qmi_utils_read_gint32_from_buffer (guint8 **buffer, - guint16 *buffer_size, - gint32 *out); - -void qmi_utils_write_guint8_to_buffer (guint8 **buffer, - guint16 *buffer_size, - guint8 *in); -void qmi_utils_write_gint8_to_buffer (guint8 **buffer, - guint16 *buffer_size, - gint8 *in); - -void qmi_utils_write_guint16_to_buffer (guint8 **buffer, - guint16 *buffer_size, - guint16 *in); -void qmi_utils_write_gint16_to_buffer (guint8 **buffer, - guint16 *buffer_size, - gint16 *in); - -void qmi_utils_write_guint32_to_buffer (guint8 **buffer, - guint16 *buffer_size, - guint32 *in); -void qmi_utils_write_gint32_to_buffer (guint8 **buffer, - guint16 *buffer_size, - gint32 *in); - -/* Reading/Writing string variables */ - -void qmi_utils_read_string_from_buffer (guint8 **buffer, - guint16 *buffer_size, - gboolean length_prefix, - gchar **out); - -void qmi_utils_write_string_to_buffer (guint8 **buffer, - guint16 *buffer_size, - gboolean length_prefix, - gchar **in); - -G_END_DECLS - -#endif /* _LIBQMI_GLIB_QMI_UTILS_H_ */ -- cgit v1.2.3