From: Luiz Augusto von Dentz <luiz.von.dentz@xxxxxxxxx> This queues ReadValue messages if the offset is the same so all the client interested in reading the attribute value are replied when the operation is completed. --- src/gatt-client.c | 318 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 163 insertions(+), 155 deletions(-) diff --git a/src/gatt-client.c b/src/gatt-client.c index 6fc0d19..c2a888f 100644 --- a/src/gatt-client.c +++ b/src/gatt-client.c @@ -74,6 +74,17 @@ struct service { struct queue *chrcs; }; +typedef bool (*async_dbus_op_complete_t)(void *data); + +struct async_dbus_op { + int ref_count; + unsigned int id; + struct queue *msgs; + void *data; + uint16_t offset; + async_dbus_op_complete_t complete; +}; + struct characteristic { struct service *service; struct gatt_db_attribute *attr; @@ -85,8 +96,8 @@ struct characteristic { bt_uuid_t uuid; char *path; - unsigned int read_id; - unsigned int write_id; + struct async_dbus_op *read_op; + struct async_dbus_op *write_op; struct queue *descs; @@ -101,8 +112,8 @@ struct descriptor { bt_uuid_t uuid; char *path; - unsigned int read_id; - unsigned int write_id; + struct async_dbus_op *read_op; + struct async_dbus_op *write_op; }; static bool uuid_cmp(const bt_uuid_t *uuid, uint16_t u16) @@ -205,22 +216,11 @@ static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len) return 0; } -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; -}; - static void async_dbus_op_free(void *data) { struct async_dbus_op *op = data; - if (op->msg) - dbus_message_unref(op->msg); + queue_destroy(op->msgs, (void *)dbus_message_unref); free(op); } @@ -296,27 +296,44 @@ static void write_descriptor_cb(struct gatt_db_attribute *attr, int err, GATT_DESCRIPTOR_IFACE, "Value"); } -static void read_op_cb(struct gatt_db_attribute *attrib, int err, - const uint8_t *value, size_t length, - void *user_data) +static void async_dbus_op_reply(struct async_dbus_op *op, int err, + const uint8_t *value, size_t length) { - struct async_dbus_op *op = user_data; + const struct queue_entry *entry; DBusMessage *reply; - if (err) { - error("Failed to read attribute"); - return; - } + op->id = 0; - reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID); - if (!reply) { - error("Failed to allocate D-Bus message reply"); - return; + for (entry = queue_get_entries(op->msgs); entry; entry = entry->next) { + DBusMessage *msg = entry->data; + + if (err) { + reply = err > 0 ? create_gatt_dbus_error(msg, err) : + btd_error_failed(msg, strerror(err)); + goto send_reply; + } + + reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID); + if (!reply) { + error("Failed to allocate D-Bus message reply"); + return; + } + + if (value) + message_append_byte_array(reply, value, length); + +send_reply: + g_dbus_send_message(btd_get_dbus_connection(), reply); } +} - message_append_byte_array(reply, value, length); +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; - g_dbus_send_message(btd_get_dbus_connection(), reply); + async_dbus_op_reply(op, err, value, length); } static void desc_read_cb(bool success, uint8_t att_ecode, @@ -326,7 +343,6 @@ static void desc_read_cb(bool success, uint8_t att_ecode, struct async_dbus_op *op = user_data; struct descriptor *desc = op->data; struct service *service = desc->chrc->service; - DBusMessage *reply; if (!success) goto fail; @@ -337,6 +353,7 @@ static void desc_read_cb(bool success, uint8_t att_ecode, if (!gatt_db_attribute_write(desc->attr, op->offset, value, length, 0, NULL, write_descriptor_cb, desc)) { error("Failed to store attribute"); + att_ecode = BT_ATT_ERROR_UNLIKELY; goto fail; } @@ -347,32 +364,30 @@ static void desc_read_cb(bool success, uint8_t att_ecode, */ if (length == bt_gatt_client_get_mtu(service->client->gatt) - 1) { op->offset += length; - desc->read_id = bt_gatt_client_read_long_value( - service->client->gatt, + op->id = 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); - if (desc->read_id) + if (op->id) return; } /* Read the stored data from db */ if (!gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_op_cb, op)) { error("Failed to read database"); + att_ecode = BT_ATT_ERROR_UNLIKELY; goto fail; } - desc->read_id = 0; + desc->read_op = NULL; return; fail: - reply = create_gatt_dbus_error(op->msg, att_ecode); - desc->read_id = 0; - g_dbus_send_message(btd_get_dbus_connection(), reply); - return; + async_dbus_op_reply(op, att_ecode, NULL, 0); + desc->read_op = NULL; } static int parse_options(DBusMessageIter *iter, uint16_t *offset) @@ -408,19 +423,45 @@ static int parse_options(DBusMessageIter *iter, uint16_t *offset) return 0; } -static unsigned int read_value(struct bt_gatt_client *gatt, uint16_t handle, - bt_gatt_client_read_callback_t callback, - struct async_dbus_op *op) +static struct async_dbus_op *async_dbus_op_new(DBusMessage *msg, void *data) +{ + struct async_dbus_op *op; + + op = new0(struct async_dbus_op, 1); + op->msgs = queue_new(); + queue_push_tail(op->msgs, dbus_message_ref(msg)); + op->data = data; + + return op; +} + +static struct async_dbus_op *read_value(struct bt_gatt_client *gatt, + DBusMessage *msg, uint16_t handle, + uint16_t offset, + bt_gatt_client_read_callback_t callback, + void *data) { + struct async_dbus_op *op; + + op = async_dbus_op_new(msg, data); + op->offset = offset; + if (op->offset) - return bt_gatt_client_read_long_value(gatt, handle, op->offset, + op->id = bt_gatt_client_read_long_value(gatt, handle, offset, callback, async_dbus_op_ref(op), async_dbus_op_unref); else - return bt_gatt_client_read_value(gatt, handle, callback, + op->id = bt_gatt_client_read_value(gatt, handle, callback, async_dbus_op_ref(op), async_dbus_op_unref); + + if (op->id) + return op; + + async_dbus_op_free(op); + + return NULL; } static DBusMessage *descriptor_read_value(DBusConnection *conn, @@ -429,63 +470,51 @@ static DBusMessage *descriptor_read_value(DBusConnection *conn, struct descriptor *desc = user_data; struct bt_gatt_client *gatt = desc->chrc->service->client->gatt; DBusMessageIter iter; - struct async_dbus_op *op; uint16_t offset = 0; if (!gatt) return btd_error_failed(msg, "Not connected"); - if (desc->read_id) - return btd_error_in_progress(msg); - dbus_message_iter_init(msg, &iter); if (parse_options(&iter, &offset)) return btd_error_invalid_args(msg); - op = new0(struct async_dbus_op, 1); - op->msg = dbus_message_ref(msg); - op->data = desc; - op->offset = offset; - - desc->read_id = read_value(gatt, desc->handle, desc_read_cb, op); - if (desc->read_id) + if (desc->read_op) { + if (desc->read_op->offset != offset) + return btd_error_in_progress(msg); + queue_push_tail(desc->read_op->msgs, dbus_message_ref(msg)); return NULL; + } - async_dbus_op_free(op); + desc->read_op = read_value(gatt, msg, desc->handle, offset, + desc_read_cb, desc); + if (!desc->read_op) + return btd_error_failed(msg, "Failed to send read request"); - return btd_error_failed(msg, "Failed to send read request"); + return NULL; } static void write_result_cb(bool success, bool reliable_error, uint8_t att_ecode, void *user_data) { struct async_dbus_op *op = user_data; - DBusMessage *reply; + int err = 0; if (op->complete && !op->complete(op->data)) { - reply = btd_error_failed(op->msg, "Operation failed"); + err = -EFAULT; goto done; } if (!success) { if (reliable_error) - reply = btd_error_failed(op->msg, - "Reliable write failed"); + err = -EFAULT; else - reply = create_gatt_dbus_error(op->msg, att_ecode); - - goto done; - } - - reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID); - if (!reply) { - error("Failed to allocate D-Bus message reply"); - return; + err = att_ecode; } done: - g_dbus_send_message(btd_get_dbus_connection(), reply); + async_dbus_op_reply(op, err, NULL, 0); } static void write_cb(bool success, uint8_t att_ecode, void *user_data) @@ -493,7 +522,7 @@ static void write_cb(bool success, uint8_t att_ecode, void *user_data) write_result_cb(success, false, att_ecode, user_data); } -static unsigned int start_long_write(DBusMessage *msg, uint16_t handle, +static struct async_dbus_op *start_long_write(DBusMessage *msg, uint16_t handle, struct bt_gatt_client *gatt, bool reliable, const uint8_t *value, size_t value_len, uint16_t offset, @@ -501,53 +530,52 @@ static unsigned int start_long_write(DBusMessage *msg, uint16_t handle, async_dbus_op_complete_t complete) { struct async_dbus_op *op; - unsigned int id; - op = new0(struct async_dbus_op, 1); - op->msg = dbus_message_ref(msg); - op->data = data; + op = async_dbus_op_new(msg, data); op->complete = complete; op->offset = offset; - id = bt_gatt_client_write_long_value(gatt, reliable, handle, offset, + op->id = bt_gatt_client_write_long_value(gatt, reliable, handle, offset, value, value_len, write_result_cb, op, async_dbus_op_free); - if (!id) + if (!op->id) { async_dbus_op_free(op); + return NULL; + } - return id; + return op; } -static unsigned int start_write_request(DBusMessage *msg, uint16_t handle, +static struct async_dbus_op *start_write_request(DBusMessage *msg, + uint16_t handle, struct bt_gatt_client *gatt, const uint8_t *value, size_t value_len, void *data, async_dbus_op_complete_t complete) { struct async_dbus_op *op; - unsigned int id; - op = new0(struct async_dbus_op, 1); - op->msg = dbus_message_ref(msg); - op->data = data; + op = async_dbus_op_new(msg, data); op->complete = complete; - id = bt_gatt_client_write_value(gatt, handle, value, value_len, + op->id = bt_gatt_client_write_value(gatt, handle, value, value_len, write_cb, op, async_dbus_op_free); - if (!id) + if (!op->id) { async_dbus_op_free(op); + return NULL; + } - return id; + return op; } static bool desc_write_complete(void *data) { struct descriptor *desc = data; - desc->write_id = 0; + desc->write_op = NULL; /* * The descriptor might have been unregistered during the read. Return @@ -569,7 +597,7 @@ static DBusMessage *descriptor_write_value(DBusConnection *conn, if (!gatt) return btd_error_failed(msg, "Not connected"); - if (desc->write_id) + if (desc->write_op) return btd_error_in_progress(msg); dbus_message_iter_init(msg, &iter); @@ -593,17 +621,17 @@ static DBusMessage *descriptor_write_value(DBusConnection *conn, * write. */ if (value_len <= bt_gatt_client_get_mtu(gatt) - 3 && !offset) - desc->write_id = start_write_request(msg, desc->handle, + desc->write_op = start_write_request(msg, desc->handle, gatt, value, value_len, desc, desc_write_complete); else - desc->write_id = start_long_write(msg, desc->handle, gatt, + desc->write_op = start_long_write(msg, desc->handle, gatt, false, value, value_len, offset, desc, desc_write_complete); - if (!desc->write_id) + if (!desc->write_op) return btd_error_failed(msg, "Failed to initiate write"); return NULL; @@ -681,11 +709,11 @@ static void unregister_descriptor(void *data) DBG("Removing GATT descriptor: %s", desc->path); - if (desc->read_id) - bt_gatt_client_cancel(gatt, desc->read_id); + if (desc->read_op) + bt_gatt_client_cancel(gatt, desc->read_op->id); - if (desc->write_id) - bt_gatt_client_cancel(gatt, desc->write_id); + if (desc->write_op) + bt_gatt_client_cancel(gatt, desc->write_op->id); desc->chrc = NULL; @@ -832,7 +860,6 @@ static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value, struct async_dbus_op *op = user_data; struct characteristic *chrc = op->data; struct service *service = chrc->service; - DBusMessage *reply; if (!success) goto fail; @@ -843,6 +870,7 @@ static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value, if (!gatt_db_attribute_write(chrc->attr, op->offset, value, length, 0, NULL, write_characteristic_cb, chrc)) { error("Failed to store attribute"); + att_ecode = BT_ATT_ERROR_UNLIKELY; goto fail; } @@ -853,31 +881,30 @@ static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value, */ if (length == bt_gatt_client_get_mtu(service->client->gatt) - 1) { op->offset += length; - chrc->read_id = bt_gatt_client_read_long_value( - service->client->gatt, + op->id = 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); - if (chrc->read_id) + if (op->id) return; } - chrc->read_id = 0; - /* Read the stored data from db */ if (!gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_op_cb, op)) { error("Failed to read database"); + att_ecode = BT_ATT_ERROR_UNLIKELY; goto fail; } + chrc->read_op = NULL; + return; fail: - reply = create_gatt_dbus_error(op->msg, att_ecode); - chrc->read_id = 0; - g_dbus_send_message(btd_get_dbus_connection(), reply); + async_dbus_op_reply(op, att_ecode, NULL, 0); + chrc->read_op = NULL; } static DBusMessage *characteristic_read_value(DBusConnection *conn, @@ -886,39 +913,36 @@ static DBusMessage *characteristic_read_value(DBusConnection *conn, struct characteristic *chrc = user_data; struct bt_gatt_client *gatt = chrc->service->client->gatt; DBusMessageIter iter; - struct async_dbus_op *op; uint16_t offset = 0; if (!gatt) return btd_error_failed(msg, "Not connected"); - if (chrc->read_id) - return btd_error_in_progress(msg); - dbus_message_iter_init(msg, &iter); if (parse_options(&iter, &offset)) return btd_error_invalid_args(msg); - op = new0(struct async_dbus_op, 1); - op->msg = dbus_message_ref(msg); - op->data = chrc; - op->offset = offset; - - chrc->read_id = read_value(gatt, chrc->value_handle, chrc_read_cb, op); - if (chrc->read_id) + if (chrc->read_op) { + if (chrc->read_op->offset != offset) + return btd_error_in_progress(msg); + queue_push_tail(chrc->read_op->msgs, dbus_message_ref(msg)); return NULL; + } - async_dbus_op_free(op); + chrc->read_op = read_value(gatt, msg, chrc->value_handle, offset, + chrc_read_cb, chrc); + if (!chrc->read_op) + return btd_error_failed(msg, "Failed to send read request"); - return btd_error_failed(msg, "Failed to send read request"); + return NULL; } static bool chrc_write_complete(void *data) { struct characteristic *chrc = data; - chrc->write_id = 0; + chrc->write_op = NULL; /* * The characteristic might have been unregistered during the read. @@ -941,7 +965,7 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn, if (!gatt) return btd_error_failed(msg, "Not connected"); - if (chrc->write_id) + if (chrc->write_op) return btd_error_in_progress(msg); dbus_message_iter_init(msg, &iter); @@ -965,10 +989,10 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn, */ if ((chrc->ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE)) { supported = true; - chrc->write_id = start_long_write(msg, chrc->value_handle, gatt, + chrc->write_op = start_long_write(msg, chrc->value_handle, gatt, true, value, value_len, offset, chrc, chrc_write_complete); - if (chrc->write_id) + if (chrc->write_op) return NULL; } @@ -981,17 +1005,17 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn, return btd_error_failed(msg, "No ATT transport"); if (value_len <= mtu - 3 && !offset) - chrc->write_id = start_write_request(msg, + chrc->write_op = start_write_request(msg, chrc->value_handle, gatt, value, value_len, chrc, chrc_write_complete); else - chrc->write_id = start_long_write(msg, + chrc->write_op = start_long_write(msg, chrc->value_handle, gatt, false, value, value_len, offset, chrc, chrc_write_complete); - if (chrc->write_id) + if (chrc->write_op) return NULL; } @@ -1140,26 +1164,17 @@ static void notify_cb(uint16_t value_handle, const uint8_t *value, write_characteristic_cb, chrc); } -static DBusMessage *create_notify_reply(struct async_dbus_op *op, - bool success, uint8_t att_ecode) +static void create_notify_reply(struct async_dbus_op *op, bool success, + uint8_t att_ecode) { - DBusMessage *reply = NULL; - - if (!op->msg) - return NULL; + int err; if (success) - reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID); - else if (att_ecode) - reply = create_gatt_dbus_error(op->msg, att_ecode); + err = 0; else - reply = btd_error_failed(op->msg, - "Characteristic not available"); + err = att_ecode ? att_ecode : -ENOENT; - if (!reply) - error("Failed to construct D-Bus message reply"); - - return reply; + async_dbus_op_reply(op, err, NULL, 0); } static void register_notify_cb(uint16_t att_ecode, void *user_data) @@ -1167,16 +1182,15 @@ static void register_notify_cb(uint16_t att_ecode, void *user_data) struct async_dbus_op *op = user_data; struct notify_client *client = op->data; struct characteristic *chrc = client->chrc; - DBusMessage *reply; if (att_ecode) { queue_remove(chrc->notify_clients, client); queue_remove(chrc->service->client->all_notify_clients, client); notify_client_free(client); - reply = create_notify_reply(op, false, att_ecode); + create_notify_reply(op, false, att_ecode); - goto done; + return; } if (!chrc->notifying) { @@ -1186,11 +1200,7 @@ static void register_notify_cb(uint16_t att_ecode, void *user_data) "Notifying"); } - reply = create_notify_reply(op, true, 0); - -done: - if (reply) - g_dbus_send_message(btd_get_dbus_connection(), reply); + create_notify_reply(op, true, 0); } static DBusMessage *characteristic_start_notify(DBusConnection *conn, @@ -1240,9 +1250,7 @@ static DBusMessage *characteristic_start_notify(DBusConnection *conn, goto fail; } - op = new0(struct async_dbus_op, 1); - op->data = client; - op->msg = dbus_message_ref(msg); + op = async_dbus_op_new(msg, client); client->notify_id = bt_gatt_client_register_notify(gatt, chrc->value_handle, @@ -1395,11 +1403,11 @@ static void unregister_characteristic(void *data) DBG("Removing GATT characteristic: %s", chrc->path); - if (chrc->read_id) - bt_gatt_client_cancel(gatt, chrc->read_id); + if (chrc->read_op) + bt_gatt_client_cancel(gatt, chrc->read_op->id); - if (chrc->write_id) - bt_gatt_client_cancel(gatt, chrc->write_id); + if (chrc->write_op) + bt_gatt_client_cancel(gatt, chrc->write_op->id); queue_remove_all(chrc->notify_clients, NULL, NULL, remove_client); queue_remove_all(chrc->descs, NULL, NULL, unregister_descriptor); -- 2.5.5 -- To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html