Return-Path: From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue Date: Mon, 9 May 2016 16:51:18 +0300 Message-Id: <1462801883-6361-1-git-send-email-luiz.dentz@gmail.com> Sender: linux-bluetooth-owner@vger.kernel.org List-ID: From: Luiz Augusto von Dentz This adds the possibility to pass an offset to these operations, and also in the server case to give the device object. --- v2: Fix Vinicius comments, add necessary changes to other tools affected. doc/gatt-api.txt | 20 ++++- src/gatt-client.c | 180 ++++++++++++++++++++++++++++-------------- src/gatt-database.c | 219 ++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 296 insertions(+), 123 deletions(-) diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt index ad2ab16..683b1b7 100644 --- a/doc/gatt-api.txt +++ b/doc/gatt-api.txt @@ -61,23 +61,29 @@ Service org.bluez Interface org.bluez.GattCharacteristic1 [Experimental] Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY -Methods array{byte} ReadValue() +Methods array{byte} ReadValue(dict options) Issues a request to read the value of the characteristic and returns the value if the operation was successful. + Possible options: "offset": uint16 offset + "device": Object Device (Server only) + Possible Errors: org.bluez.Error.Failed org.bluez.Error.InProgress org.bluez.Error.NotPermitted org.bluez.Error.NotAuthorized org.bluez.Error.NotSupported - void WriteValue(array{byte} value) + void WriteValue(array{byte} value, dict options) Issues a request to write the value of the characteristic. + Possible options: "offset": Start offset + "device": Device path (Server only) + Possible Errors: org.bluez.Error.Failed org.bluez.Error.InProgress org.bluez.Error.NotPermitted @@ -154,23 +160,29 @@ Service org.bluez Interface org.bluez.GattDescriptor1 [Experimental] Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY/descriptorZZZ -Methods array{byte} ReadValue() +Methods array{byte} ReadValue(dict flags) Issues a request to read the value of the characteristic and returns the value if the operation was successful. + Possible options: "offset": Start offset + "device": Device path (Server only) + Possible Errors: org.bluez.Error.Failed org.bluez.Error.InProgress org.bluez.Error.NotPermitted org.bluez.Error.NotAuthorized org.bluez.Error.NotSupported - void WriteValue(array{byte} value) + void WriteValue(array{byte} value, dict flags) Issues a request to write the value of the characteristic. + Possible options: "offset": Start offset + "device": Device path (Server only) + Possible Errors: org.bluez.Error.Failed org.bluez.Error.InProgress org.bluez.Error.NotPermitted diff --git a/src/gatt-client.c b/src/gatt-client.c index 16a1f6c..0cbacca 100644 --- a/src/gatt-client.c +++ b/src/gatt-client.c @@ -23,6 +23,7 @@ #include #include +#include #include @@ -191,33 +192,17 @@ static gboolean descriptor_value_exists(const GDBusPropertyTable *property, return ret; } -static bool parse_value_arg(DBusMessage *msg, uint8_t **value, - size_t *value_len) +static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len) { - DBusMessageIter iter, array; - uint8_t *val; - int len; - - if (!dbus_message_iter_init(msg, &iter)) - return false; - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) - return false; - - dbus_message_iter_recurse(&iter, &array); - dbus_message_iter_get_fixed_array(&array, &val, &len); - dbus_message_iter_next(&iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) - return false; + DBusMessageIter array; - if (len < 0) - return false; + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return -EINVAL; - *value = val; - *value_len = len; + dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_fixed_array(&array, value, len); - return true; + return 0; } typedef bool (*async_dbus_op_complete_t)(void *data); @@ -390,12 +375,60 @@ fail: return; } +static int parse_options(DBusMessageIter *iter, uint16_t *offset) +{ + DBusMessageIter dict; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return -EINVAL; + + dbus_message_iter_recurse(iter, &dict); + + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + const char *key; + DBusMessageIter value, entry; + int var; + + dbus_message_iter_recurse(&dict, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + + var = dbus_message_iter_get_arg_type(&value); + if (strcasecmp(key, "offset") == 0) { + if (var != DBUS_TYPE_UINT16) + return -EINVAL; + dbus_message_iter_get_basic(&value, 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) +{ + if (op->offset) + return bt_gatt_client_read_long_value(gatt, handle, op->offset, + callback, + async_dbus_op_ref(op), + async_dbus_op_unref); + else + return bt_gatt_client_read_value(gatt, handle, callback, + async_dbus_op_ref(op), + async_dbus_op_unref); +} + static DBusMessage *descriptor_read_value(DBusConnection *conn, DBusMessage *msg, void *user_data) { 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"); @@ -403,14 +436,17 @@ static DBusMessage *descriptor_read_value(DBusConnection *conn, 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 = bt_gatt_client_read_value(gatt, desc->handle, - desc_read_cb, - async_dbus_op_ref(op), - async_dbus_op_unref); + desc->read_id = read_value(gatt, desc->handle, desc_read_cb, op); if (desc->read_id) return NULL; @@ -450,7 +486,6 @@ done: g_dbus_send_message(btd_get_dbus_connection(), reply); } - static void write_cb(bool success, uint8_t att_ecode, void *user_data) { write_result_cb(success, false, att_ecode, user_data); @@ -459,7 +494,8 @@ static void write_cb(bool success, uint8_t att_ecode, void *user_data) static unsigned int start_long_write(DBusMessage *msg, uint16_t handle, struct bt_gatt_client *gatt, bool reliable, const uint8_t *value, - size_t value_len, void *data, + size_t value_len, uint16_t offset, + void *data, async_dbus_op_complete_t complete) { struct async_dbus_op *op; @@ -469,9 +505,10 @@ static unsigned int start_long_write(DBusMessage *msg, uint16_t handle, op->msg = dbus_message_ref(msg); op->data = data; op->complete = complete; + op->offset = offset; - id = bt_gatt_client_write_long_value(gatt, reliable, handle, - 0, value, value_len, + id = bt_gatt_client_write_long_value(gatt, reliable, handle, offset, + value, value_len, write_result_cb, op, async_dbus_op_free); @@ -522,8 +559,10 @@ static DBusMessage *descriptor_write_value(DBusConnection *conn, { struct descriptor *desc = user_data; struct bt_gatt_client *gatt = desc->chrc->service->client->gatt; + DBusMessageIter iter; uint8_t *value = NULL; - size_t value_len = 0; + int value_len = 0; + uint16_t offset = 0; if (!gatt) return btd_error_failed(msg, "Not connected"); @@ -531,7 +570,12 @@ static DBusMessage *descriptor_write_value(DBusConnection *conn, if (desc->write_id) return btd_error_in_progress(msg); - if (!parse_value_arg(msg, &value, &value_len)) + dbus_message_iter_init(msg, &iter); + + if (parse_value_arg(&iter, &value, &value_len)) + return btd_error_invalid_args(msg); + + if (parse_options(&iter, &offset)) return btd_error_invalid_args(msg); /* @@ -546,15 +590,15 @@ static DBusMessage *descriptor_write_value(DBusConnection *conn, * Based on the value length and the MTU, either use a write or a long * write. */ - if (value_len <= (unsigned) bt_gatt_client_get_mtu(gatt) - 3) + if (value_len <= bt_gatt_client_get_mtu(gatt) - 3 && !offset) desc->write_id = 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, false, value, - value_len, desc, + desc->write_id = start_long_write(msg, desc->handle, gatt, + false, value, + value_len, offset, desc, desc_write_complete); if (!desc->write_id) @@ -574,13 +618,15 @@ static const GDBusPropertyTable descriptor_properties[] = { }; static const GDBusMethodTable descriptor_methods[] = { - { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", NULL, - GDBUS_ARGS({ "value", "ay" }), - descriptor_read_value) }, + { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", + GDBUS_ARGS({ "options", "a{sv}" }), + GDBUS_ARGS({ "value", "ay" }), + descriptor_read_value) }, { GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue", - GDBUS_ARGS({ "value", "ay" }), - NULL, - descriptor_write_value) }, + GDBUS_ARGS({ "value", "ay" }, + { "options", "a{sv}" }), + NULL, + descriptor_write_value) }, { } }; @@ -837,7 +883,9 @@ 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"); @@ -845,14 +893,17 @@ static DBusMessage *characteristic_read_value(DBusConnection *conn, 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 = bt_gatt_client_read_value(gatt, chrc->value_handle, - chrc_read_cb, - async_dbus_op_ref(op), - async_dbus_op_unref); + chrc->read_id = read_value(gatt, chrc->value_handle, chrc_read_cb, op); if (chrc->read_id) return NULL; @@ -879,9 +930,11 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn, { struct characteristic *chrc = user_data; struct bt_gatt_client *gatt = chrc->service->client->gatt; + DBusMessageIter iter; uint8_t *value = NULL; - size_t value_len = 0; + int value_len = 0; bool supported = false; + uint16_t offset = 0; if (!gatt) return btd_error_failed(msg, "Not connected"); @@ -889,7 +942,12 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn, if (chrc->write_id) return btd_error_in_progress(msg); - if (!parse_value_arg(msg, &value, &value_len)) + dbus_message_iter_init(msg, &iter); + + if (parse_value_arg(&iter, &value, &value_len)) + return btd_error_invalid_args(msg); + + if (parse_options(&iter, &offset)) return btd_error_invalid_args(msg); /* @@ -906,7 +964,7 @@ 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, - true, value, value_len, + true, value, value_len, offset, chrc, chrc_write_complete); if (chrc->write_id) return NULL; @@ -920,7 +978,7 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn, if (!mtu) return btd_error_failed(msg, "No ATT transport"); - if (value_len <= (unsigned) mtu - 3) + if (value_len <= mtu - 3 && !offset) chrc->write_id = start_write_request(msg, chrc->value_handle, gatt, value, value_len, @@ -928,7 +986,7 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn, else chrc->write_id = start_long_write(msg, chrc->value_handle, gatt, - false, value, value_len, + false, value, value_len, offset, chrc, chrc_write_complete); if (chrc->write_id) @@ -1242,17 +1300,19 @@ static const GDBusPropertyTable characteristic_properties[] = { }; static const GDBusMethodTable characteristic_methods[] = { - { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", NULL, - GDBUS_ARGS({ "value", "ay" }), - characteristic_read_value) }, + { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", + GDBUS_ARGS({ "options", "a{sv}" }), + GDBUS_ARGS({ "value", "ay" }), + characteristic_read_value) }, { GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue", - GDBUS_ARGS({ "value", "ay" }), - NULL, - characteristic_write_value) }, + GDBUS_ARGS({ "value", "ay" }, + { "options", "a{sv}" }), + NULL, + characteristic_write_value) }, { GDBUS_EXPERIMENTAL_ASYNC_METHOD("StartNotify", NULL, NULL, - characteristic_start_notify) }, + characteristic_start_notify) }, { GDBUS_EXPERIMENTAL_METHOD("StopNotify", NULL, NULL, - characteristic_stop_notify) }, + characteristic_stop_notify) }, { } }; diff --git a/src/gatt-database.c b/src/gatt-database.c index b8da955..99d084d 100644 --- a/src/gatt-database.c +++ b/src/gatt-database.c @@ -135,6 +135,7 @@ struct external_desc { }; struct pending_op { + struct btd_device *device; unsigned int id; struct gatt_db_attribute *attrib; struct queue *owner_queue; @@ -1592,7 +1593,8 @@ static void pending_op_free(void *data) free(op); } -static struct pending_op *pending_read_new(struct queue *owner_queue, +static struct pending_op *pending_read_new(struct btd_device *device, + struct queue *owner_queue, struct gatt_db_attribute *attrib, unsigned int id) { @@ -1601,6 +1603,7 @@ static struct pending_op *pending_read_new(struct queue *owner_queue, op = new0(struct pending_op, 1); op->owner_queue = owner_queue; + op->device = device; op->attrib = attrib; op->id = id; queue_push_tail(owner_queue, op); @@ -1608,33 +1611,75 @@ static struct pending_op *pending_read_new(struct queue *owner_queue, return op; } -static void send_read(struct gatt_db_attribute *attrib, GDBusProxy *proxy, - struct queue *owner_queue, - unsigned int id) +static void append_options(DBusMessageIter *iter, void *user_data) +{ + struct pending_op *op = user_data; + const char *path = device_get_path(op->device); + + dict_append_entry(iter, "device", DBUS_TYPE_OBJECT_PATH, &path); +} + +static void read_setup_cb(DBusMessageIter *iter, void *user_data) +{ + struct pending_op *op = user_data; + DBusMessageIter dict; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &dict); + + append_options(&dict, op); + + dbus_message_iter_close_container(iter, &dict); +} + +static struct pending_op *send_read(struct btd_device *device, + struct gatt_db_attribute *attrib, + GDBusProxy *proxy, + struct queue *owner_queue, + unsigned int id) { struct pending_op *op; - uint8_t ecode = BT_ATT_ERROR_UNLIKELY; - op = pending_read_new(owner_queue, attrib, id); + op = pending_read_new(device, owner_queue, attrib, id); - if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply_cb, - op, pending_op_free) == TRUE) - return; + if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup_cb, + read_reply_cb, op, pending_op_free) == TRUE) + return op; pending_op_free(op); - gatt_db_attribute_read_result(attrib, id, ecode, NULL, 0); + return NULL; } static void write_setup_cb(DBusMessageIter *iter, void *user_data) { struct pending_op *op = user_data; - DBusMessageIter array; + DBusMessageIter array, dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &op->data.iov_base, op->data.iov_len); dbus_message_iter_close_container(iter, &array); + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &dict); + + append_options(&dict, op); + + dbus_message_iter_close_container(iter, &dict); + + if (!op->owner_queue) { + gatt_db_attribute_write_result(op->attrib, op->id, 0); + pending_op_free(op); + } } static void write_reply_cb(DBusMessage *message, void *user_data) @@ -1673,7 +1718,8 @@ done: gatt_db_attribute_write_result(op->attrib, op->id, ecode); } -static struct pending_op *pending_write_new(struct queue *owner_queue, +static struct pending_op *pending_write_new(struct btd_device *device, + struct queue *owner_queue, struct gatt_db_attribute *attrib, unsigned int id, const uint8_t *value, @@ -1686,6 +1732,7 @@ static struct pending_op *pending_write_new(struct queue *owner_queue, op->data.iov_base = (uint8_t *) value; op->data.iov_len = len; + op->device = device; op->owner_queue = owner_queue; op->attrib = attrib; op->id = id; @@ -1694,24 +1741,25 @@ static struct pending_op *pending_write_new(struct queue *owner_queue, return op; } -static void send_write(struct gatt_db_attribute *attrib, GDBusProxy *proxy, +static struct pending_op *send_write(struct btd_device *device, + struct gatt_db_attribute *attrib, + GDBusProxy *proxy, struct queue *owner_queue, unsigned int id, const uint8_t *value, size_t len) { struct pending_op *op; - uint8_t ecode = BT_ATT_ERROR_UNLIKELY; - op = pending_write_new(owner_queue, attrib, id, value, len); + op = pending_write_new(device, owner_queue, attrib, id, value, len); if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup_cb, - write_reply_cb, op, - pending_op_free) == TRUE) - return; + owner_queue ? write_reply_cb : NULL, + op, pending_op_free) == TRUE) + return op; pending_op_free(op); - gatt_db_attribute_write_result(attrib, id, ecode); + return NULL; } static uint32_t permissions_from_props(uint8_t props, uint8_t ext_props) @@ -1895,19 +1943,65 @@ static bool database_add_cep(struct external_service *service, return true; } +static struct btd_device *att_get_device(struct bt_att *att) +{ + GIOChannel *io = NULL; + GError *gerr = NULL; + bdaddr_t src, dst; + uint8_t dst_type; + struct btd_adapter *adapter; + + io = g_io_channel_unix_new(bt_att_get_fd(att)); + if (!io) + return NULL; + + bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_DEST_TYPE, &dst_type, + BT_IO_OPT_INVALID); + if (gerr) { + error("bt_io_get: %s", gerr->message); + g_error_free(gerr); + g_io_channel_unref(io); + return NULL; + } + + g_io_channel_unref(io); + + adapter = adapter_find(&src); + if (!adapter) { + error("Unable to find adapter object"); + return NULL; + } + + return btd_adapter_find_device(adapter, &dst, dst_type); +} + static void desc_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct external_desc *desc = user_data; + struct btd_device *device; if (desc->attrib != attrib) { error("Read callback called with incorrect attribute"); - return; + goto fail; } - send_read(attrib, desc->proxy, desc->pending_reads, id); + device = att_get_device(att); + if (!device) { + error("Unable to find device object"); + goto fail; + } + + if (send_read(device, attrib, desc->proxy, desc->pending_reads, id)) + return; + +fail: + gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY, + NULL, 0); } static void desc_write_cb(struct gatt_db_attribute *attrib, @@ -1917,13 +2011,26 @@ static void desc_write_cb(struct gatt_db_attribute *attrib, void *user_data) { struct external_desc *desc = user_data; + struct btd_device *device; if (desc->attrib != attrib) { error("Read callback called with incorrect attribute"); - return; + goto fail; } - send_write(attrib, desc->proxy, desc->pending_writes, id, value, len); + device = att_get_device(att); + if (!device) { + error("Unable to find device object"); + goto fail; + } + + if (send_write(device, attrib, desc->proxy, desc->pending_writes, id, + value, len)) + return; + +fail: + gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY, + NULL, 0); } static bool database_add_desc(struct external_service *service, @@ -1956,43 +2063,25 @@ static void chrc_read_cb(struct gatt_db_attribute *attrib, void *user_data) { struct external_chrc *chrc = user_data; + struct btd_device *device; if (chrc->attrib != attrib) { error("Read callback called with incorrect attribute"); - return; + goto fail; } - send_read(attrib, chrc->proxy, chrc->pending_reads, id); -} - -static void write_without_response_setup_cb(DBusMessageIter *iter, - void *user_data) -{ - struct iovec *iov = user_data; - DBusMessageIter array; - - dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array); - dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, - &iov->iov_base, iov->iov_len); - dbus_message_iter_close_container(iter, &array); -} - -static void send_write_without_response(struct gatt_db_attribute *attrib, - GDBusProxy *proxy, unsigned int id, - const uint8_t *value, size_t len) -{ - struct iovec iov; - uint8_t ecode = 0; - - iov.iov_base = (uint8_t *) value; - iov.iov_len = len; + device = att_get_device(att); + if (!device) { + error("Unable to find device object"); + goto fail; + } - if (!g_dbus_proxy_method_call(proxy, "WriteValue", - write_without_response_setup_cb, - NULL, &iov, NULL)) - ecode = BT_ATT_ERROR_UNLIKELY; + if (send_read(device, attrib, chrc->proxy, chrc->pending_reads, id)) + return; - gatt_db_attribute_write_result(attrib, id, ecode); +fail: + gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY, + NULL, 0); } static void chrc_write_cb(struct gatt_db_attribute *attrib, @@ -2002,19 +2091,31 @@ static void chrc_write_cb(struct gatt_db_attribute *attrib, void *user_data) { struct external_chrc *chrc = user_data; + struct btd_device *device; + struct queue *queue; if (chrc->attrib != attrib) { error("Write callback called with incorrect attribute"); - return; + goto fail; } - if (chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP) { - send_write_without_response(attrib, chrc->proxy, id, value, - len); - return; + device = att_get_device(att); + if (!device) { + error("Unable to find device object"); + goto fail; } - send_write(attrib, chrc->proxy, chrc->pending_writes, id, value, len); + if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP)) + queue = chrc->pending_writes; + else + queue = NULL; + + if (send_write(device, attrib, chrc->proxy, queue, id, value, len)) + return; + +fail: + gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY, + NULL, 0); } static bool database_add_chrc(struct external_service *service, -- 2.5.5