summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@aleksander.es>2014-10-09 21:25:31 +0200
committerAleksander Morgado <aleksander@aleksander.es>2014-11-09 20:15:56 +0100
commit809356fd95c1a2956a0df443c594156e172bde33 (patch)
tree9b3ab57a3d04a7edd12179537c9d5465cbd096cd
parent75d2b34c8c3cb78171fe0c3eb4d2dfd1c89c23ba (diff)
libqmi-glib,message: new TLV reader API
-rw-r--r--docs/reference/libqmi-glib/libqmi-glib-common.sections14
-rw-r--r--src/libqmi-glib/qmi-message.c667
-rw-r--r--src/libqmi-glib/qmi-message.h79
3 files changed, 760 insertions, 0 deletions
diff --git a/docs/reference/libqmi-glib/libqmi-glib-common.sections b/docs/reference/libqmi-glib/libqmi-glib-common.sections
index cd78c1d..a34226d 100644
--- a/docs/reference/libqmi-glib/libqmi-glib-common.sections
+++ b/docs/reference/libqmi-glib/libqmi-glib-common.sections
@@ -860,6 +860,20 @@ qmi_message_tlv_write_guint64
qmi_message_tlv_write_gint64
qmi_message_tlv_write_sized_guint
qmi_message_tlv_write_string
+<SUBSECTION TLV reader>
+qmi_message_tlv_read_init
+qmi_message_tlv_read_guint8
+qmi_message_tlv_read_gint8
+qmi_message_tlv_read_guint16
+qmi_message_tlv_read_gint16
+qmi_message_tlv_read_guint32
+qmi_message_tlv_read_gint32
+qmi_message_tlv_read_guint64
+qmi_message_tlv_read_gint64
+qmi_message_tlv_read_sized_guint
+qmi_message_tlv_read_gfloat
+qmi_message_tlv_read_string
+qmi_message_tlv_read_fixed_size_string
<SUBSECTION RAW TLVs>
QmiMessageForeachRawTlvFn
qmi_message_foreach_raw_tlv
diff --git a/src/libqmi-glib/qmi-message.c b/src/libqmi-glib/qmi-message.c
index 3377d1d..07ed279 100644
--- a/src/libqmi-glib/qmi-message.c
+++ b/src/libqmi-glib/qmi-message.c
@@ -1163,6 +1163,673 @@ qmi_message_tlv_write_string (QmiMessage *self,
}
/*****************************************************************************/
+/* TLV reader */
+
+/**
+ * qmi_message_tlv_read_init:
+ * @self: a #QmiMessage.
+ * @type: specific ID of the TLV to read.
+ * @out_tlv_length: optional return location for the TLV size.
+ * @error: return location for error or %NULL.
+ *
+ * Starts reading a given TLV from the #QmiMessage.
+ *
+ * Returns: the offset where the TLV starts, or 0 if an error happens.
+ *
+ * Since: 1.12
+ */
+gsize
+qmi_message_tlv_read_init (QmiMessage *self,
+ guint8 type,
+ guint16 *out_tlv_length,
+ GError **error)
+{
+ struct tlv *tlv;
+ guint16 tlv_length;
+
+ g_return_val_if_fail (self != NULL, 0);
+ g_return_val_if_fail (self->len > 0, 0);
+
+ for (tlv = qmi_tlv_first (self); tlv; tlv = qmi_tlv_next (self, tlv)) {
+ if (tlv->type == type)
+ break;
+ }
+
+ if (!tlv) {
+ g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_TLV_NOT_FOUND,
+ "TLV 0x%02X not found", type);
+ return 0;
+ }
+
+ tlv_length = GUINT16_FROM_LE (tlv->length);
+ if (!tlv_length) {
+ g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_TLV_EMPTY,
+ "TLV 0x%02X is empty", type);
+ return 0;
+ }
+
+ if (((guint8 *) tlv_next (tlv)) > ((guint8 *) qmi_end (self))) {
+ g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_TLV_TOO_LONG,
+ "Invalid length for TLV 0x%02X: %" G_GUINT16_FORMAT, type, tlv_length);
+ return 0;
+ }
+
+ if (out_tlv_length)
+ *out_tlv_length = tlv_length;
+
+ return (((guint8 *)tlv) - self->data);
+}
+
+static const guint8 *
+tlv_error_if_read_overflow (QmiMessage *self,
+ gsize tlv_offset,
+ gsize offset,
+ gsize len,
+ GError **error)
+{
+ const guint8 *ptr;
+ struct tlv *tlv;
+
+ tlv = (struct tlv *) &(self->data[tlv_offset]);
+ ptr = ((guint8 *)tlv) + sizeof (struct tlv) + offset;
+
+ if (((guint8 *)(ptr + len) > (guint8 *)tlv_next (tlv)) ||
+ ((guint8 *)(ptr + len) > (guint8 *)qmi_end (self))) {
+ g_set_error (error,
+ QMI_CORE_ERROR,
+ QMI_CORE_ERROR_TLV_TOO_LONG,
+ "Reading TLV would overflow");
+ return NULL;
+ }
+
+ return ptr;
+}
+
+/**
+ * qmi_message_tlv_read_guint8:
+ * @self: a #QmiMessage.
+ * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
+ * @offset: address of the offset within the TLV value.
+ * @out: return location for the read #guint8.
+ * @error: return location for error or %NULL.
+ *
+ * Reads an unsigned byte from the TLV.
+ *
+ * @offset needs to point to a valid @gsize specifying the index to start
+ * reading from within the TLV value (0 for the first item). If the variable
+ * is successfully read, @offset will be updated to point past the read item.
+ *
+ * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+qmi_message_tlv_read_guint8 (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ guint8 *out,
+ GError **error)
+{
+ const guint8 *ptr;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (offset != NULL, FALSE);
+ g_return_val_if_fail (out != NULL, FALSE);
+
+ if (!(ptr = tlv_error_if_read_overflow (self, tlv_offset, *offset, sizeof (*out), error)))
+ return FALSE;
+
+ *offset = *offset + 1;
+ *out = *ptr;
+ return TRUE;
+}
+
+/**
+ * qmi_message_tlv_read_gint8:
+ * @self: a #QmiMessage.
+ * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
+ * @offset: address of a the offset within the TLV value.
+ * @out: return location for the read #gint8.
+ * @error: return location for error or %NULL.
+ *
+ * Reads a signed byte from the TLV.
+ *
+ * @offset needs to point to a valid @gsize specifying the index to start
+ * reading from within the TLV value (0 for the first item). If the variable
+ * is successfully read, @offset will be updated to point past the read item.
+ *
+ * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+qmi_message_tlv_read_gint8 (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ gint8 *out,
+ GError **error)
+{
+ const guint8 *ptr;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (offset != NULL, FALSE);
+ g_return_val_if_fail (out != NULL, FALSE);
+
+ if (!(ptr = tlv_error_if_read_overflow (self, tlv_offset, *offset, sizeof (*out), error)))
+ return FALSE;
+
+ *out = (gint8)(*ptr);
+ *offset = *offset + 1;
+ return TRUE;
+}
+
+/**
+ * qmi_message_tlv_read_guint16:
+ * @self: a #QmiMessage.
+ * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
+ * @offset: address of a the offset within the TLV value.
+ * @endian: source endianness, which will be swapped to host byte order if necessary.
+ * @out: return location for the read #guint16.
+ * @error: return location for error or %NULL.
+ *
+ * Reads an unsigned 16-bit integer from the TLV, in host byte order.
+ *
+ * @offset needs to point to a valid @gsize specifying the index to start
+ * reading from within the TLV value (0 for the first item). If the variable
+ * is successfully read, @offset will be updated to point past the read item.
+ *
+ * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+qmi_message_tlv_read_guint16 (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ QmiEndian endian,
+ guint16 *out,
+ GError **error)
+{
+ const guint8 *ptr;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (offset != NULL, FALSE);
+ g_return_val_if_fail (out != NULL, FALSE);
+
+ if (!(ptr = tlv_error_if_read_overflow (self, tlv_offset, *offset, sizeof (*out), error)))
+ return FALSE;
+
+ memcpy (out, ptr, 2);
+ if (endian == QMI_ENDIAN_BIG)
+ *out = GUINT16_FROM_BE (*out);
+ else
+ *out = GUINT16_FROM_LE (*out);
+ *offset = *offset + 2;
+ return TRUE;
+}
+
+/**
+ * qmi_message_tlv_read_gint16:
+ * @self: a #QmiMessage.
+ * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
+ * @offset: address of a the offset within the TLV value.
+ * @endian: source endianness, which will be swapped to host byte order if necessary.
+ * @out: return location for the read #gint16.
+ * @error: return location for error or %NULL.
+ *
+ * Reads a signed 16-bit integer from the TLV, in host byte order.
+ *
+ * @offset needs to point to a valid @gsize specifying the index to start
+ * reading from within the TLV value (0 for the first item). If the variable
+ * is successfully read, @offset will be updated to point past the read item.
+ *
+ * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+qmi_message_tlv_read_gint16 (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ QmiEndian endian,
+ gint16 *out,
+ GError **error)
+{
+ const guint8 *ptr;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (offset != NULL, FALSE);
+ g_return_val_if_fail (out != NULL, FALSE);
+
+ if (!(ptr = tlv_error_if_read_overflow (self, tlv_offset, *offset, sizeof (*out), error)))
+ return FALSE;
+
+ memcpy (out, ptr, 2);
+ if (endian == QMI_ENDIAN_BIG)
+ *out = GUINT16_FROM_BE (*out);
+ else
+ *out = GUINT16_FROM_LE (*out);
+ *offset = *offset + 2;
+ return TRUE;
+}
+
+/**
+ * qmi_message_tlv_read_guint32:
+ * @self: a #QmiMessage.
+ * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
+ * @offset: address of a the offset within the TLV value.
+ * @endian: source endianness, which will be swapped to host byte order if necessary.
+ * @out: return location for the read #guint32.
+ * @error: return location for error or %NULL.
+ *
+ * Reads an unsigned 32-bit integer from the TLV, in host byte order.
+ *
+ * @offset needs to point to a valid @gsize specifying the index to start
+ * reading from within the TLV value (0 for the first item). If the variable
+ * is successfully read, @offset will be updated to point past the read item.
+ *
+ * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+qmi_message_tlv_read_guint32 (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ QmiEndian endian,
+ guint32 *out,
+ GError **error)
+{
+ const guint8 *ptr;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (offset != NULL, FALSE);
+ g_return_val_if_fail (out != NULL, FALSE);
+
+ if (!(ptr = tlv_error_if_read_overflow (self, tlv_offset, *offset, sizeof (*out), error)))
+ return FALSE;
+
+ memcpy (out, ptr, 4);
+ if (endian == QMI_ENDIAN_BIG)
+ *out = GUINT32_FROM_BE (*out);
+ else
+ *out = GUINT32_FROM_LE (*out);
+ *offset = *offset + 4;
+ return TRUE;
+}
+
+/**
+ * qmi_message_tlv_read_gint32:
+ * @self: a #QmiMessage.
+ * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
+ * @offset: address of a the offset within the TLV value.
+ * @endian: source endianness, which will be swapped to host byte order if necessary.
+ * @out: return location for the read #gint32.
+ * @error: return location for error or %NULL.
+ *
+ * Reads a signed 32-bit integer from the TLV, in host byte order.
+ *
+ * @offset needs to point to a valid @gsize specifying the index to start
+ * reading from within the TLV value (0 for the first item). If the variable
+ * is successfully read, @offset will be updated to point past the read item.
+ *
+ * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+qmi_message_tlv_read_gint32 (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ QmiEndian endian,
+ gint32 *out,
+ GError **error)
+{
+ const guint8 *ptr;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (offset != NULL, FALSE);
+ g_return_val_if_fail (out != NULL, FALSE);
+
+ if (!(ptr = tlv_error_if_read_overflow (self, tlv_offset, *offset, sizeof (*out), error)))
+ return FALSE;
+
+ memcpy (out, ptr, 4);
+ if (endian == QMI_ENDIAN_BIG)
+ *out = GINT32_FROM_BE (*out);
+ else
+ *out = GINT32_FROM_LE (*out);
+ *offset = *offset + 4;
+ return TRUE;
+}
+
+/**
+ * qmi_message_tlv_read_guint64:
+ * @self: a #QmiMessage.
+ * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
+ * @offset: address of a the offset within the TLV value.
+ * @endian: source endianness, which will be swapped to host byte order if necessary.
+ * @out: return location for the read #guint64.
+ * @error: return location for error or %NULL.
+ *
+ * Reads an unsigned 64-bit integer from the TLV, in host byte order.
+ *
+ * @offset needs to point to a valid @gsize specifying the index to start
+ * reading from within the TLV value (0 for the first item). If the variable
+ * is successfully read, @offset will be updated to point past the read item.
+ *
+ * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+qmi_message_tlv_read_guint64 (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ QmiEndian endian,
+ guint64 *out,
+ GError **error)
+{
+ const guint8 *ptr;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (offset != NULL, FALSE);
+ g_return_val_if_fail (out != NULL, FALSE);
+
+ if (!(ptr = tlv_error_if_read_overflow (self, tlv_offset, *offset, sizeof (*out), error)))
+ return FALSE;
+
+ memcpy (out, ptr, 8);
+ if (endian == QMI_ENDIAN_BIG)
+ *out = GUINT64_FROM_BE (*out);
+ else
+ *out = GUINT64_FROM_LE (*out);
+ *offset = *offset + 8;
+ return TRUE;
+}
+
+/**
+ * qmi_message_tlv_read_gint64:
+ * @self: a #QmiMessage.
+ * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
+ * @offset: address of a the offset within the TLV value.
+ * @endian: source endianness, which will be swapped to host byte order if necessary.
+ * @out: return location for the read #gint64.
+ * @error: return location for error or %NULL.
+ *
+ * Reads a signed 64-bit integer from the TLV, in host byte order.
+ *
+ * @offset needs to point to a valid @gsize specifying the index to start
+ * reading from within the TLV value (0 for the first item). If the variable
+ * is successfully read, @offset will be updated to point past the read item.
+ *
+ * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+qmi_message_tlv_read_gint64 (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ QmiEndian endian,
+ gint64 *out,
+ GError **error)
+{
+ const guint8 *ptr;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (offset != NULL, FALSE);
+ g_return_val_if_fail (out != NULL, FALSE);
+
+ if (!(ptr = tlv_error_if_read_overflow (self, tlv_offset, *offset, sizeof (*out), error)))
+ return FALSE;
+
+ memcpy (out, ptr, 8);
+ if (endian == QMI_ENDIAN_BIG)
+ *out = GINT64_FROM_BE (*out);
+ else
+ *out = GINT64_FROM_LE (*out);
+ *offset = *offset + 8;
+ return TRUE;
+}
+
+/**
+ * qmi_message_tlv_read_sized_guint:
+ * @self: a #QmiMessage.
+ * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
+ * @offset: address of a the offset within the TLV value.
+ * @n_bytes: number of bytes to read.
+ * @endian: source endianness, which will be swapped to host byte order if necessary.
+ * @out: return location for the read #guint64.
+ * @error: return location for error or %NULL.
+ *
+ * Reads a @b_bytes-sized integer from the TLV, in host byte order.
+ *
+ * @offset needs to point to a valid @gsize specifying the index to start
+ * reading from within the TLV value (0 for the first item). If the variable
+ * is successfully read, @offset will be updated to point past the read item.
+ *
+ * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+qmi_message_tlv_read_sized_guint (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ guint n_bytes,
+ QmiEndian endian,
+ guint64 *out,
+ GError **error)
+{
+ const guint8 *ptr;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (offset != NULL, FALSE);
+ g_return_val_if_fail (out != NULL, FALSE);
+ g_return_val_if_fail ((n_bytes == 1 ||
+ n_bytes == 2 ||
+ n_bytes == 4 ||
+ n_bytes == 8), FALSE);
+
+ if (!(ptr = tlv_error_if_read_overflow (self, tlv_offset, *offset, n_bytes, error)))
+ return FALSE;
+
+ *out = 0;
+
+ /* In Little Endian, we copy the bytes to the beginning of the output
+ * buffer. */
+ if (endian == QMI_ENDIAN_LITTLE) {
+ memcpy (out, ptr, n_bytes);
+ *out = GUINT64_FROM_LE (*out);
+ }
+ /* In Big Endian, we copy the bytes to the end of the output buffer */
+ else {
+ guint8 tmp[8] = { 0 };
+
+ memcpy (&tmp[8 - n_bytes], ptr, n_bytes);
+ memcpy (out, &tmp[0], 8);
+ *out = GUINT64_FROM_BE (*out);
+ }
+
+ *offset = *offset + n_bytes;
+ return TRUE;
+}
+
+/**
+ * qmi_message_tlv_read_gfloat:
+ * @self: a #QmiMessage.
+ * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
+ * @offset: address of a the offset within the TLV value.
+ * @out: return location for the read #gfloat.
+ * @error: return location for error or %NULL.
+ *
+ * Reads a 32-bit floating-point number from the TLV.
+ *
+ * @offset needs to point to a valid @gsize specifying the index to start
+ * reading from within the TLV value (0 for the first item). If the variable
+ * is successfully read, @offset will be updated to point past the read item.
+ *
+ * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+qmi_message_tlv_read_gfloat (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ gfloat *out,
+ GError **error)
+{
+ const guint8 *ptr;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (offset != NULL, FALSE);
+ g_return_val_if_fail (out != NULL, FALSE);
+
+ if (!(ptr = tlv_error_if_read_overflow (self, tlv_offset, *offset, 4, error)))
+ return FALSE;
+
+ /* Yeah, do this for now */
+ memcpy (out, ptr, 4);
+ *offset = *offset + 4;
+ return TRUE;
+}
+
+/**
+ * qmi_message_tlv_read_string:
+ * @self: a #QmiMessage.
+ * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
+ * @offset: address of a the offset within the TLV value.
+ * @n_size_prefix_bytes: number of bytes used in the size prefix.
+ * @max_size: maximum number of bytes to read, or 0 to read all available bytes.
+ * @out: return location for the read string. The returned value should be freed with g_free().
+ * @error: return location for error or %NULL.
+ *
+ * Reads a string from the TLV.
+ *
+ * @offset needs to point to a valid @gsize specifying the index to start
+ * reading from within the TLV value (0 for the first item). If the variable
+ * is successfully read, @offset will be updated to point past the read item.
+ *
+ * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+qmi_message_tlv_read_string (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ guint8 n_size_prefix_bytes,
+ guint16 max_size,
+ gchar **out,
+ GError **error)
+{
+ const guint8 *ptr;
+ guint16 string_length;
+ guint16 valid_string_length;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (offset != NULL, FALSE);
+ g_return_val_if_fail (out != NULL, FALSE);
+ g_return_val_if_fail (n_size_prefix_bytes <= 2, FALSE);
+
+ switch (n_size_prefix_bytes) {
+ case 0: {
+ struct tlv *tlv;
+
+ if (!tlv_error_if_read_overflow (self, tlv_offset, *offset, 0, error))
+ return FALSE;
+
+ /* If no length prefix given, read the remaining TLV buffer into a string */
+ tlv = (struct tlv *) &(self->data[tlv_offset]);
+ string_length = (tlv->length - *offset);
+ break;
+ }
+ case 1: {
+ guint8 string_length_8;
+
+ if (!qmi_message_tlv_read_guint8 (self, tlv_offset, offset, &string_length_8, error))
+ return FALSE;
+ string_length = (guint16) string_length_8;
+ break;
+ }
+ case 2:
+ if (!qmi_message_tlv_read_guint16 (self, tlv_offset, offset, QMI_ENDIAN_LITTLE, &string_length, error))
+ return FALSE;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (string_length == 0) {
+ *out = g_strdup ("");
+ return TRUE;
+ }
+
+ if (max_size > 0 && string_length > max_size)
+ valid_string_length = max_size;
+ else
+ valid_string_length = string_length;
+
+ if (!(ptr = tlv_error_if_read_overflow (self, tlv_offset, *offset, valid_string_length, error)))
+ return FALSE;
+
+ *out = g_malloc (valid_string_length + 1);
+ memcpy (*out, ptr, valid_string_length);
+ (*out)[valid_string_length] = '\0';
+
+ *offset = (*offset + string_length);
+ return TRUE;
+}
+
+/**
+ * qmi_message_tlv_read_fixed_size_string:
+ * @self: a #QmiMessage.
+ * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
+ * @offset: address of a the offset within the TLV value.
+ * @string_length: amount of bytes to read.
+ * @out: buffer preallocated by the client, with at least @string_length bytes.
+ * @error: return location for error or %NULL.
+ *
+ * Reads a string from the TLV.
+ *
+ * The string written in @out will need to be NUL-terminated by the caller.
+ *
+ * @offset needs to point to a valid @gsize specifying the index to start
+ * reading from within the TLV value (0 for the first item). If the variable
+ * is successfully read, @offset will be updated to point past the read item.
+ *
+ * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+qmi_message_tlv_read_fixed_size_string (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ guint16 string_length,
+ gchar *out,
+ GError **error)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (offset != NULL, FALSE);
+ g_return_val_if_fail (out != NULL, FALSE);
+
+ if (string_length > 0) {
+ const guint8 *ptr;
+
+ if (!(ptr = tlv_error_if_read_overflow (self, tlv_offset, *offset, string_length, error)))
+ return FALSE;
+
+ memcpy (out, ptr, string_length);
+ }
+
+ *offset = (*offset + string_length);
+ return TRUE;
+}
+
+/*****************************************************************************/
/**
* qmi_message_get_raw_tlv:
diff --git a/src/libqmi-glib/qmi-message.h b/src/libqmi-glib/qmi-message.h
index d6b15f8..1c7331c 100644
--- a/src/libqmi-glib/qmi-message.h
+++ b/src/libqmi-glib/qmi-message.h
@@ -137,6 +137,85 @@ gboolean qmi_message_tlv_write_string (QmiMessage *self,
GError **error);
/*****************************************************************************/
+/* TLV reader */
+
+gsize qmi_message_tlv_read_init (QmiMessage *self,
+ guint8 type,
+ guint16 *out_tlv_length,
+ GError **error);
+gboolean qmi_message_tlv_read_guint8 (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ guint8 *out,
+ GError **error);
+gboolean qmi_message_tlv_read_gint8 (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ gint8 *out,
+ GError **error);
+gboolean qmi_message_tlv_read_guint16 (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ QmiEndian endian,
+ guint16 *out,
+ GError **error);
+gboolean qmi_message_tlv_read_gint16 (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ QmiEndian endian,
+ gint16 *out,
+ GError **error);
+gboolean qmi_message_tlv_read_guint32 (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ QmiEndian endian,
+ guint32 *out,
+ GError **error);
+gboolean qmi_message_tlv_read_gint32 (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ QmiEndian endian,
+ gint32 *out,
+ GError **error);
+gboolean qmi_message_tlv_read_guint64 (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ QmiEndian endian,
+ guint64 *out,
+ GError **error);
+gboolean qmi_message_tlv_read_gint64 (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ QmiEndian endian,
+ gint64 *out,
+ GError **error);
+gboolean qmi_message_tlv_read_sized_guint (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ guint n_bytes,
+ QmiEndian endian,
+ guint64 *out,
+ GError **error);
+gboolean qmi_message_tlv_read_gfloat (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ gfloat *out,
+ GError **error);
+gboolean qmi_message_tlv_read_string (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ guint8 n_size_prefix_bytes,
+ guint16 max_size,
+ gchar **out,
+ GError **error);
+gboolean qmi_message_tlv_read_fixed_size_string (QmiMessage *self,
+ gsize tlv_offset,
+ gsize *offset,
+ guint16 string_length,
+ gchar *out,
+ GError **error);
+
+/*****************************************************************************/
/* Raw TLV handling */
typedef void (* QmiMessageForeachRawTlvFn) (guint8 type,