From 98485ad79457dd988df7db83cd28cca94087e1cb Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 24 Dec 2014 12:11:33 -0200 Subject: [PATCH] core/gatt: Don't always issue read-long for ReadValue A remote GATT server may not support the "long read" procedure for all of its attributes (or not at all). This patch revises the implementation of GattCharacteristic1.ReadValue and GattDescriptor1.ReadValue, so that instead of always initiating a "long read" (i.e. ATT Read Blob Req), they first send out a regular read request and then proceed to a long read only if the value fills the current MTU and may potentially have more bytes waiting. --- src/gatt-client.c | 133 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 98 insertions(+), 35 deletions(-) diff --git a/src/gatt-client.c b/src/gatt-client.c index 02ef57ef5..809aca5a0 100644 --- a/src/gatt-client.c +++ b/src/gatt-client.c @@ -202,8 +202,10 @@ static bool parse_value_arg(DBusMessage *msg, uint8_t **value, typedef bool (*async_dbus_op_complete_t)(void *data); struct async_dbus_op { + int ref_count; DBusMessage *msg; void *data; + uint16_t offset; async_dbus_op_complete_t complete; }; @@ -215,6 +217,23 @@ static void async_dbus_op_free(void *data) free(op); } +static struct async_dbus_op *async_dbus_op_ref(struct async_dbus_op *op) +{ + __sync_fetch_and_add(&op->ref_count, 1); + + return op; +} + +static void async_dbus_op_unref(void *data) +{ + struct async_dbus_op *op = data; + + if (__sync_sub_and_fetch(&op->ref_count, 1)) + return; + + async_dbus_op_free(op); +} + static void message_append_byte_array(DBusMessage *msg, const uint8_t *bytes, size_t len) { @@ -269,24 +288,18 @@ static void write_descriptor_cb(struct gatt_db_attribute *attr, int err, GATT_DESCRIPTOR_IFACE, "Value"); } -static void desc_read_long_cb(bool success, uint8_t att_ecode, - const uint8_t *value, uint16_t length, - void *user_data) +static void read_op_cb(struct gatt_db_attribute *attrib, int err, + const uint8_t *value, size_t length, + void *user_data) { struct async_dbus_op *op = user_data; - struct descriptor *desc = op->data; DBusMessage *reply; - desc->in_read = false; - - if (!success) { - reply = create_gatt_dbus_error(op->msg, att_ecode); - goto done; + if (err) { + error("Failed to read attribute"); + return; } - gatt_db_attribute_write(desc->attr, 0, value, length, 0, NULL, - write_descriptor_cb, desc); - reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID); if (!reply) { error("Failed to allocate D-Bus message reply"); @@ -295,10 +308,50 @@ static void desc_read_long_cb(bool success, uint8_t att_ecode, message_append_byte_array(reply, value, length); -done: g_dbus_send_message(btd_get_dbus_connection(), reply); } +static void desc_read_cb(bool success, uint8_t att_ecode, + const uint8_t *value, uint16_t length, + void *user_data) +{ + struct async_dbus_op *op = user_data; + struct descriptor *desc = op->data; + struct service *service = desc->chrc->service; + + if (!success) { + DBusMessage *reply = create_gatt_dbus_error(op->msg, att_ecode); + + desc->in_read = false; + g_dbus_send_message(btd_get_dbus_connection(), reply); + return; + } + + gatt_db_attribute_write(desc->attr, 0, value, length, 0, NULL, + write_descriptor_cb, desc); + + /* + * If the value length is exactly MTU-1, then we may not have read the + * entire value. Perform a long read to obtain the rest, otherwise, + * we're done. + */ + if (length == bt_gatt_client_get_mtu(service->client->gatt) - 1) { + op->offset += length; + if (bt_gatt_client_read_long_value(service->client->gatt, + desc->handle, + op->offset, + desc_read_cb, + async_dbus_op_ref(op), + async_dbus_op_unref)) + return; + } + + desc->in_read = false; + + /* Read the stored data from db */ + gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_op_cb, op); +} + static DBusMessage *descriptor_read_value(DBusConnection *conn, DBusMessage *msg, void *user_data) { @@ -316,9 +369,9 @@ static DBusMessage *descriptor_read_value(DBusConnection *conn, op->msg = dbus_message_ref(msg); op->data = desc; - if (bt_gatt_client_read_long_value(gatt, desc->handle, 0, - desc_read_long_cb, op, - async_dbus_op_free)) { + if (bt_gatt_client_read_value(gatt, desc->handle, desc_read_cb, + async_dbus_op_ref(op), + async_dbus_op_unref)) { desc->in_read = true; return NULL; } @@ -667,34 +720,44 @@ static void write_characteristic_cb(struct gatt_db_attribute *attr, int err, GATT_CHARACTERISTIC_IFACE, "Value"); } -static void chrc_read_long_cb(bool success, uint8_t att_ecode, - const uint8_t *value, uint16_t length, - void *user_data) +static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value, + uint16_t length, void *user_data) { struct async_dbus_op *op = user_data; struct characteristic *chrc = op->data; - DBusMessage *reply; - - chrc->in_read = false; + struct service *service = chrc->service; if (!success) { - reply = create_gatt_dbus_error(op->msg, att_ecode); - goto done; + DBusMessage *reply = create_gatt_dbus_error(op->msg, att_ecode); + + chrc->in_read = false; + g_dbus_send_message(btd_get_dbus_connection(), reply); + return ; } - gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL, + gatt_db_attribute_write(chrc->attr, 0, value, length, op->offset, NULL, write_characteristic_cb, chrc); - reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID); - if (!reply) { - error("Failed to allocate D-Bus message reply"); - return; + /* + * If the value length is exactly MTU-1, then we may not have read the + * entire value. Perform a long read to obtain the rest, otherwise, + * we're done. + */ + if (length == bt_gatt_client_get_mtu(service->client->gatt) - 1) { + op->offset += length; + if (bt_gatt_client_read_long_value(service->client->gatt, + chrc->value_handle, + op->offset, + chrc_read_cb, + async_dbus_op_ref(op), + async_dbus_op_unref)) + return; } - message_append_byte_array(reply, value, length); + chrc->in_read = false; -done: - g_dbus_send_message(btd_get_dbus_connection(), reply); + /* Read the stored data from db */ + gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_op_cb, op); } static DBusMessage *characteristic_read_value(DBusConnection *conn, @@ -714,9 +777,9 @@ static DBusMessage *characteristic_read_value(DBusConnection *conn, op->msg = dbus_message_ref(msg); op->data = chrc; - if (bt_gatt_client_read_long_value(gatt, chrc->value_handle, 0, - chrc_read_long_cb, op, - async_dbus_op_free)) { + if (bt_gatt_client_read_value(gatt, chrc->value_handle, chrc_read_cb, + async_dbus_op_ref(op), + async_dbus_op_unref)) { chrc->in_read = true; return NULL; } -- 2.47.3