aboutsummaryrefslogtreecommitdiff
path: root/attrib
diff options
context:
space:
mode:
authorBrian Gix <bgix@codeaurora.org>2011-01-06 16:39:43 -0800
committerJohan Hedberg <johan.hedberg@nokia.com>2011-01-07 21:31:34 +0200
commitacdc181a5d97687f17815953db052e1534ddafb8 (patch)
treeb8bf1f3b2706766e0d0973d48fa1414211feb352 /attrib
parent7f0b31cbb436a93e41f1b67cbbd2e7d217b9d0c7 (diff)
downloadbluez-acdc181a5d97687f17815953db052e1534ddafb8.tar.gz
Fix gatt_read_char() to support long Attrib Values
Fix gatt_read_char() to support long Attribute Values by recognizing that results longer that 21 octets may include data beyond what has been returned with the first read. Extra data is obtained by issuing READ_BLOB requests until either a result is returned shorter than 22 octets, or an error is recieved indicating that no further data is available. The API for this function has not changed.
Diffstat (limited to 'attrib')
-rw-r--r--attrib/gatt.c130
1 files changed, 128 insertions, 2 deletions
diff --git a/attrib/gatt.c b/attrib/gatt.c
index 320759f8..ae33211e 100644
--- a/attrib/gatt.c
+++ b/attrib/gatt.c
@@ -97,15 +97,141 @@ guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end,
pdu, plen, func, user_data, NULL);
}
+struct read_long_data {
+ GAttrib *attrib;
+ GAttribResultFunc func;
+ gpointer user_data;
+ guint8 *buffer;
+ guint16 size;
+ guint16 handle;
+ guint id;
+ gint ref;
+};
+
+static void read_long_destroy(gpointer user_data)
+{
+ struct read_long_data *long_read = user_data;
+
+ if (g_atomic_int_dec_and_test(&long_read->ref) == FALSE)
+ return;
+
+ if (long_read->buffer != NULL)
+ g_free(long_read->buffer);
+
+ g_free(long_read);
+}
+
+static void read_blob_helper(guint8 status, const guint8 *rpdu, guint16 rlen,
+ gpointer user_data)
+{
+ struct read_long_data *long_read = user_data;
+ uint8_t pdu[ATT_DEFAULT_MTU];
+ guint8 *tmp;
+ guint16 plen;
+ guint id;
+
+ if (status != 0 || rlen == 1) {
+ status = 0;
+ goto done;
+ }
+
+ tmp = g_try_realloc(long_read->buffer, long_read->size + rlen - 1);
+
+ if (tmp == NULL) {
+ status = ATT_ECODE_INSUFF_RESOURCES;
+ goto done;
+ }
+
+ memcpy(&tmp[long_read->size], &rpdu[1], rlen - 1);
+ long_read->buffer = tmp;
+ long_read->size += rlen - 1;
+
+ if (rlen < ATT_DEFAULT_MTU)
+ goto done;
+
+ plen = enc_read_blob_req(long_read->handle, long_read->size - 1,
+ pdu, sizeof(pdu));
+ id = g_attrib_send(long_read->attrib, long_read->id,
+ ATT_OP_READ_BLOB_REQ, pdu, plen,
+ read_blob_helper, long_read, read_long_destroy);
+
+ if (id != 0) {
+ g_atomic_int_inc(&long_read->ref);
+ return;
+ }
+
+ status = ATT_ECODE_IO;
+
+done:
+ long_read->func(status, long_read->buffer, long_read->size,
+ long_read->user_data);
+}
+
+static void read_char_helper(guint8 status, const guint8 *rpdu,
+ guint16 rlen, gpointer user_data)
+{
+ struct read_long_data *long_read = user_data;
+ uint8_t pdu[ATT_DEFAULT_MTU];
+ guint16 plen;
+ guint id;
+
+ if (status != 0 || rlen < ATT_DEFAULT_MTU)
+ goto done;
+
+ long_read->buffer = g_malloc(rlen);
+
+ if (long_read->buffer == NULL)
+ goto done;
+
+ memcpy(long_read->buffer, rpdu, rlen);
+ long_read->size = rlen;
+
+ plen = enc_read_blob_req(long_read->handle, rlen - 1, pdu, sizeof(pdu));
+ id = g_attrib_send(long_read->attrib, long_read->id,
+ ATT_OP_READ_BLOB_REQ, pdu, plen, read_blob_helper,
+ long_read, read_long_destroy);
+
+ if (id != 0) {
+ g_atomic_int_inc(&long_read->ref);
+ return;
+ }
+
+ status = ATT_ECODE_IO;
+
+done:
+ long_read->func(status, rpdu, rlen, long_read->user_data);
+}
+
guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func,
gpointer user_data)
{
uint8_t pdu[ATT_DEFAULT_MTU];
guint16 plen;
+ guint id;
+ struct read_long_data *long_read;
+
+ long_read = g_try_new0(struct read_long_data, 1);
+
+ if (long_read == NULL)
+ return 0;
+
+ long_read->attrib = attrib;
+ long_read->func = func;
+ long_read->user_data = user_data;
+ long_read->handle = handle;
plen = enc_read_req(handle, pdu, sizeof(pdu));
- return g_attrib_send(attrib, 0, ATT_OP_READ_REQ, pdu, plen, func,
- user_data, NULL);
+ id = g_attrib_send(attrib, 0, ATT_OP_READ_REQ, pdu, plen,
+ read_char_helper, long_read, read_long_destroy);
+
+ if (id == 0)
+ g_free(long_read);
+ else {
+ g_atomic_int_inc(&long_read->ref);
+ long_read->id = id;
+ }
+
+ return id;
}
guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,