diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2014-10-08 17:27:11 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2014-11-09 20:13:52 +0100 |
commit | 7a0f48562ea49ce6daaf23deaa6a46ff95f31d16 (patch) | |
tree | 5c1d28b222fd2391ce70700d1acd56eb6f6c431d | |
parent | ffa016d95fc94e0ab2387e6fc7b75bdc939ddaf1 (diff) | |
download | external_libqmi-7a0f48562ea49ce6daaf23deaa6a46ff95f31d16.zip external_libqmi-7a0f48562ea49ce6daaf23deaa6a46ff95f31d16.tar.gz external_libqmi-7a0f48562ea49ce6daaf23deaa6a46ff95f31d16.tar.bz2 |
libqmi-glib,message: new TLV builder API
-rw-r--r-- | docs/reference/libqmi-glib/libqmi-glib-common.sections | 18 | ||||
-rw-r--r-- | src/libqmi-glib/qmi-errors.h | 4 | ||||
-rw-r--r-- | src/libqmi-glib/qmi-message.c | 535 | ||||
-rw-r--r-- | src/libqmi-glib/qmi-message.h | 54 |
4 files changed, 609 insertions, 2 deletions
diff --git a/docs/reference/libqmi-glib/libqmi-glib-common.sections b/docs/reference/libqmi-glib/libqmi-glib-common.sections index 8c5e223..cd78c1d 100644 --- a/docs/reference/libqmi-glib/libqmi-glib-common.sections +++ b/docs/reference/libqmi-glib/libqmi-glib-common.sections @@ -835,6 +835,7 @@ qmi_message_new_from_raw qmi_message_response_new qmi_message_ref qmi_message_unref +<SUBSECTION Getters> qmi_message_is_request qmi_message_is_response qmi_message_is_indication @@ -845,11 +846,28 @@ qmi_message_get_message_id qmi_message_get_length qmi_message_get_raw qmi_message_get_version_introduced +<SUBSECTION TLV writer> +qmi_message_tlv_write_init +qmi_message_tlv_write_reset +qmi_message_tlv_write_complete +qmi_message_tlv_write_guint8 +qmi_message_tlv_write_gint8 +qmi_message_tlv_write_guint16 +qmi_message_tlv_write_gint16 +qmi_message_tlv_write_guint32 +qmi_message_tlv_write_gint32 +qmi_message_tlv_write_guint64 +qmi_message_tlv_write_gint64 +qmi_message_tlv_write_sized_guint +qmi_message_tlv_write_string +<SUBSECTION RAW TLVs> QmiMessageForeachRawTlvFn qmi_message_foreach_raw_tlv qmi_message_get_raw_tlv qmi_message_add_raw_tlv +<SUBSECTION Setters> qmi_message_set_transaction_id +<SUBSECTION Printable> qmi_message_get_printable qmi_message_get_tlv_printable </SECTION> diff --git a/src/libqmi-glib/qmi-errors.h b/src/libqmi-glib/qmi-errors.h index d8e36b2..9c73d27 100644 --- a/src/libqmi-glib/qmi-errors.h +++ b/src/libqmi-glib/qmi-errors.h @@ -49,6 +49,7 @@ * @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. + * @QMI_CORE_ERROR_TLV_EMPTY: TLV has no value. * * Common errors that may be reported by libqmi-glib. */ @@ -60,7 +61,8 @@ typedef enum { /*< underscore_name=qmi_core_error >*/ QMI_CORE_ERROR_INVALID_MESSAGE = 4, /*< nick=InvalidMessage >*/ QMI_CORE_ERROR_TLV_NOT_FOUND = 5, /*< nick=TlvNotFound >*/ QMI_CORE_ERROR_TLV_TOO_LONG = 6, /*< nick=TlvTooLong >*/ - QMI_CORE_ERROR_UNSUPPORTED = 7 /*< nick=Unsupported >*/ + QMI_CORE_ERROR_UNSUPPORTED = 7, /*< nick=Unsupported >*/ + QMI_CORE_ERROR_TLV_EMPTY = 8, /*< nick=TlvEmpty >*/ } QmiCoreError; /** diff --git a/src/libqmi-glib/qmi-message.c b/src/libqmi-glib/qmi-message.c index b38e7ce..6b503ac 100644 --- a/src/libqmi-glib/qmi-message.c +++ b/src/libqmi-glib/qmi-message.c @@ -659,6 +659,539 @@ qmi_message_get_raw (QmiMessage *self, return self->data; } +/*****************************************************************************/ +/* TLV builder & writer */ + +static gboolean +tlv_error_if_write_overflow (QmiMessage *self, + gsize len, + GError **error) +{ + /* Check for overflow of message size. */ + if (self->len + len > G_MAXUINT16) { + g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_TLV_TOO_LONG, + "Writing TLV would overflow"); + return FALSE; + } + + return TRUE; +} + +static struct tlv * +tlv_get_header (QmiMessage *self, + gssize init_offset) +{ + g_assert (init_offset <= self->len); + return (struct tlv *)(&self->data[init_offset]); +} + +/** + * qmi_message_tlv_write_init: + * @self: a #QmiMessage. + * @type: specific ID of the TLV to add. + * @error: return location for error or %NULL. + * + * Starts building a new TLV in the #QmiMessage. + * + * In order to finish adding the TLV, qmi_message_tlv_write_complete() needs to be + * called. + * + * If any error happens adding fields on the TLV, the previous state can be + * recovered using qmi_message_tlv_write_reset(). + * + * Returns: the offset where the TLV was started to be added, or 0 if an error happens. + * + * Since: 1.12 + */ +gsize +qmi_message_tlv_write_init (QmiMessage *self, + guint8 type, + GError **error) +{ + gsize init_offset; + struct tlv *tlv; + + g_return_val_if_fail (self != NULL, 0); + g_return_val_if_fail (self->len > 0, 0); + + /* Check for overflow of message size. Note that a valid TLV will at least + * have 1 byte of value. */ + if (!tlv_error_if_write_overflow (self, sizeof (struct tlv) + 1, error)) + return 0; + + /* Store where exactly we started adding the TLV */ + init_offset = self->len; + + /* Resize buffer to fit the TLV header */ + g_byte_array_set_size (self, self->len + sizeof (struct tlv)); + + /* Write the TLV header */ + tlv = tlv_get_header (self, init_offset); + tlv->type = type; + tlv->length = 0; /* Correct value will be set in complete() */ + + return init_offset; +} + +/** + * qmi_message_tlv_write_reset: + * @self: a #QmiMessage. + * @tlv_offset: offset that was returned by qmi_message_tlv_write_init(). + * + * Removes the TLV being currently added. + * + * Since: 1.12 + */ +void +qmi_message_tlv_write_reset (QmiMessage *self, + gsize tlv_offset) +{ + g_return_val_if_fail (self != NULL, FALSE); + + g_byte_array_set_size (self, tlv_offset); +} + +/** + * qmi_message_tlv_write_complete: + * @self: a #QmiMessage. + * @tlv_offset: offset that was returned by qmi_message_tlv_write_init(). + * @error: return location for error or %NULL. + * + * Completes building a TLV in the #QmiMessage. + * + * In case of error the TLV will be reseted; i.e. there is no need to explicitly + * call qmi_message_tlv_write_reset(). + * + * Returns: %TRUE if the TLV is successfully completed, otherwise %FALSE is returned and @error is set. + * + * Since: 1.12 + */ +gboolean +qmi_message_tlv_write_complete (QmiMessage *self, + gsize tlv_offset, + GError **error) +{ + gsize tlv_length; + struct tlv *tlv; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (self->len >= (tlv_offset + sizeof (struct tlv)), FALSE); + + tlv_length = self->len - tlv_offset; + if (tlv_length == sizeof (struct tlv)) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_TLV_EMPTY, + "Empty TLV, no value set"); + g_byte_array_set_size (self, tlv_offset); + return FALSE; + } + + /* Update length fields. */ + tlv = tlv_get_header (self, tlv_offset); + tlv->length = GUINT16_TO_LE (tlv_length - sizeof (struct tlv)); + set_qmux_length (self, (guint16)(get_qmux_length (self) + tlv_length)); + set_all_tlvs_length (self, (guint16)(get_all_tlvs_length (self) + tlv_length)); + + /* Make sure we didn't break anything. */ + g_assert (message_check (self, error)); + + return TRUE; +} + +/** + * qmi_message_tlv_write_guint8: + * @self: a #QmiMessage. + * @in: a #guint8. + * @error: return location for error or %NULL. + * + * Appends an unsigned byte to the TLV being built. + * + * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set. + * + * Since: 1.12 + */ +gboolean +qmi_message_tlv_write_guint8 (QmiMessage *self, + guint8 in, + GError **error) +{ + g_return_val_if_fail (self != NULL, FALSE); + + /* Check for overflow of message size */ + if (!tlv_error_if_write_overflow (self, sizeof (in), error)) + return FALSE; + + g_byte_array_append (self, &in, sizeof (in)); + return TRUE; +} + +/** + * qmi_message_tlv_write_gint8: + * @self: a #QmiMessage. + * @in: a #gint8. + * @error: return location for error or %NULL. + * + * Appends a signed byte variable to the TLV being built. + * + * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set. + * + * Since: 1.12 + */ +gboolean +qmi_message_tlv_write_gint8 (QmiMessage *self, + gint8 in, + GError **error) +{ + g_return_val_if_fail (self != NULL, FALSE); + + /* Check for overflow of message size */ + if (!tlv_error_if_write_overflow (self, sizeof (in), error)) + return FALSE; + + g_byte_array_append (self, (guint8 *)&in, sizeof (in)); + return TRUE; +} + +/** + * qmi_message_tlv_write_guint16: + * @self: a #QmiMessage. + * @endian: target endianness, swapped from host byte order if necessary. + * @in: a #guint16 in host byte order. + * @error: return location for error or %NULL. + * + * Appends an unsigned 16-bit integer to the TLV being built. The number to be + * written is expected to be given in host endianness, and this method takes + * care of converting the value written to the byte order specified by @endian. + * + * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set. + * + * Since: 1.12 + */ +gboolean +qmi_message_tlv_write_guint16 (QmiMessage *self, + QmiEndian endian, + guint16 in, + GError **error) +{ + guint16 tmp; + + g_return_val_if_fail (self != NULL, FALSE); + + /* Check for overflow of message size */ + if (!tlv_error_if_write_overflow (self, sizeof (in), error)) + return FALSE; + + tmp = (endian == QMI_ENDIAN_BIG ? GUINT16_TO_BE (in) : GUINT16_TO_LE (in)); + g_byte_array_append (self, (guint8 *)&tmp, sizeof (tmp)); + return TRUE; +} + +/** + * qmi_message_tlv_write_gint16: + * @self: a #QmiMessage. + * @endian: target endianness, swapped from host byte order if necessary. + * @in: a #gint16 in host byte order. + * @error: return location for error or %NULL. + * + * Appends a signed 16-bit integer to the TLV being built. The number to be + * written is expected to be given in host endianness, and this method takes + * care of converting the value written to the byte order specified by @endian. + * + * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set. + * + * Since: 1.12 + */ +gboolean +qmi_message_tlv_write_gint16 (QmiMessage *self, + QmiEndian endian, + gint16 in, + GError **error) +{ + gint16 tmp; + + g_return_val_if_fail (self != NULL, FALSE); + + /* Check for overflow of message size */ + if (!tlv_error_if_write_overflow (self, sizeof (in), error)) + return FALSE; + + tmp = (endian == QMI_ENDIAN_BIG ? GINT16_TO_BE (in) : GINT16_TO_LE (in)); + g_byte_array_append (self, (guint8 *)&tmp, sizeof (tmp)); + return TRUE; +} + +/** + * qmi_message_tlv_write_guint32: + * @self: a #QmiMessage. + * @endian: target endianness, swapped from host byte order if necessary. + * @in: a #guint32 in host byte order. + * @error: return location for error or %NULL. + * + * Appends an unsigned 32-bit integer to the TLV being built. The number to be + * written is expected to be given in host endianness, and this method takes + * care of converting the value written to the byte order specified by @endian. + * + * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set. + * + * Since: 1.12 + */ +gboolean +qmi_message_tlv_write_guint32 (QmiMessage *self, + QmiEndian endian, + guint32 in, + GError **error) +{ + guint32 tmp; + + g_return_val_if_fail (self != NULL, FALSE); + + /* Check for overflow of message size */ + if (!tlv_error_if_write_overflow (self, sizeof (in), error)) + return FALSE; + + tmp = (endian == QMI_ENDIAN_BIG ? GUINT32_TO_BE (in) : GUINT32_TO_LE (in)); + g_byte_array_append (self, (guint8 *)&tmp, sizeof (tmp)); + return TRUE; +} + +/** + * qmi_message_tlv_write_gint32: + * @self: a #QmiMessage. + * @endian: target endianness, swapped from host byte order if necessary. + * @in: a #gint32 in host byte order. + * @error: return location for error or %NULL. + * + * Appends a signed 32-bit integer to the TLV being built. The number to be + * written is expected to be given in host endianness, and this method takes + * care of converting the value written to the byte order specified by @endian. + * + * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set. + * + * Since: 1.12 + */ +gboolean +qmi_message_tlv_write_gint32 (QmiMessage *self, + QmiEndian endian, + gint32 in, + GError **error) +{ + gint32 tmp; + + g_return_val_if_fail (self != NULL, FALSE); + + /* Check for overflow of message size */ + if (!tlv_error_if_write_overflow (self, sizeof (in), error)) + return FALSE; + + tmp = (endian == QMI_ENDIAN_BIG ? GINT32_TO_BE (in) : GINT32_TO_LE (in)); + g_byte_array_append (self, (guint8 *)&tmp, sizeof (tmp)); + return TRUE; +} + +/** + * qmi_message_tlv_write_guint64: + * @self: a #QmiMessage. + * @endian: target endianness, swapped from host byte order if necessary. + * @in: a #guint64 in host byte order. + * @error: return location for error or %NULL. + * + * Appends an unsigned 64-bit integer to the TLV being built. The number to be + * written is expected to be given in host endianness, and this method takes + * care of converting the value written to the byte order specified by @endian. + * + * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set. + * + * Since: 1.12 + */ +gboolean +qmi_message_tlv_write_guint64 (QmiMessage *self, + QmiEndian endian, + guint64 in, + GError **error) +{ + guint64 tmp; + + g_return_val_if_fail (self != NULL, FALSE); + + /* Check for overflow of message size */ + if (!tlv_error_if_write_overflow (self, sizeof (in), error)) + return FALSE; + + tmp = (endian == QMI_ENDIAN_BIG ? GUINT64_TO_BE (in) : GUINT64_TO_LE (in)); + g_byte_array_append (self, (guint8 *)&tmp, sizeof (tmp)); + return TRUE; +} + +/** + * qmi_message_tlv_write_gint64: + * @self: a #QmiMessage. + * @endian: target endianness, swapped from host byte order if necessary. + * @in: a #gint64 in host byte order. + * @error: return location for error or %NULL. + * + * Appends a signed 32-bit integer to the TLV being built. The number to be + * written is expected to be given in host endianness, and this method takes + * care of converting the value written to the byte order specified by @endian. + * + * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set. + * + * Since: 1.12 + */ +gboolean +qmi_message_tlv_write_gint64 (QmiMessage *self, + QmiEndian endian, + gint64 in, + GError **error) +{ + gint64 tmp; + + g_return_val_if_fail (self != NULL, FALSE); + + /* Check for overflow of message size */ + if (!tlv_error_if_write_overflow (self, sizeof (in), error)) + return FALSE; + + tmp = (endian == QMI_ENDIAN_BIG ? GINT64_TO_BE (in) : GINT64_TO_LE (in)); + g_byte_array_append (self, (guint8 *)&tmp, sizeof (tmp)); + return TRUE; +} + +/** + * qmi_message_tlv_write_sized_guint: + * @self: a #QmiMessage. + * @n_bytes: number of bytes to write. + * @endian: target endianness, swapped from host byte order if necessary. + * @in: a #guint64 in host byte order. + * @error: return location for error or %NULL. + * + * Appends a @n_bytes-sized unsigned integer to the TLV being built. The number + * to be written is expected to be given in host endianness, and this method + * takes care of converting the value written to the byte order specified by + * @endian. + * + * Valid values for @n_bytes are: 1, 2, 4, 8. + * + * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set. + * + * Since: 1.12 + */ +gboolean +qmi_message_tlv_write_sized_guint (QmiMessage *self, + guint n_bytes, + QmiEndian endian, + guint64 in, + GError **error) +{ + guint64 tmp; + goffset offset; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail ((n_bytes == 1 || + n_bytes == 2 || + n_bytes == 4 || + n_bytes == 8), FALSE); + + /* Check for overflow of message size */ + if (!tlv_error_if_write_overflow (self, n_bytes, error)) + return FALSE; + + tmp = (endian == QMI_ENDIAN_BIG ? GUINT64_TO_BE (in) : GUINT64_TO_LE (in)); + + /* Update buffer size */ + offset = self->len; + g_byte_array_set_size (self, self->len + n_bytes); + + /* In Little Endian, we read the bytes from the beginning of the buffer */ + if (endian == QMI_ENDIAN_LITTLE) { + memcpy (&self->data[offset], &tmp, n_bytes); + } + /* In Big Endian, we read the bytes from the end of the buffer */ + else { + guint8 tmp_buffer[8]; + + memcpy (&tmp_buffer[0], &tmp, 8); + memcpy (&self->data[offset], &tmp_buffer[8 - n_bytes], n_bytes); + } + + return TRUE; +} + +/** + * qmi_message_tlv_write_string: + * @self: a #QmiMessage. + * @n_size_prefix_bytes: number of bytes to use in the size prefix. + * @in: string to write. + * @in_length: length of @in, or -1 if @in is NUL-terminated. + * @error: return location for error or %NULL. + * + * Appends a string to the TLV being built. + * + * Fixed-sized strings should give @n_size_prefix_bytes equal to 0. + * + * Returns: %TRUE if the string is successfully added, otherwise %FALSE is returned and @error is set. + * + * Since: 1.12 + */ +gboolean +qmi_message_tlv_write_string (QmiMessage *self, + guint8 n_size_prefix_bytes, + const gchar *in, + gssize in_length, + GError **error) +{ + gsize len; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (in != NULL, FALSE); + g_return_val_if_fail (n_size_prefix_bytes <= 2, FALSE); + + len = (in_length < 0 ? strlen (in) : (gsize) in_length); + + /* Write size prefix first */ + switch (n_size_prefix_bytes) { + case 0: + break; + case 1: + if (len > G_MAXUINT8) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_INVALID_ARGS, + "String too long for a 1 byte size prefix: %" G_GSIZE_FORMAT, len); + return FALSE; + } + if (!qmi_message_tlv_write_guint8 (self, (guint8) len, error)) { + g_prefix_error (error, "Cannot append string 1 byte size prefix"); + return FALSE; + } + break; + case 2: + if (len > G_MAXUINT16) { + g_set_error (error, + QMI_CORE_ERROR, + QMI_CORE_ERROR_INVALID_ARGS, + "String too long for a 2 byte size prefix: %" G_GSIZE_FORMAT, len); + return FALSE; + } + if (!qmi_message_tlv_write_guint16 (self, QMI_ENDIAN_LITTLE, (guint16) len, error)) { + g_prefix_error (error, "Cannot append string 2 byte size prefix"); + return FALSE; + } + break; + default: + g_assert_not_reached (); + } + + /* Check for overflow of string size */ + if (!tlv_error_if_write_overflow (self, len, error)) + return FALSE; + + g_byte_array_append (self, (guint8 *)in, len); + return TRUE; +} + +/*****************************************************************************/ + /** * qmi_message_get_raw_tlv: * @self: a #QmiMessage. @@ -725,7 +1258,7 @@ qmi_message_foreach_raw_tlv (QmiMessage *self, * * Creates a new @type TLV with the value given in @raw, and adds it to the #QmiMessage. * - * Returns: %TRUE if the TLV as successfully added, otherwise %FALSE is returned and @error is set. + * Returns: %TRUE if the TLV is successfully added, otherwise %FALSE is returned and @error is set. */ gboolean qmi_message_add_raw_tlv (QmiMessage *self, diff --git a/src/libqmi-glib/qmi-message.h b/src/libqmi-glib/qmi-message.h index 69daebf..ef08865 100644 --- a/src/libqmi-glib/qmi-message.h +++ b/src/libqmi-glib/qmi-message.h @@ -36,6 +36,7 @@ #include <glib.h> +#include "qmi-utils.h" #include "qmi-enums.h" #include "qmi-errors.h" @@ -82,6 +83,59 @@ gboolean qmi_message_get_version_introduced (QmiMessage *self, guint *major, guint *minor); + +/*****************************************************************************/ +/* TLV builder & writer */ + +gsize qmi_message_tlv_write_init (QmiMessage *self, + guint8 type, + GError **error); +void qmi_message_tlv_write_reset (QmiMessage *self, + gsize tlv_offset); +gboolean qmi_message_tlv_write_complete (QmiMessage *self, + gsize tlv_offset, + GError **error); +gboolean qmi_message_tlv_write_guint8 (QmiMessage *self, + guint8 in, + GError **error); +gboolean qmi_message_tlv_write_gint8 (QmiMessage *self, + gint8 in, + GError **error); +gboolean qmi_message_tlv_write_guint16 (QmiMessage *self, + QmiEndian endian, + guint16 in, + GError **error); +gboolean qmi_message_tlv_write_gint16 (QmiMessage *self, + QmiEndian endian, + gint16 in, + GError **error); +gboolean qmi_message_tlv_write_guint32 (QmiMessage *self, + QmiEndian endian, + guint32 in, + GError **error); +gboolean qmi_message_tlv_write_gint32 (QmiMessage *self, + QmiEndian endian, + gint32 in, + GError **error); +gboolean qmi_message_tlv_write_guint64 (QmiMessage *self, + QmiEndian endian, + guint64 in, + GError **error); +gboolean qmi_message_tlv_write_gint64 (QmiMessage *self, + QmiEndian endian, + gint64 in, + GError **error); +gboolean qmi_message_tlv_write_sized_guint (QmiMessage *self, + guint n_bytes, + QmiEndian endian, + guint64 in, + GError **error); +gboolean qmi_message_tlv_write_string (QmiMessage *self, + guint8 n_size_prefix_bytes, + const gchar *in, + gssize in_length, + GError **error); + /*****************************************************************************/ /* Raw TLV handling */ |