aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@aleksander.es>2017-01-22 18:17:09 +0100
committerAleksander Morgado <aleksander@aleksander.es>2017-01-29 10:37:53 +0100
commit9bd604224f56fcf6fb0be577a646e62f59200594 (patch)
treee0eb725cd2f094502f8c613866ce027536a4aa42
parent132bd1b0ef5c3b8b9f2544261a9e57e0fd589c14 (diff)
downloadexternal_libqmi-9bd604224f56fcf6fb0be577a646e62f59200594.zip
external_libqmi-9bd604224f56fcf6fb0be577a646e62f59200594.tar.gz
external_libqmi-9bd604224f56fcf6fb0be577a646e62f59200594.tar.bz2
libqmi-glib: support vendor-specific request/responses
We want to support non-standard messages that may be encoded with different TLVs depending on how the vendor implemented them. Anyway, right now this is really just to support the correct translation of TLVs and message contents in the get_printable() methods. The support is only included for QMI request/responses, and not for QMI indications. This is because the library knows in which moment the requests are created (and can apply the same rules to the matched response when it is received). For the indications, though, there is no such context configurable yet.
-rw-r--r--build-aux/qmi-codegen/Client.py46
-rw-r--r--build-aux/qmi-codegen/Message.py5
-rw-r--r--build-aux/qmi-codegen/MessageList.py85
-rw-r--r--docs/reference/libqmi-glib/libqmi-glib-common.sections18
-rw-r--r--docs/reference/libqmi-glib/libqmi-glib-docs.xml1
-rw-r--r--src/libqmi-glib/Makefile.am2
-rw-r--r--src/libqmi-glib/libqmi-glib.h1
-rw-r--r--src/libqmi-glib/qmi-device.c226
-rw-r--r--src/libqmi-glib/qmi-device.h34
-rw-r--r--src/libqmi-glib/qmi-message-context.c123
-rw-r--r--src/libqmi-glib/qmi-message-context.h74
-rw-r--r--src/libqmi-glib/qmi-message.c121
-rw-r--r--src/libqmi-glib/qmi-message.h30
-rw-r--r--src/libqmi-glib/qmi-proxy.c3
14 files changed, 627 insertions, 142 deletions
diff --git a/build-aux/qmi-codegen/Client.py b/build-aux/qmi-codegen/Client.py
index 1e342ae..32e1e0d 100644
--- a/build-aux/qmi-codegen/Client.py
+++ b/build-aux/qmi-codegen/Client.py
@@ -278,6 +278,7 @@ class Client:
continue
translations['message_name'] = message.name
+ translations['message_vendor_id'] = message.vendor
translations['message_underscore'] = utils.build_underscore_name(message.name)
translations['message_fullname_underscore'] = utils.build_underscore_name(message.fullname)
translations['input_camelcase'] = utils.build_camelcase_name(message.input.fullname)
@@ -370,7 +371,7 @@ class Client:
' QmiMessage *reply;\n'
' ${output_camelcase} *output;\n'
'\n'
- ' reply = qmi_device_command_finish (device, res, &error);\n'
+ ' reply = qmi_device_command_full_finish (device, res, &error);\n'
' if (!reply) {\n')
if message.abort:
@@ -461,7 +462,13 @@ class Client:
' GSimpleAsyncResult *result;\n'
' QmiMessage *request;\n'
' GError *error = NULL;\n'
- ' guint16 transaction_id;\n'
+ ' guint16 transaction_id;\n')
+
+ if message.vendor is not None:
+ template += (
+ ' QmiMessageContext *context;\n')
+
+ template += (
'\n'
' result = g_simple_async_result_new (G_OBJECT (self),\n'
' callback,\n'
@@ -490,15 +497,36 @@ class Client:
' "transaction-id",\n'
' GUINT_TO_POINTER (transaction_id));\n')
+ if message.vendor is not None:
+ template += (
+ '\n'
+ ' context = qmi_message_context_new ();\n'
+ ' qmi_message_context_set_vendor_id (context, ${message_vendor_id});\n')
+
template += (
'\n'
- ' qmi_device_command (QMI_DEVICE (qmi_client_peek_device (QMI_CLIENT (self))),\n'
- ' request,\n'
- ' timeout,\n'
- ' cancellable,\n'
- ' (GAsyncReadyCallback)${message_underscore}_ready,\n'
- ' result);\n'
- ' qmi_message_unref (request);\n'
+ ' qmi_device_command_full (QMI_DEVICE (qmi_client_peek_device (QMI_CLIENT (self))),\n'
+ ' request,\n')
+
+ if message.vendor is not None:
+ template += (
+ ' context,\n')
+ else:
+ template += (
+ ' NULL,\n')
+
+ template += (
+ ' timeout,\n'
+ ' cancellable,\n'
+ ' (GAsyncReadyCallback)${message_underscore}_ready,\n'
+ ' result);\n'
+ ' qmi_message_unref (request);\n')
+
+ if message.vendor is not None:
+ template += (
+ ' qmi_message_context_unref (context);\n')
+
+ template += (
'}\n'
'\n')
cfile.write(string.Template(template).substitute(translations))
diff --git a/build-aux/qmi-codegen/Message.py b/build-aux/qmi-codegen/Message.py
index aae9561..9c78cf5 100644
--- a/build-aux/qmi-codegen/Message.py
+++ b/build-aux/qmi-codegen/Message.py
@@ -45,6 +45,11 @@ class Message:
self.static = True if 'scope' in dictionary and dictionary['scope'] == 'library-only' else False
self.abort = True if 'abort' in dictionary and dictionary['abort'] == 'yes' else False
+ # The vendor id if this command is vendor specific
+ self.vendor = dictionary['vendor'] if 'vendor' in dictionary else None
+ if self.type == 'Indication' and self.vendor is not None:
+ raise ValueError('Vendor-specific indications unsupported')
+
# The message prefix
self.prefix = 'Qmi ' + self.type
diff --git a/build-aux/qmi-codegen/MessageList.py b/build-aux/qmi-codegen/MessageList.py
index bbd020f..4331b90 100644
--- a/build-aux/qmi-codegen/MessageList.py
+++ b/build-aux/qmi-codegen/MessageList.py
@@ -73,8 +73,13 @@ class MessageList:
if message.type == 'Message':
translations['enum_name'] = message.id_enum_name
translations['enum_value'] = message.id
- enum_template = (
- ' ${enum_name} = ${enum_value},\n')
+ if message.vendor is None:
+ enum_template = (
+ ' ${enum_name} = ${enum_value},\n')
+ else:
+ translations['vendor'] = message.vendor
+ enum_template = (
+ ' ${enum_name} = ${enum_value}, /* vendor ${vendor} */\n')
template += string.Template(enum_template).substitute(translations)
template += (
@@ -118,6 +123,7 @@ class MessageList:
'G_GNUC_INTERNAL\n'
'gchar *__qmi_message_${service}_get_printable (\n'
' QmiMessage *self,\n'
+ ' QmiMessageContext *context,\n'
' const gchar *line_prefix);\n'
'\n'
'#endif\n'
@@ -129,6 +135,7 @@ class MessageList:
'gchar *\n'
'__qmi_message_${service}_get_printable (\n'
' QmiMessage *self,\n'
+ ' QmiMessageContext *context,\n'
' const gchar *line_prefix)\n'
'{\n'
' if (qmi_message_is_indication (self)) {\n'
@@ -138,7 +145,6 @@ class MessageList:
if message.type == 'Indication':
translations['enum_name'] = message.id_enum_name
translations['message_underscore'] = utils.build_underscore_name (message.name)
- translations['enum_value'] = message.id
inner_template = (
' case ${enum_name}:\n'
' return indication_${message_underscore}_get_printable (self, line_prefix);\n')
@@ -149,21 +155,39 @@ class MessageList:
' return NULL;\n'
' }\n'
' } else {\n'
- ' switch (qmi_message_get_message_id (self)) {\n')
+ ' guint16 vendor_id;\n'
+ '\n'
+ ' vendor_id = (context ? qmi_message_context_get_vendor_id (context) : QMI_MESSAGE_VENDOR_GENERIC);\n'
+ ' if (vendor_id == QMI_MESSAGE_VENDOR_GENERIC) {\n'
+ ' switch (qmi_message_get_message_id (self)) {\n')
for message in self.list:
- if message.type == 'Message':
+ if message.type == 'Message' and message.vendor is None:
translations['enum_name'] = message.id_enum_name
translations['message_underscore'] = utils.build_underscore_name (message.name)
- translations['enum_value'] = message.id
inner_template = (
- ' case ${enum_name}:\n'
- ' return message_${message_underscore}_get_printable (self, line_prefix);\n')
+ ' case ${enum_name}:\n'
+ ' return message_${message_underscore}_get_printable (self, line_prefix);\n')
template += string.Template(inner_template).substitute(translations)
template += (
- ' default:\n'
- ' return NULL;\n'
+ ' default:\n'
+ ' return NULL;\n'
+ ' }\n'
+ ' } else {\n')
+
+ for message in self.list:
+ if message.type == 'Message' and message.vendor is not None:
+ translations['enum_name'] = message.id_enum_name
+ translations['message_underscore'] = utils.build_underscore_name (message.name)
+ translations['message_vendor'] = message.vendor
+ inner_template = (
+ ' if (vendor_id == ${message_vendor} && (qmi_message_get_message_id (self) == ${enum_name}))\n'
+ ' return message_${message_underscore}_get_printable (self, line_prefix);\n')
+ template += string.Template(inner_template).substitute(translations)
+
+ template += (
+ ' return NULL;\n'
' }\n'
' }\n'
'}\n')
@@ -184,6 +208,7 @@ class MessageList:
'G_GNUC_INTERNAL\n'
'gboolean __qmi_message_${service}_get_version_introduced (\n'
' QmiMessage *self,\n'
+ ' QmiMessageContext *context,\n'
' guint *major,\n'
' guint *minor);\n'
'\n'
@@ -196,27 +221,53 @@ class MessageList:
'gboolean\n'
'__qmi_message_${service}_get_version_introduced (\n'
' QmiMessage *self,\n'
+ ' QmiMessageContext *context,\n'
' guint *major,\n'
' guint *minor)\n'
'{\n'
- ' switch (qmi_message_get_message_id (self)) {\n')
+ ' guint16 vendor_id;\n'
+ '\n'
+ ' vendor_id = (context ? qmi_message_context_get_vendor_id (context) : QMI_MESSAGE_VENDOR_GENERIC);\n'
+ ' if (vendor_id == QMI_MESSAGE_VENDOR_GENERIC) {\n'
+ ' switch (qmi_message_get_message_id (self)) {\n')
for message in self.list:
- if message.type == 'Message':
+ if message.type == 'Message' and message.vendor is None:
+ # Only add if we know the version info
+ if message.version_info != []:
+ translations['enum_name'] = message.id_enum_name
+ translations['message_major'] = message.version_info[0]
+ translations['message_minor'] = message.version_info[1]
+ inner_template = (
+ ' case ${enum_name}:\n'
+ ' *major = ${message_major};\n'
+ ' *minor = ${message_minor};\n'
+ ' return TRUE;\n')
+ template += string.Template(inner_template).substitute(translations)
+
+ template += (
+ ' default:\n'
+ ' return FALSE;\n'
+ ' }\n'
+ ' } else {\n')
+
+ for message in self.list:
+ if message.type == 'Message' and message.vendor is not None:
# Only add if we know the version info
if message.version_info != []:
translations['enum_name'] = message.id_enum_name
translations['message_major'] = message.version_info[0]
translations['message_minor'] = message.version_info[1]
+ translations['message_vendor'] = message.vendor
inner_template = (
- ' case ${enum_name}:\n'
- ' *major = ${message_major};\n'
- ' *minor = ${message_minor};\n'
- ' return TRUE;\n')
+ ' if (vendor_id == ${message_vendor} && (qmi_message_get_message_id (self) == ${enum_name})) {\n'
+ ' *major = ${message_major};\n'
+ ' *minor = ${message_minor};\n'
+ ' return TRUE;\n'
+ ' }\n')
template += string.Template(inner_template).substitute(translations)
template += (
- ' default:\n'
' return FALSE;\n'
' }\n'
'}\n')
diff --git a/docs/reference/libqmi-glib/libqmi-glib-common.sections b/docs/reference/libqmi-glib/libqmi-glib-common.sections
index a81af67..6edf5fe 100644
--- a/docs/reference/libqmi-glib/libqmi-glib-common.sections
+++ b/docs/reference/libqmi-glib/libqmi-glib-common.sections
@@ -73,6 +73,8 @@ qmi_device_set_instance_id
qmi_device_set_instance_id_finish
qmi_device_command
qmi_device_command_finish
+qmi_device_command_full
+qmi_device_command_full_finish
qmi_device_get_service_version_info
qmi_device_get_service_version_info_finish
qmi_device_open_flags_build_string_from_mask
@@ -1060,6 +1062,7 @@ qmi_message_get_message_id
qmi_message_get_length
qmi_message_get_raw
qmi_message_get_version_introduced
+qmi_message_get_version_introduced_full
<SUBSECTION TLV writer>
qmi_message_tlv_write_init
qmi_message_tlv_write_reset
@@ -1097,10 +1100,25 @@ qmi_message_add_raw_tlv
qmi_message_set_transaction_id
<SUBSECTION Printable>
qmi_message_get_printable
+qmi_message_get_printable_full
qmi_message_get_tlv_printable
</SECTION>
<SECTION>
+<FILE>qmi-message-context</FILE>
+QMI_MESSAGE_VENDOR_GENERIC
+QmiMessageContext
+qmi_message_context_new
+qmi_message_context_ref
+qmi_message_context_unref
+<SUBSECTION Vendor>
+qmi_message_context_set_vendor_id
+qmi_message_context_get_vendor_id
+<SUBSECTION Standard>
+qmi_message_context_get_type
+</SECTION>
+
+<SECTION>
<FILE>qmi-utils</FILE>
QmiEndian
<SUBSECTION Traces>
diff --git a/docs/reference/libqmi-glib/libqmi-glib-docs.xml b/docs/reference/libqmi-glib/libqmi-glib-docs.xml
index 07f9795..55f4ec8 100644
--- a/docs/reference/libqmi-glib/libqmi-glib-docs.xml
+++ b/docs/reference/libqmi-glib/libqmi-glib-docs.xml
@@ -45,6 +45,7 @@
<title>Core</title>
<xi:include href="xml/qmi-version.xml"/>
<xi:include href="xml/qmi-message.xml"/>
+ <xi:include href="xml/qmi-message-context.xml"/>
<xi:include href="xml/qmi-device.xml"/>
<xi:include href="xml/qmi-client.xml"/>
<xi:include href="xml/qmi-proxy.xml"/>
diff --git a/src/libqmi-glib/Makefile.am b/src/libqmi-glib/Makefile.am
index 8731de7..e379b0b 100644
--- a/src/libqmi-glib/Makefile.am
+++ b/src/libqmi-glib/Makefile.am
@@ -35,6 +35,7 @@ libqmi_glib_la_SOURCES = \
qmi-utils.h qmi-utils.c \
qmi-compat.h qmi-compat.c \
qmi-message.h qmi-message.c \
+ qmi-message-context.h qmi-message-context.c \
qmi-device.h qmi-device.c \
qmi-client.h qmi-client.c \
qmi-proxy.h qmi-proxy.c
@@ -68,6 +69,7 @@ include_HEADERS = \
qmi-enums-voice.h \
qmi-utils.h \
qmi-message.h \
+ qmi-message-context.h \
qmi-device.h \
qmi-client.h \
qmi-proxy.h
diff --git a/src/libqmi-glib/libqmi-glib.h b/src/libqmi-glib/libqmi-glib.h
index f954342..1e1459e 100644
--- a/src/libqmi-glib/libqmi-glib.h
+++ b/src/libqmi-glib/libqmi-glib.h
@@ -33,6 +33,7 @@
#include "qmi-client.h"
#include "qmi-proxy.h"
#include "qmi-message.h"
+#include "qmi-message-context.h"
#include "qmi-enums.h"
#include "qmi-utils.h"
diff --git a/src/libqmi-glib/qmi-device.c b/src/libqmi-glib/qmi-device.c
index d93044d..6f7ed48 100644
--- a/src/libqmi-glib/qmi-device.c
+++ b/src/libqmi-glib/qmi-device.c
@@ -18,7 +18,7 @@
* Boston, MA 02110-1301 USA.
*
* Copyright (C) 2012 Lanedo GmbH
- * Copyright (C) 2012-2015 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2012-2017 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <config.h>
@@ -143,25 +143,28 @@ typedef struct {
} TransactionWaitContext;
typedef struct {
- QmiMessage *message;
- GSimpleAsyncResult *result;
- GSource *timeout_source;
- GCancellable *cancellable;
- gulong cancellable_id;
+ QmiMessage *message;
+ QmiMessageContext *message_context;
+ GSimpleAsyncResult *result;
+ GSource *timeout_source;
+ GCancellable *cancellable;
+ gulong cancellable_id;
TransactionWaitContext *wait_ctx;
} Transaction;
static Transaction *
-transaction_new (QmiDevice *self,
- QmiMessage *message,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+transaction_new (QmiDevice *self,
+ QmiMessage *message,
+ QmiMessageContext *message_context,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
Transaction *tr;
tr = g_slice_new0 (Transaction);
tr->message = qmi_message_ref (message);
+ tr->message_context = (message_context ? qmi_message_context_ref (message_context) : NULL);
tr->result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
@@ -200,6 +203,8 @@ transaction_complete_and_free (Transaction *tr,
g_simple_async_result_complete_in_idle (tr->result);
g_object_unref (tr->result);
+ if (tr->message_context)
+ qmi_message_context_unref (tr->message_context);
qmi_message_unref (tr->message);
g_slice_free (Transaction, tr);
}
@@ -1590,32 +1595,69 @@ report_indication (QmiClient *client,
}
static void
-process_message (QmiDevice *self,
- QmiMessage *message)
-{
- if (qmi_utils_get_traces_enabled ()) {
- gchar *printable;
+trace_message (QmiDevice *self,
+ QmiMessage *message,
+ gboolean sent_or_received,
+ const gchar *message_str,
+ QmiMessageContext *message_context)
+{
+ gchar *printable;
+ const gchar *prefix_str;
+ const gchar *action_str;
+ gchar *vendor_str = NULL;
+
+ if (!qmi_utils_get_traces_enabled ())
+ return;
- printable = __qmi_utils_str_hex (((GByteArray *)message)->data,
- ((GByteArray *)message)->len,
- ':');
- g_debug ("[%s] Received message...\n"
- ">>>>>> RAW:\n"
- ">>>>>> length = %u\n"
- ">>>>>> data = %s\n",
- self->priv->path_display,
- ((GByteArray *)message)->len,
- printable);
- g_free (printable);
+ if (sent_or_received) {
+ prefix_str = "<<<<<< ";
+ action_str = "sent";
+ } else {
+ prefix_str = "<<<<<< ";
+ action_str = "received";
+ }
+
+ printable = __qmi_utils_str_hex (((GByteArray *)message)->data,
+ ((GByteArray *)message)->len,
+ ':');
+ g_debug ("[%s] %s message...\n"
+ "%sRAW:\n"
+ "%s length = %u\n"
+ "%s data = %s\n",
+ self->priv->path_display, action_str,
+ prefix_str,
+ prefix_str, ((GByteArray *)message)->len,
+ prefix_str, printable);
+ g_free (printable);
+
+ if (message_context) {
+ guint16 vendor_id;
+
+ vendor_id = qmi_message_context_get_vendor_id (message_context);
+ if (vendor_id != QMI_MESSAGE_VENDOR_GENERIC)
+ vendor_str = g_strdup_printf ("vendor-specific (0x%04x)", vendor_id);
+ }
+
+ printable = qmi_message_get_printable_full (message, message_context, prefix_str);
+ g_debug ("[%s] %s %s %s (translated)...\n%s",
+ self->priv->path_display,
+ action_str,
+ vendor_str ? vendor_str : "generic",
+ message_str,
+ printable);
+ g_free (printable);
- printable = qmi_message_get_printable (message, ">>>>>> ");
- g_debug ("[%s] Received message (translated)...\n%s",
- self->priv->path_display,
- printable);
- g_free (printable);
- }
+ g_free (vendor_str);
+}
+static void
+process_message (QmiDevice *self,
+ QmiMessage *message)
+{
if (qmi_message_is_indication (message)) {
+ /* Indication traces translated without an explicit vendor */
+ trace_message (self, message, FALSE, "indication", NULL);
+
/* Generic emission of the indication */
g_signal_emit (self, signals[SIGNAL_INDICATION], 0, message);
@@ -1647,16 +1689,23 @@ process_message (QmiDevice *self,
Transaction *tr;
tr = device_match_transaction (self, message);
- if (!tr)
+ if (!tr) {
+ /* Unmatched transactions translated without an explicit context */
+ trace_message (self, message, FALSE, "response", NULL);
g_debug ("[%s] No transaction matched in received message",
self->priv->path_display);
- else
+ } else {
+ /* Matched transactions translated with the same context as the request */
+ trace_message (self, message, FALSE, "response", tr->message_context);
/* Report the reply message */
transaction_complete_and_free (tr, message, NULL);
+ }
return;
}
+ /* Unexpected message types translated without an explicit context */
+ trace_message (self, message, FALSE, "unexpected message", NULL);
g_debug ("[%s] Message received but it is neither an indication nor a response. Skipping it.",
self->priv->path_display);
}
@@ -2755,19 +2804,19 @@ mbim_command (QmiDevice *self,
/* Command */
/**
- * qmi_device_command_finish:
+ * qmi_device_command_full_finish:
* @self: a #QmiDevice.
* @res: a #GAsyncResult.
* @error: Return location for error or %NULL.
*
- * Finishes an operation started with qmi_device_command().
+ * Finishes an operation started with qmi_device_command_full().
*
* Returns: a #QmiMessage response, or #NULL if @error is set. The returned value should be freed with qmi_message_unref().
*/
QmiMessage *
-qmi_device_command_finish (QmiDevice *self,
- GAsyncResult *res,
- GError **error)
+qmi_device_command_full_finish (QmiDevice *self,
+ GAsyncResult *res,
+ GError **error)
{
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
return NULL;
@@ -2794,9 +2843,10 @@ transaction_early_error (QmiDevice *self,
}
/**
- * qmi_device_command:
+ * qmi_device_command_full:
* @self: a #QmiDevice.
* @message: the message to send.
+ * @message_context: the context of the message.
* @timeout: maximum time, in seconds, to wait for the response.
* @cancellable: a #GCancellable, or %NULL.
* @callback: a #GAsyncReadyCallback to call when the operation is finished.
@@ -2804,16 +2854,22 @@ transaction_early_error (QmiDevice *self,
*
* Asynchronously sends a #QmiMessage to the device.
*
+ * The message will be processed according to the specific @message_context
+ * given.
+ *
* When the operation is finished @callback will be called. You can then call
- * qmi_device_command_finish() to get the result of the operation.
+ * qmi_device_command_full_finish() to get the result of the operation.
+ *
+ * If no @context given, the behavior is the same as qmi_device_command().
*/
void
-qmi_device_command (QmiDevice *self,
- QmiMessage *message,
- guint timeout,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+qmi_device_command_full (QmiDevice *self,
+ QmiMessage *message,
+ QmiMessageContext *message_context,
+ guint timeout,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
GError *error = NULL;
Transaction *tr;
@@ -2835,7 +2891,7 @@ qmi_device_command (QmiDevice *self,
self->priv->client_ctl)));
}
- tr = transaction_new (self, message, cancellable, callback, user_data);
+ tr = transaction_new (self, message, message_context, cancellable, callback, user_data);
/* Device must be open */
if (!self->priv->istream || !self->priv->ostream) {
@@ -2896,27 +2952,7 @@ qmi_device_command (QmiDevice *self,
/* From now on, if we want to complete the transaction with an early error,
* it needs to be removed from the tracking table as well. */
- if (qmi_utils_get_traces_enabled ()) {
- gchar *printable;
-
- printable = __qmi_utils_str_hex (((GByteArray *)message)->data,
- ((GByteArray *)message)->len,
- ':');
- g_debug ("[%s] Sent message...\n"
- "<<<<<< RAW:\n"
- "<<<<<< length = %u\n"
- "<<<<<< data = %s\n",
- self->priv->path_display,
- ((GByteArray *)message)->len,
- printable);
- g_free (printable);
-
- printable = qmi_message_get_printable (message, "<<<<<< ");
- g_debug ("[%s] Sent message (translated)...\n%s",
- self->priv->path_display,
- printable);
- g_free (printable);
- }
+ trace_message (self, message, TRUE, "request", message_context);
#if defined MBIM_QMUX_ENABLED
if (self->priv->mbimdev) {
@@ -2950,6 +2986,56 @@ qmi_device_command (QmiDevice *self,
}
/*****************************************************************************/
+/* Generic command */
+
+/**
+ * qmi_device_command_finish:
+ * @self: a #QmiDevice.
+ * @res: a #GAsyncResult.
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with qmi_device_command().
+ *
+ * Returns: a #QmiMessage response, or #NULL if @error is set. The returned value should be freed with qmi_message_unref().
+ *
+ * Deprecated: 1.18.
+ */
+QmiMessage *
+qmi_device_command_finish (QmiDevice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return qmi_device_command_full_finish (self, res, error);
+}
+
+/**
+ * qmi_device_command:
+ * @self: a #QmiDevice.
+ * @message: the message to send.
+ * @timeout: maximum time, in seconds, to wait for the response.
+ * @cancellable: a #GCancellable, or %NULL.
+ * @callback: a #GAsyncReadyCallback to call when the operation is finished.
+ * @user_data: the data to pass to callback function.
+ *
+ * Asynchronously sends a generic #QmiMessage to the device with no context.
+ *
+ * When the operation is finished @callback will be called. You can then call
+ * qmi_device_command_finish() to get the result of the operation.
+ *
+ * Deprecated: 1.18: Use qmi_device_command_full() instead.
+ */
+void
+qmi_device_command (QmiDevice *self,
+ QmiMessage *message,
+ guint timeout,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ qmi_device_command_full (self, message, NULL, timeout, cancellable, callback, user_data);
+}
+
+/*****************************************************************************/
/* New QMI device */
/**
diff --git a/src/libqmi-glib/qmi-device.h b/src/libqmi-glib/qmi-device.h
index 957efac..51e07ae 100644
--- a/src/libqmi-glib/qmi-device.h
+++ b/src/libqmi-glib/qmi-device.h
@@ -17,7 +17,7 @@
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2012-2015 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2012-2017 Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef _LIBQMI_GLIB_QMI_DEVICE_H_
@@ -32,6 +32,7 @@
#include "qmi-enums.h"
#include "qmi-message.h"
+#include "qmi-message-context.h"
#include "qmi-client.h"
G_BEGIN_DECLS
@@ -171,15 +172,28 @@ gboolean qmi_device_set_instance_id_finish (QmiDevice *self,
guint16 *link_id,
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_DEPRECATED
+void qmi_device_command (QmiDevice *self,
+ QmiMessage *message,
+ guint timeout,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+G_DEPRECATED
+QmiMessage *qmi_device_command_finish (QmiDevice *self,
+ GAsyncResult *res,
+ GError **error);
+
+void qmi_device_command_full (QmiDevice *self,
+ QmiMessage *message,
+ QmiMessageContext *message_context,
+ guint timeout,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+QmiMessage *qmi_device_command_full_finish (QmiDevice *self,
+ GAsyncResult *res,
+ GError **error);
/**
* QmiDeviceServiceVersionInfo:
diff --git a/src/libqmi-glib/qmi-message-context.c b/src/libqmi-glib/qmi-message-context.c
new file mode 100644
index 0000000..96f7a67
--- /dev/null
+++ b/src/libqmi-glib/qmi-message-context.c
@@ -0,0 +1,123 @@
+/* -*- 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) 2017 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <glib.h>
+
+#include "qmi-message-context.h"
+
+/*****************************************************************************/
+/* Basic context */
+
+struct _QmiMessageContext {
+ volatile gint ref_count;
+
+ /* Vendor ID */
+ guint16 vendor_id;
+};
+
+/**
+ * qmi_message_context_new:
+ *
+ * Create a new empty #QmiMessageContext.
+ *
+ * Returns: (transfer full): a newly created #QmiMessageContext. The returned value should be freed with qmi_message_context_unref().
+ */
+QmiMessageContext *
+qmi_message_context_new (void)
+{
+ QmiMessageContext *self;
+
+ self = g_slice_new0 (QmiMessageContext);
+ self->ref_count = 1;
+ return self;
+}
+
+GType
+qmi_message_context_get_type (void)
+{
+ static volatile gsize g_define_type_id__volatile = 0;
+
+ if (g_once_init_enter (&g_define_type_id__volatile)) {
+ GType g_define_type_id =
+ g_boxed_type_register_static (g_intern_static_string ("QmiMessageContext"),
+ (GBoxedCopyFunc) qmi_message_context_ref,
+ (GBoxedFreeFunc) qmi_message_context_unref);
+
+ g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ }
+
+ return g_define_type_id__volatile;
+}
+
+/**
+ * qmi_message_context_ref:
+ * @self: a #QmiMessageContext.
+ *
+ * Atomically increments the reference count of @self by one.
+ *
+ * Returns: (transfer full) the new reference to @self.
+ */
+QmiMessageContext *
+qmi_message_context_ref (QmiMessageContext *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+
+ g_atomic_int_inc (&self->ref_count);
+ return self;
+}
+
+/**
+ * qmi_message_context_unref:
+ * @self: a #QmiMessageContext.
+ *
+ * Atomically decrements the reference count of @self by one.
+ * If the reference count drops to 0, @self is completely disposed.
+ */
+void
+qmi_message_context_unref (QmiMessageContext *self)
+{
+ g_return_if_fail (self != NULL);
+
+ if (g_atomic_int_dec_and_test (&self->ref_count)) {
+ g_slice_free (QmiMessageContext, self);
+ }
+}
+
+/*****************************************************************************/
+/* Vendor ID */
+
+void
+qmi_message_context_set_vendor_id (QmiMessageContext *self,
+ guint16 vendor_id)
+{
+ g_return_if_fail (self != NULL);
+
+ self->vendor_id = vendor_id;
+}
+
+guint16
+qmi_message_context_get_vendor_id (QmiMessageContext *self)
+{
+ g_return_val_if_fail (self != NULL, QMI_MESSAGE_VENDOR_GENERIC);
+ return self->vendor_id;
+}
diff --git a/src/libqmi-glib/qmi-message-context.h b/src/libqmi-glib/qmi-message-context.h
new file mode 100644
index 0000000..2dcf2ec
--- /dev/null
+++ b/src/libqmi-glib/qmi-message-context.h
@@ -0,0 +1,74 @@
+/* -*- 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) 2017 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef _LIBQMI_GLIB_QMI_MESSAGE_CONTEXT_H_
+#define _LIBQMI_GLIB_QMI_MESSAGE_CONTEXT_H_
+
+#if !defined (__LIBQMI_GLIB_H_INSIDE__) && !defined (LIBQMI_GLIB_COMPILATION)
+#error "Only <libqmi-glib.h> can be included directly."
+#endif
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/**
+ * QmiMessageContext:
+ *
+ * An opaque type representing a QMI message context.
+ *
+ * The context defines non-standard features of the QMI message associated with
+ * it, which may be required for a correct processing.
+ *
+ * When a context is given when sending a request with qmi_device_command_full(),
+ * the same context will then be applied for the associated response.
+ */
+typedef struct _QmiMessageContext QmiMessageContext;
+
+GType qmi_message_context_get_type (void);
+
+/*****************************************************************************/
+/* Basic context */
+
+QmiMessageContext *qmi_message_context_new (void);
+QmiMessageContext *qmi_message_context_ref (QmiMessageContext *self);
+void qmi_message_context_unref (QmiMessageContext *self);
+
+/*****************************************************************************/
+/* Vendor ID */
+
+/**
+ * QMI_MESSAGE_VENDOR_GENERIC:
+ *
+ * Generic vendor id (0x0000).
+ */
+#define QMI_MESSAGE_VENDOR_GENERIC 0x0000
+
+void qmi_message_context_set_vendor_id (QmiMessageContext *self,
+ guint16 vendor_id);
+guint16 qmi_message_context_get_vendor_id (QmiMessageContext *self);
+
+G_END_DECLS
+
+#endif /* _LIBQMI_GLIB_QMI_MESSAGE_CONTEXT_H_ */
diff --git a/src/libqmi-glib/qmi-message.c b/src/libqmi-glib/qmi-message.c
index 23afa29..bbf08b6 100644
--- a/src/libqmi-glib/qmi-message.c
+++ b/src/libqmi-glib/qmi-message.c
@@ -24,7 +24,7 @@
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2012-2015 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2012-2017 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <glib.h>
@@ -2077,20 +2077,27 @@ get_generic_printable (QmiMessage *self,
}
/**
- * qmi_message_get_printable:
+ * qmi_message_get_printable_full:
* @self: a #QmiMessage.
+ * @context: a #QmiMessageContext.
* @line_prefix: prefix string to use in each new generated line.
*
* Gets a printable string with the contents of the whole QMI message.
*
- * If known, the printable string will contain translated TLV values as well as the raw
- * data buffer contents.
+ * If known, the printable string will contain translated TLV values as well as
+ * the raw data buffer contents.
+ *
+ * The translation of the contents may be specific to the @context provided,
+ * e.g. for vendor-specific messages.
+ *
+ * If no @context given, the behavior is the same as qmi_message_get_printable().
*
* Returns: (transfer full): a newly allocated string, which should be freed with g_free().
*/
gchar *
-qmi_message_get_printable (QmiMessage *self,
- const gchar *line_prefix)
+qmi_message_get_printable_full (QmiMessage *self,
+ QmiMessageContext *context,
+ const gchar *line_prefix)
{
GString *printable;
gchar *qmi_flags_str;
@@ -2134,40 +2141,40 @@ qmi_message_get_printable (QmiMessage *self,
contents = NULL;
switch (qmi_message_get_service (self)) {
case QMI_SERVICE_CTL:
- contents = __qmi_message_ctl_get_printable (self, line_prefix);
+ contents = __qmi_message_ctl_get_printable (self, context, line_prefix);
break;
case QMI_SERVICE_DMS:
- contents = __qmi_message_dms_get_printable (self, line_prefix);
+ contents = __qmi_message_dms_get_printable (self, context, line_prefix);
break;
case QMI_SERVICE_WDS:
- contents = __qmi_message_wds_get_printable (self, line_prefix);
+ contents = __qmi_message_wds_get_printable (self, context, line_prefix);
break;
case QMI_SERVICE_NAS:
- contents = __qmi_message_nas_get_printable (self, line_prefix);
+ contents = __qmi_message_nas_get_printable (self, context, line_prefix);
break;
case QMI_SERVICE_WMS:
- contents = __qmi_message_wms_get_printable (self, line_prefix);
+ contents = __qmi_message_wms_get_printable (self, context, line_prefix);
break;
case QMI_SERVICE_PDC:
- contents = __qmi_message_pdc_get_printable (self, line_prefix);
+ contents = __qmi_message_pdc_get_printable (self, context, line_prefix);
break;
case QMI_SERVICE_PDS:
- contents = __qmi_message_pds_get_printable (self, line_prefix);
+ contents = __qmi_message_pds_get_printable (self, context, line_prefix);
break;
case QMI_SERVICE_PBM:
- contents = __qmi_message_pbm_get_printable (self, line_prefix);
+ contents = __qmi_message_pbm_get_printable (self, context, line_prefix);
break;
case QMI_SERVICE_UIM:
- contents = __qmi_message_uim_get_printable (self, line_prefix);
+ contents = __qmi_message_uim_get_printable (self, context, line_prefix);
break;
case QMI_SERVICE_OMA:
- contents = __qmi_message_oma_get_printable (self, line_prefix);
+ contents = __qmi_message_oma_get_printable (self, context, line_prefix);
break;
case QMI_SERVICE_WDA:
- contents = __qmi_message_wda_get_printable (self, line_prefix);
+ contents = __qmi_message_wda_get_printable (self, context, line_prefix);
break;
case QMI_SERVICE_VOICE:
- contents = __qmi_message_voice_get_printable (self, line_prefix);
+ contents = __qmi_message_voice_get_printable (self, context, line_prefix);
break;
default:
break;
@@ -2182,19 +2189,48 @@ qmi_message_get_printable (QmiMessage *self,
}
/**
- * qmi_message_get_version_introduced:
+ * qmi_message_get_printable:
* @self: a #QmiMessage.
+ * @line_prefix: prefix string to use in each new generated line.
+ *
+ * Gets a printable string with the contents of the whole QMI message.
+ *
+ * If known, the printable string will contain translated TLV values as well as the raw
+ * data buffer contents.
+ *
+ * Returns: (transfer full): a newly allocated string, which should be freed with g_free().
+ *
+ * Deprecated: 1.18: Use qmi_message_get_printable_full() instead.
+ */
+gchar *
+qmi_message_get_printable (QmiMessage *self,
+ const gchar *line_prefix)
+{
+ return qmi_message_get_printable_full (self, NULL, line_prefix);
+}
+
+/**
+ * qmi_message_get_version_introduced_full:
+ * @self: a #QmiMessage.
+ * @context: a #QmiMessageContext.
* @major: (out) return location for the major version.
* @minor: (out) return location for the minor version.
*
- * Gets, if known, the service version in which the given message was first introduced.
+ * Gets, if known, the service version in which the given message was first
+ * introduced.
+ *
+ * The lookup of the version may be specific to the @context provided, e.g. for
+ * vendor-specific messages.
+ *
+ * If no @context given, the behavior is the same as qmi_message_get_version_introduced().
*
* Returns: %TRUE if @major and @minor are set, %FALSE otherwise.
*/
gboolean
-qmi_message_get_version_introduced (QmiMessage *self,
- guint *major,
- guint *minor)
+qmi_message_get_version_introduced_full (QmiMessage *self,
+ QmiMessageContext *context,
+ guint *major,
+ guint *minor)
{
switch (qmi_message_get_service (self)) {
case QMI_SERVICE_CTL:
@@ -2204,34 +2240,55 @@ qmi_message_get_version_introduced (QmiMessage *self,
return TRUE;
case QMI_SERVICE_DMS:
- return __qmi_message_dms_get_version_introduced (self, major, minor);
+ return __qmi_message_dms_get_version_introduced (self, context, major, minor);
case QMI_SERVICE_WDS:
- return __qmi_message_wds_get_version_introduced (self, major, minor);
+ return __qmi_message_wds_get_version_introduced (self, context, major, minor);
case QMI_SERVICE_NAS:
- return __qmi_message_nas_get_version_introduced (self, major, minor);
+ return __qmi_message_nas_get_version_introduced (self, context, major, minor);
case QMI_SERVICE_WMS:
- return __qmi_message_wms_get_version_introduced (self, major, minor);
+ return __qmi_message_wms_get_version_introduced (self, context, major, minor);
case QMI_SERVICE_PDS:
- return __qmi_message_pds_get_version_introduced (self, major, minor);
+ return __qmi_message_pds_get_version_introduced (self, context, major, minor);
case QMI_SERVICE_PBM:
- return __qmi_message_pbm_get_version_introduced (self, major, minor);
+ return __qmi_message_pbm_get_version_introduced (self, context, major, minor);
case QMI_SERVICE_UIM:
- return __qmi_message_uim_get_version_introduced (self, major, minor);
+ return __qmi_message_uim_get_version_introduced (self, context, major, minor);
case QMI_SERVICE_OMA:
- return __qmi_message_oma_get_version_introduced (self, major, minor);
+ return __qmi_message_oma_get_version_introduced (self, context, major, minor);
case QMI_SERVICE_WDA:
- return __qmi_message_wda_get_version_introduced (self, major, minor);
+ return __qmi_message_wda_get_version_introduced (self, context, major, minor);
default:
/* For the still unsupported services, cannot do anything */
return FALSE;
}
}
+
+/**
+ * qmi_message_get_version_introduced:
+ * @self: a #QmiMessage.
+ * @major: (out) return location for the major version.
+ * @minor: (out) return location for the minor version.
+ *
+ * Gets, if known, the service version in which the given message was first
+ * introduced.
+ *
+ * Returns: %TRUE if @major and @minor are set, %FALSE otherwise.
+ *
+ * Deprecated: 1.18: Use qmi_message_get_version_introduced_full() instead.
+ */
+gboolean
+qmi_message_get_version_introduced (QmiMessage *self,
+ guint *major,
+ guint *minor)
+{
+ return qmi_message_get_version_introduced_full (self, NULL, major, minor);
+}
diff --git a/src/libqmi-glib/qmi-message.h b/src/libqmi-glib/qmi-message.h
index 4f21560..7ca240e 100644
--- a/src/libqmi-glib/qmi-message.h
+++ b/src/libqmi-glib/qmi-message.h
@@ -24,7 +24,7 @@
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2012-2015 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2012-2017 Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef _LIBQMI_GLIB_QMI_MESSAGE_H_
@@ -39,6 +39,7 @@
#include "qmi-utils.h"
#include "qmi-enums.h"
#include "qmi-errors.h"
+#include "qmi-message-context.h"
G_BEGIN_DECLS
@@ -51,6 +52,13 @@ G_BEGIN_DECLS
*/
typedef GByteArray QmiMessage;
+/**
+ * QMI_MESSAGE_VENDOR_GENERIC:
+ *
+ * Generic vendor id (0x0000).
+ */
+#define QMI_MESSAGE_VENDOR_GENERIC 0x0000
+
/*****************************************************************************/
/* QMI Message life cycle */
@@ -79,10 +87,19 @@ gsize qmi_message_get_length (QmiMessage *self);
const guint8 *qmi_message_get_raw (QmiMessage *self,
gsize *length,
GError **error);
-gboolean qmi_message_get_version_introduced (QmiMessage *self,
- guint *major,
- guint *minor);
+/*****************************************************************************/
+/* Version support from the database */
+
+G_DEPRECATED
+gboolean qmi_message_get_version_introduced (QmiMessage *self,
+ guint *major,
+ guint *minor);
+
+gboolean qmi_message_get_version_introduced_full (QmiMessage *self,
+ QmiMessageContext *context,
+ guint *major,
+ guint *minor);
/*****************************************************************************/
/* TLV builder & writer */
@@ -250,9 +267,14 @@ void qmi_message_set_transaction_id (QmiMessage *self,
/*****************************************************************************/
/* Printable helpers */
+G_DEPRECATED
gchar *qmi_message_get_printable (QmiMessage *self,
const gchar *line_prefix);
+gchar *qmi_message_get_printable_full (QmiMessage *self,
+ QmiMessageContext *context,
+ const gchar *line_prefix);
+
gchar *qmi_message_get_tlv_printable (QmiMessage *self,
const gchar *line_prefix,
guint8 type,
diff --git a/src/libqmi-glib/qmi-proxy.c b/src/libqmi-glib/qmi-proxy.c
index 5077041..592ed8b 100644
--- a/src/libqmi-glib/qmi-proxy.c
+++ b/src/libqmi-glib/qmi-proxy.c
@@ -608,6 +608,9 @@ process_message (QmiProxy *self,
* complete, otherwise the remote clients will lose the reply if they
* configured a timeout bigger than this internal one. We should likely
* make this value configurable per-client, instead of a hardcoded value.
+ *
+ * Note: the proxy will not translate vendor-specific messages in its
+ * logs (as it doesn't have the orignal message context with the vendor id).
*/
qmi_device_command (client->device,
message,