All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue
@ 2016-05-09 13:51 Luiz Augusto von Dentz
  2016-05-09 13:51 ` [PATCHv2 BlueZ 2/6] doc/gatt-api: Merge RegisterProfile with RegisterApplication Luiz Augusto von Dentz
                   ` (6 more replies)
  0 siblings, 7 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2016-05-09 13:51 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

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 <stdbool.h>
 #include <stdint.h>
+#include <errno.h>
 
 #include <dbus/dbus.h>
 
@@ -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


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCHv2 BlueZ 2/6] doc/gatt-api: Merge RegisterProfile with RegisterApplication
  2016-05-09 13:51 [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue Luiz Augusto von Dentz
@ 2016-05-09 13:51 ` Luiz Augusto von Dentz
  2016-05-09 13:51 ` [PATCHv2 BlueZ 3/6] doc/gatt-api: Add secure flags Luiz Augusto von Dentz
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2016-05-09 13:51 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Since RegisterApplication makes use of ObjectManager it is also possible
to verify the existance of GattProfile objects unifying the API for both
services (GATT server) and profiles (GATT client).
---
 doc/gatt-api.txt    |  37 ++---
 src/gatt-database.c | 413 +++++++++++++++++++++-------------------------------
 2 files changed, 171 insertions(+), 279 deletions(-)

diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
index 683b1b7..9404986 100644
--- a/doc/gatt-api.txt
+++ b/doc/gatt-api.txt
@@ -218,8 +218,8 @@ Properties	string UUID [read-only]
 				"encrypt-authenticated-read"
 				"encrypt-authenticated-write"
 
-Profile hierarcy
-================
+GATT Profile hierarcy
+=====================
 
 Local profile (GATT client) instance. By registering this type of object
 an application effectively indicates support for a specific GATT profile
@@ -238,6 +238,10 @@ Methods		void Release()
 			profile, because when this method gets called it has
 			already been unregistered.
 
+Properties	array{string} UUIDs [read-only]
+
+			128-bit GATT service UUIDs.
+
 
 GATT Manager hierarchy
 ======================
@@ -306,11 +310,12 @@ Object path	[variable prefix]/{hci0,hci1,...}
 Methods		void RegisterApplication(object application, dict options)
 
 			Registers a local GATT services hierarchy as described
-			above.
+			above (GATT Server) and/or GATT profiles (GATT Client).
 
 			The application object path together with the D-Bus
 			system bus connection ID define the identification of
-			the application registering a GATT based service.
+			the application registering a GATT based
+			service or profile.
 
 			Possible errors: org.bluez.Error.InvalidArguments
 					 org.bluez.Error.AlreadyExists
@@ -324,27 +329,3 @@ Methods		void RegisterApplication(object application, dict options)
 
 			Possible errors: org.bluez.Error.InvalidArguments
 					 org.bluez.Error.DoesNotExist
-
-		void RegisterProfile(object profile, array{string} UUIDs,
-				     dict options)
-
-			Registers a GATT (client role) profile exported
-			under interface GattProfile1. The array of UUIDs
-			specifies the mandatory set of remote service
-			UUIDs that should all be available for the
-			remote device to match this profile. Matching
-			devices will be added to the auto-connection
-			list and connected whenever available.
-
-			Possible errors: org.bluez.Error.InvalidArguments
-					 org.bluez.Error.AlreadyExists
-
-		void UnregisterProfile(object profile)
-
-			This unregisters the profile that has been
-			previously registered. The object path parameter
-			must match the same value that has been used
-			on registration.
-
-			Possible errors: org.bluez.Error.InvalidArguments
-					 org.bluez.Error.DoesNotExist
diff --git a/src/gatt-database.c b/src/gatt-database.c
index 99d084d..09bec0b 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -54,6 +54,7 @@
 #endif
 
 #define GATT_MANAGER_IFACE	"org.bluez.GattManager1"
+#define GATT_PROFILE_IFACE	"org.bluez.GattProfile1"
 #define GATT_SERVICE_IFACE	"org.bluez.GattService1"
 #define GATT_CHRC_IFACE		"org.bluez.GattCharacteristic1"
 #define GATT_DESC_IFACE		"org.bluez.GattDescriptor1"
@@ -88,6 +89,7 @@ struct gatt_app {
 	DBusMessage *reg;
 	GDBusClient *client;
 	bool failed;
+	struct queue *profiles;
 	struct queue *services;
 	struct queue *proxies;
 };
@@ -103,10 +105,8 @@ struct external_service {
 };
 
 struct external_profile {
-	struct btd_gatt_database *database;
-	char *owner;
-	char *path;	/* Path to GattProfile1 */
-	unsigned int id;
+	struct gatt_app *app;
+	GDBusProxy *proxy;
 	struct queue *profiles; /* btd_profile list */
 };
 
@@ -362,30 +362,6 @@ static void service_free(void *data)
 	free(service);
 }
 
-static void app_free(void *data)
-{
-	struct gatt_app *app = data;
-
-	queue_destroy(app->services, service_free);
-	queue_destroy(app->proxies, NULL);
-
-	if (app->client) {
-		g_dbus_client_set_disconnect_watch(app->client, NULL, NULL);
-		g_dbus_client_set_proxy_handlers(app->client, NULL, NULL,
-								NULL, NULL);
-		g_dbus_client_set_ready_watch(app->client, NULL, NULL);
-		g_dbus_client_unref(app->client);
-	}
-
-	if (app->reg)
-		dbus_message_unref(app->reg);
-
-	g_free(app->owner);
-	g_free(app->path);
-
-	free(app);
-}
-
 static void profile_remove(void *data)
 {
 	struct btd_profile *p = data;
@@ -403,20 +379,10 @@ static void profile_remove(void *data)
 
 static void profile_release(struct external_profile *profile)
 {
-	DBusMessage *msg;
-
-	if (!profile->id)
-		return;
-
-	DBG("Releasing \"%s\"", profile->owner);
-
-	g_dbus_remove_watch(btd_get_dbus_connection(), profile->id);
+	DBG("Releasing \"%s\"", profile->app->owner);
 
-	msg = dbus_message_new_method_call(profile->owner, profile->path,
-						"org.bluez.GattProfile1",
-						"Release");
-	if (msg)
-		g_dbus_send_message(btd_get_dbus_connection(), msg);
+	g_dbus_proxy_method_call(profile->proxy, "Release", NULL, NULL, NULL,
+									NULL);
 }
 
 static void profile_free(void *data)
@@ -427,12 +393,36 @@ static void profile_free(void *data)
 
 	profile_release(profile);
 
-	g_free(profile->owner);
-	g_free(profile->path);
+	g_dbus_proxy_unref(profile->proxy);
 
 	free(profile);
 }
 
+static void app_free(void *data)
+{
+	struct gatt_app *app = data;
+
+	queue_destroy(app->profiles, profile_free);
+	queue_destroy(app->services, service_free);
+	queue_destroy(app->proxies, NULL);
+
+	if (app->client) {
+		g_dbus_client_set_disconnect_watch(app->client, NULL, NULL);
+		g_dbus_client_set_proxy_handlers(app->client, NULL, NULL,
+								NULL, NULL);
+		g_dbus_client_set_ready_watch(app->client, NULL, NULL);
+		g_dbus_client_unref(app->client);
+	}
+
+	if (app->reg)
+		dbus_message_unref(app->reg);
+
+	g_free(app->owner);
+	g_free(app->path);
+
+	free(app);
+}
+
 static void gatt_database_free(void *data)
 {
 	struct btd_gatt_database *database = data;
@@ -2235,9 +2225,6 @@ static bool database_add_app(struct gatt_app *app)
 {
 	const struct queue_entry *entry;
 
-	if (queue_isempty(app->services))
-		return false;
-
 	entry = queue_get_entries(app->services);
 	while (entry) {
 		if (!database_add_service(entry->data)) {
@@ -2251,6 +2238,131 @@ static bool database_add_app(struct gatt_app *app)
 	return true;
 }
 
+static int profile_device_probe(struct btd_service *service)
+{
+	struct btd_profile *p = btd_service_get_profile(service);
+
+	DBG("%s probed", p->name);
+
+	return 0;
+}
+
+static void profile_device_remove(struct btd_service *service)
+{
+	struct btd_profile *p = btd_service_get_profile(service);
+
+	DBG("%s removed", p->name);
+}
+
+static int profile_add(struct external_profile *profile, const char *uuid)
+{
+	struct btd_profile *p;
+
+	p = new0(struct btd_profile, 1);
+
+	/* Assign directly to avoid having extra fields */
+	p->name = (const void *) g_strdup_printf("%s%s/%s", profile->app->owner,
+				g_dbus_proxy_get_path(profile->proxy), uuid);
+	if (!p->name) {
+		free(p);
+		return -ENOMEM;
+	}
+
+	p->remote_uuid = (const void *) g_strdup(uuid);
+	if (!p->remote_uuid) {
+		g_free((void *) p->name);
+		free(p);
+		return -ENOMEM;
+	}
+
+	p->device_probe = profile_device_probe;
+	p->device_remove = profile_device_remove;
+	p->auto_connect = true;
+	p->external = true;
+
+	queue_push_tail(profile->profiles, p);
+
+	DBG("Added \"%s\"", p->name);
+
+	return 0;
+}
+
+static void add_profile(void *data, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	btd_profile_register(data);
+	adapter_add_profile(adapter, data);
+}
+
+static struct external_profile *create_profile(struct gatt_app *app,
+						GDBusProxy *proxy,
+						const char *path)
+{
+	struct external_profile *profile;
+	DBusMessageIter iter, array;
+
+	if (!path || !g_str_has_prefix(path, "/"))
+		return NULL;
+
+	profile = new0(struct external_profile, 1);
+
+	profile->app = app;
+	profile->proxy = g_dbus_proxy_ref(proxy);
+	profile->profiles = queue_new();
+
+	if (!g_dbus_proxy_get_property(proxy, "UUIDs", &iter)) {
+		DBG("UUIDs property not found");
+		goto fail;
+	}
+
+	dbus_message_iter_recurse(&iter, &array);
+
+	while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
+		const char *uuid;
+
+		dbus_message_iter_get_basic(&array, &uuid);
+
+		if (profile_add(profile, uuid) < 0)
+			goto fail;
+
+		dbus_message_iter_next(&array);
+	}
+
+	if (queue_isempty(profile->profiles))
+		goto fail;
+
+	queue_foreach(profile->profiles, add_profile, app->database->adapter);
+	queue_push_tail(app->profiles, profile);
+
+	return profile;
+
+fail:
+	profile_free(profile);
+	return NULL;
+}
+
+static void register_profile(void *data, void *user_data)
+{
+	struct gatt_app *app = user_data;
+	GDBusProxy *proxy = data;
+	const char *iface = g_dbus_proxy_get_interface(proxy);
+	const char *path = g_dbus_proxy_get_path(proxy);
+
+	if (app->failed)
+		return;
+
+	if (g_strcmp0(iface, GATT_PROFILE_IFACE) == 0) {
+		struct external_profile *profile;
+
+		profile = create_profile(app, proxy, path);
+		if (!profile) {
+			app->failed = true;
+			return;
+		}
+	}
+}
+
 static void register_service(void *data, void *user_data)
 {
 	struct gatt_app *app = user_data;
@@ -2333,11 +2445,13 @@ static void client_ready_cb(GDBusClient *client, void *user_data)
 		goto reply;
 	}
 
+	queue_foreach(app->proxies, register_profile, app);
 	queue_foreach(app->proxies, register_service, app);
 	queue_foreach(app->proxies, register_characteristic, app);
 	queue_foreach(app->proxies, register_descriptor, app);
 
-	if (!app->services || app->failed) {
+	if ((queue_isempty(app->services) && queue_isempty(app->profiles)) ||
+							app->failed) {
 		error("No valid external GATT objects found");
 		fail = true;
 		reply = btd_error_failed(app->reg,
@@ -2390,6 +2504,7 @@ static struct gatt_app *create_app(DBusConnection *conn, DBusMessage *msg,
 		goto fail;
 
 	app->services = queue_new();
+	app->profiles = queue_new();
 	app->proxies = queue_new();
 	app->reg = dbus_message_ref(msg);
 
@@ -2476,203 +2591,6 @@ static DBusMessage *manager_unregister_app(DBusConnection *conn,
 	return dbus_message_new_method_return(msg);
 }
 
-static void profile_exited(DBusConnection *conn, void *user_data)
-{
-	struct external_profile *profile = user_data;
-
-	DBG("\"%s\" exited", profile->owner);
-
-	profile->id = 0;
-
-	queue_remove(profile->database->profiles, profile);
-
-	profile_free(profile);
-}
-
-static int profile_device_probe(struct btd_service *service)
-{
-	struct btd_profile *p = btd_service_get_profile(service);
-
-	DBG("%s probed", p->name);
-
-	return 0;
-}
-
-static void profile_device_remove(struct btd_service *service)
-{
-	struct btd_profile *p = btd_service_get_profile(service);
-
-	DBG("%s removed", p->name);
-}
-
-static int profile_add(struct external_profile *profile, const char *uuid)
-{
-	struct btd_profile *p;
-
-	p = new0(struct btd_profile, 1);
-
-	/* Assign directly to avoid having extra fields */
-	p->name = (const void *) g_strdup_printf("%s%s/%s", profile->owner,
-							profile->path, uuid);
-	if (!p->name) {
-		free(p);
-		return -ENOMEM;
-	}
-
-	p->remote_uuid = (const void *) g_strdup(uuid);
-	if (!p->remote_uuid) {
-		g_free((void *) p->name);
-		free(p);
-		return -ENOMEM;
-	}
-
-	p->device_probe = profile_device_probe;
-	p->device_remove = profile_device_remove;
-	p->auto_connect = true;
-	p->external = true;
-
-	queue_push_tail(profile->profiles, p);
-
-	DBG("Added \"%s\"", p->name);
-
-	return 0;
-}
-
-static void add_profile(void *data, void *user_data)
-{
-	struct btd_adapter *adapter = user_data;
-
-	btd_profile_register(data);
-	adapter_add_profile(adapter, data);
-}
-
-static int profile_create(DBusConnection *conn,
-				struct btd_gatt_database *database,
-				const char *sender, const char *path,
-				DBusMessageIter *iter)
-{
-	struct external_profile *profile;
-	DBusMessageIter uuids;
-
-	if (!path || !g_str_has_prefix(path, "/"))
-		return -EINVAL;
-
-	profile = new0(struct external_profile, 1);
-
-	profile->owner = g_strdup(sender);
-	if (!profile->owner)
-		goto fail;
-
-	profile->path = g_strdup(path);
-	if (!profile->path)
-		goto fail;
-
-	profile->profiles = queue_new();
-	profile->database = database;
-	profile->id = g_dbus_add_disconnect_watch(conn, sender, profile_exited,
-								profile, NULL);
-
-	dbus_message_iter_recurse(iter, &uuids);
-
-	while (dbus_message_iter_get_arg_type(&uuids) == DBUS_TYPE_STRING) {
-		const char *uuid;
-
-		dbus_message_iter_get_basic(&uuids, &uuid);
-
-		if (profile_add(profile, uuid) < 0)
-			goto fail;
-
-		dbus_message_iter_next(&uuids);
-	}
-
-	if (queue_isempty(profile->profiles))
-		goto fail;
-
-	queue_foreach(profile->profiles, add_profile, database->adapter);
-	queue_push_tail(database->profiles, profile);
-
-	return 0;
-
-fail:
-	profile_free(profile);
-	return -EINVAL;
-}
-
-static bool match_profile(const void *a, const void *b)
-{
-	const struct external_profile *profile = a;
-	const struct svc_match_data *data = b;
-
-	return g_strcmp0(profile->path, data->path) == 0 &&
-				g_strcmp0(profile->owner, data->sender) == 0;
-}
-
-static DBusMessage *manager_register_profile(DBusConnection *conn,
-					DBusMessage *msg, void *user_data)
-{
-	struct btd_gatt_database *database = user_data;
-	const char *sender = dbus_message_get_sender(msg);
-	DBusMessageIter args;
-	const char *path;
-	struct svc_match_data match_data;
-
-	DBG("sender %s", sender);
-
-	if (!dbus_message_iter_init(msg, &args))
-		return btd_error_invalid_args(msg);
-
-	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
-		return btd_error_invalid_args(msg);
-
-	dbus_message_iter_get_basic(&args, &path);
-
-	match_data.path = path;
-	match_data.sender = sender;
-
-	if (queue_find(database->profiles, match_profile, &match_data))
-		return btd_error_already_exists(msg);
-
-	dbus_message_iter_next(&args);
-	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
-		return btd_error_invalid_args(msg);
-
-	if (profile_create(conn, database, sender, path, &args) < 0)
-		return btd_error_failed(msg, "Failed to register profile");
-
-	return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *manager_unregister_profile(DBusConnection *conn,
-					DBusMessage *msg, void *user_data)
-{
-	struct btd_gatt_database *database = user_data;
-	const char *sender = dbus_message_get_sender(msg);
-	const char *path;
-	DBusMessageIter args;
-	struct external_profile *profile;
-	struct svc_match_data match_data;
-
-	if (!dbus_message_iter_init(msg, &args))
-		return btd_error_invalid_args(msg);
-
-	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
-		return btd_error_invalid_args(msg);
-
-	dbus_message_iter_get_basic(&args, &path);
-
-	match_data.path = path;
-	match_data.sender = sender;
-
-	profile = queue_remove_if(database->profiles, match_profile,
-								&match_data);
-	if (!profile)
-		return btd_error_does_not_exist(msg);
-
-	profile_free(profile);
-
-	return dbus_message_new_method_return(msg);
-}
-
 static const GDBusMethodTable manager_methods[] = {
 	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterApplication",
 			GDBUS_ARGS({ "application", "o" },
@@ -2681,13 +2599,6 @@ static const GDBusMethodTable manager_methods[] = {
 	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterApplication",
 					GDBUS_ARGS({ "application", "o" }),
 					NULL, manager_unregister_app) },
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterProfile",
-			GDBUS_ARGS({ "profile", "o" }, { "UUIDs", "as" },
-			{ "options", "a{sv}" }), NULL,
-			manager_register_profile) },
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterProfile",
-					GDBUS_ARGS({ "profile", "o" }),
-					NULL, manager_unregister_profile) },
 	{ }
 };
 
-- 
2.5.5


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCHv2 BlueZ 3/6] doc/gatt-api: Add secure flags
  2016-05-09 13:51 [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue Luiz Augusto von Dentz
  2016-05-09 13:51 ` [PATCHv2 BlueZ 2/6] doc/gatt-api: Merge RegisterProfile with RegisterApplication Luiz Augusto von Dentz
@ 2016-05-09 13:51 ` Luiz Augusto von Dentz
  2016-05-09 13:51 ` [PATCHv2 BlueZ 4/6] test: Update GATT examples with the new API Luiz Augusto von Dentz
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2016-05-09 13:51 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This add secure-{read,write} which shall be used by servers that want
to restrict attribute access to secure connection only (BT_SECURITY_FIPS)
---
 doc/gatt-api.txt         |  4 +++
 lib/bluetooth.h          |  1 +
 src/gatt-database.c      | 90 ++++++++++++++++++++----------------------------
 src/shared/att-types.h   |  5 +++
 src/shared/gatt-server.c |  3 ++
 5 files changed, 51 insertions(+), 52 deletions(-)

diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
index 9404986..cb4e196 100644
--- a/doc/gatt-api.txt
+++ b/doc/gatt-api.txt
@@ -150,6 +150,8 @@ Properties	string UUID [read-only]
 				"encrypt-write"
 				"encrypt-authenticated-read"
 				"encrypt-authenticated-write"
+				"secure-read" (Server only)
+				"secure-write" (Server only)
 
 Characteristic Descriptors hierarchy
 ====================================
@@ -217,6 +219,8 @@ Properties	string UUID [read-only]
 				"encrypt-write"
 				"encrypt-authenticated-read"
 				"encrypt-authenticated-write"
+				"secure-read" (Server Only)
+				"secure-write" (Server Only)
 
 GATT Profile hierarcy
 =====================
diff --git a/lib/bluetooth.h b/lib/bluetooth.h
index 852a6b2..eb27926 100644
--- a/lib/bluetooth.h
+++ b/lib/bluetooth.h
@@ -69,6 +69,7 @@ struct bt_security {
 #define BT_SECURITY_LOW		1
 #define BT_SECURITY_MEDIUM	2
 #define BT_SECURITY_HIGH	3
+#define BT_SECURITY_FIPS	4
 
 #define BT_DEFER_SETUP	7
 
diff --git a/src/gatt-database.c b/src/gatt-database.c
index 09bec0b..e287b98 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -116,6 +116,7 @@ struct external_chrc {
 	GDBusProxy *proxy;
 	uint8_t props;
 	uint8_t ext_props;
+	uint32_t perm;
 	struct gatt_db_attribute *attrib;
 	struct gatt_db_attribute *ccc;
 	struct queue *pending_reads;
@@ -1113,7 +1114,7 @@ static bool incr_attr_count(struct external_service *service, uint16_t incr)
 }
 
 static bool parse_chrc_flags(DBusMessageIter *array, uint8_t *props,
-							uint8_t *ext_props)
+					uint8_t *ext_props, uint32_t *perm)
 {
 	const char *flag;
 
@@ -1127,34 +1128,51 @@ static bool parse_chrc_flags(DBusMessageIter *array, uint8_t *props,
 
 		if (!strcmp("broadcast", flag))
 			*props |= BT_GATT_CHRC_PROP_BROADCAST;
-		else if (!strcmp("read", flag))
+		else if (!strcmp("read", flag)) {
 			*props |= BT_GATT_CHRC_PROP_READ;
-		else if (!strcmp("write-without-response", flag))
+			*perm |= BT_ATT_PERM_READ;
+		} else if (!strcmp("write-without-response", flag)) {
 			*props |= BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP;
-		else if (!strcmp("write", flag))
+			*perm |= BT_ATT_PERM_WRITE;
+		} else if (!strcmp("write", flag)) {
 			*props |= BT_GATT_CHRC_PROP_WRITE;
-		else if (!strcmp("notify", flag))
+			*perm |= BT_ATT_PERM_WRITE;
+		} else if (!strcmp("notify", flag)) {
 			*props |= BT_GATT_CHRC_PROP_NOTIFY;
-		else if (!strcmp("indicate", flag))
+		} else if (!strcmp("indicate", flag)) {
 			*props |= BT_GATT_CHRC_PROP_INDICATE;
-		else if (!strcmp("authenticated-signed-writes", flag))
+		} else if (!strcmp("authenticated-signed-writes", flag)) {
 			*props |= BT_GATT_CHRC_PROP_AUTH;
-		else if (!strcmp("reliable-write", flag))
+			*perm |= BT_ATT_PERM_WRITE;
+		} else if (!strcmp("reliable-write", flag)) {
 			*ext_props |= BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE;
-		else if (!strcmp("writable-auxiliaries", flag))
+			*perm |= BT_ATT_PERM_WRITE;
+		} else if (!strcmp("writable-auxiliaries", flag)) {
 			*ext_props |= BT_GATT_CHRC_EXT_PROP_WRITABLE_AUX;
-		else if (!strcmp("encrypt-read", flag)) {
+		} else if (!strcmp("encrypt-read", flag)) {
 			*props |= BT_GATT_CHRC_PROP_READ;
 			*ext_props |= BT_GATT_CHRC_EXT_PROP_ENC_READ;
+			*perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT;
 		} else if (!strcmp("encrypt-write", flag)) {
 			*props |= BT_GATT_CHRC_PROP_WRITE;
 			*ext_props |= BT_GATT_CHRC_EXT_PROP_ENC_WRITE;
+			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_ENCRYPT;
 		} else if (!strcmp("encrypt-authenticated-read", flag)) {
 			*props |= BT_GATT_CHRC_PROP_READ;
 			*ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_READ;
+			*perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN;
 		} else if (!strcmp("encrypt-authenticated-write", flag)) {
 			*props |= BT_GATT_CHRC_PROP_WRITE;
 			*ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_WRITE;
+			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN;
+		} else if (!strcmp("secure-read", flag)) {
+			*props |= BT_GATT_CHRC_PROP_READ;
+			*ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_READ;
+			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_READ_SECURE;
+		} else if (!strcmp("secure-write", flag)) {
+			*props |= BT_GATT_CHRC_PROP_WRITE;
+			*ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_WRITE;
+			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_SECURE;
 		} else {
 			error("Invalid characteristic flag: %s", flag);
 			return false;
@@ -1191,6 +1209,10 @@ static bool parse_desc_flags(DBusMessageIter *array, uint32_t *perm)
 			*perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN;
 		else if (!strcmp("encrypt-authenticated-write", flag))
 			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN;
+		else if (!strcmp("secure-read", flag))
+			*perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN;
+		else if (!strcmp("secure-write", flag))
+			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN;
 		else {
 			error("Invalid descriptor flag: %s", flag);
 			return false;
@@ -1204,6 +1226,7 @@ static bool parse_flags(GDBusProxy *proxy, uint8_t *props, uint8_t *ext_props,
 								uint32_t *perm)
 {
 	DBusMessageIter iter, array;
+	const char *iface;
 
 	if (!g_dbus_proxy_get_property(proxy, "Flags", &iter))
 		return false;
@@ -1213,10 +1236,11 @@ static bool parse_flags(GDBusProxy *proxy, uint8_t *props, uint8_t *ext_props,
 
 	dbus_message_iter_recurse(&iter, &array);
 
-	if (perm)
+	iface = g_dbus_proxy_get_interface(proxy);
+	if (!strcmp(iface, GATT_DESC_IFACE))
 		return parse_desc_flags(&array, perm);
 
-	return parse_chrc_flags(&array, props, ext_props);
+	return parse_chrc_flags(&array, props, ext_props, perm);
 }
 
 static struct external_chrc *chrc_create(struct gatt_app *app,
@@ -1264,7 +1288,7 @@ static struct external_chrc *chrc_create(struct gatt_app *app,
 	 * are used to determine if any special descriptors should be
 	 * created.
 	 */
-	if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, NULL)) {
+	if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, &chrc->perm)) {
 		error("Failed to parse characteristic properties");
 		goto fail;
 	}
@@ -1752,37 +1776,6 @@ static struct pending_op *send_write(struct btd_device *device,
 	return NULL;
 }
 
-static uint32_t permissions_from_props(uint8_t props, uint8_t ext_props)
-{
-	uint32_t perm = 0;
-
-	if (props & BT_GATT_CHRC_PROP_WRITE ||
-			props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP ||
-			ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE ||
-			ext_props & BT_GATT_CHRC_EXT_PROP_ENC_WRITE ||
-			ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_WRITE)
-		perm |= BT_ATT_PERM_WRITE;
-
-	if (props & BT_GATT_CHRC_PROP_READ ||
-			ext_props & BT_GATT_CHRC_EXT_PROP_ENC_READ ||
-			ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_READ)
-		perm |= BT_ATT_PERM_READ;
-
-	if (ext_props & BT_GATT_CHRC_EXT_PROP_ENC_READ)
-		perm |= BT_ATT_PERM_READ_ENCRYPT;
-
-	if (ext_props & BT_GATT_CHRC_EXT_PROP_ENC_WRITE)
-		perm |= BT_ATT_PERM_WRITE_ENCRYPT;
-
-	if (ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_READ)
-		perm |= BT_ATT_PERM_READ_AUTHEN;
-
-	if (ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_WRITE)
-		perm |= BT_ATT_PERM_WRITE_AUTHEN;
-
-	return perm;
-}
-
 static uint8_t ccc_write_cb(uint16_t value, void *user_data)
 {
 	struct external_chrc *chrc = user_data;
@@ -2112,7 +2105,6 @@ static bool database_add_chrc(struct external_service *service,
 						struct external_chrc *chrc)
 {
 	bt_uuid_t uuid;
-	uint32_t perm;
 	const struct queue_entry *entry;
 
 	if (!parse_uuid(chrc->proxy, &uuid)) {
@@ -2125,14 +2117,8 @@ static bool database_add_chrc(struct external_service *service,
 		return false;
 	}
 
-	/*
-	 * TODO: Once shared/gatt-server properly supports permission checks,
-	 * set the permissions based on a D-Bus property of the external
-	 * characteristic.
-	 */
-	perm = permissions_from_props(chrc->props, chrc->ext_props);
 	chrc->attrib = gatt_db_service_add_characteristic(service->attrib,
-						&uuid, perm,
+						&uuid, chrc->perm,
 						chrc->props, chrc_read_cb,
 						chrc_write_cb, chrc);
 	if (!chrc->attrib) {
diff --git a/src/shared/att-types.h b/src/shared/att-types.h
index c3062c0..4a9b67f 100644
--- a/src/shared/att-types.h
+++ b/src/shared/att-types.h
@@ -31,6 +31,7 @@
 #define BT_ATT_SECURITY_LOW	1
 #define BT_ATT_SECURITY_MEDIUM	2
 #define BT_ATT_SECURITY_HIGH	3
+#define BT_ATT_SECURITY_FIPS	4
 
 #define BT_ATT_DEFAULT_LE_MTU	23
 #define BT_ATT_MAX_LE_MTU	517
@@ -123,6 +124,10 @@ struct bt_att_pdu_error_rsp {
 					BT_ATT_PERM_WRITE_AUTHEN)
 #define BT_ATT_PERM_AUTHOR		0x40
 #define BT_ATT_PERM_NONE		0x80
+#define BT_ATT_PERM_READ_SECURE		0x0100
+#define BT_ATT_PERM_WRITE_SECURE	0x0200
+#define BT_ATT_PERM_SECURE		(BT_ATT_PERM_READ_SECURE | \
+					BT_ATT_PERM_WRITE_SECURE)
 
 /* GATT Characteristic Properties Bitfield values */
 #define BT_GATT_CHRC_PROP_BROADCAST			0x01
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index 123d9c1..79e01c8 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -398,6 +398,9 @@ static uint8_t check_permissions(struct bt_gatt_server *server,
 		return 0;
 
 	security = bt_att_get_security(server->att);
+	if (perm & BT_ATT_PERM_SECURE && security < BT_ATT_SECURITY_FIPS)
+		return BT_ATT_ERROR_AUTHENTICATION;
+
 	if (perm & BT_ATT_PERM_AUTHEN && security < BT_ATT_SECURITY_HIGH)
 		return BT_ATT_ERROR_AUTHENTICATION;
 
-- 
2.5.5


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCHv2 BlueZ 4/6] test: Update GATT examples with the new API
  2016-05-09 13:51 [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue Luiz Augusto von Dentz
  2016-05-09 13:51 ` [PATCHv2 BlueZ 2/6] doc/gatt-api: Merge RegisterProfile with RegisterApplication Luiz Augusto von Dentz
  2016-05-09 13:51 ` [PATCHv2 BlueZ 3/6] doc/gatt-api: Add secure flags Luiz Augusto von Dentz
@ 2016-05-09 13:51 ` Luiz Augusto von Dentz
  2016-05-09 13:51 ` [PATCHv2 BlueZ 5/6] client: Update to use new GATT API Luiz Augusto von Dentz
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2016-05-09 13:51 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

---
 test/example-gatt-client |   2 +-
 test/example-gatt-server |  90 +++++++++++++++++++++++++-------
 test/test-gatt-profile   | 130 ++++++++++++++++++++++++++++++++++++-----------
 3 files changed, 172 insertions(+), 50 deletions(-)

diff --git a/test/example-gatt-client b/test/example-gatt-client
index b77a627..4d1df2f 100755
--- a/test/example-gatt-client
+++ b/test/example-gatt-client
@@ -113,7 +113,7 @@ def hr_msrmt_changed_cb(iface, changed_props, invalidated_props):
 
 def start_client():
     # Read the Body Sensor Location value and print it asynchronously.
-    body_snsr_loc_chrc[0].ReadValue(reply_handler=body_sensor_val_cb,
+    body_snsr_loc_chrc[0].ReadValue({}, reply_handler=body_sensor_val_cb,
                                     error_handler=generic_error_cb,
                                     dbus_interface=GATT_CHRC_IFACE)
 
diff --git a/test/example-gatt-server b/test/example-gatt-server
index 93cf068..71aeb1b 100755
--- a/test/example-gatt-server
+++ b/test/example-gatt-server
@@ -166,13 +166,15 @@ class Characteristic(dbus.service.Object):
 
         return self.get_properties[GATT_CHRC_IFACE]
 
-    @dbus.service.method(GATT_CHRC_IFACE, out_signature='ay')
-    def ReadValue(self):
+    @dbus.service.method(GATT_CHRC_IFACE,
+                        in_signature='a{sv}',
+                        out_signature='ay')
+    def ReadValue(self, options):
         print('Default ReadValue called, returning error')
         raise NotSupportedException()
 
-    @dbus.service.method(GATT_CHRC_IFACE, in_signature='ay')
-    def WriteValue(self, value):
+    @dbus.service.method(GATT_CHRC_IFACE, in_signature='aya{sv}')
+    def WriteValue(self, value, options):
         print('Default WriteValue called, returning error')
         raise NotSupportedException()
 
@@ -222,13 +224,15 @@ class Descriptor(dbus.service.Object):
 
         return self.get_properties[GATT_CHRC_IFACE]
 
-    @dbus.service.method(GATT_DESC_IFACE, out_signature='ay')
-    def ReadValue(self):
+    @dbus.service.method(GATT_DESC_IFACE,
+                        in_signature='a{sv}',
+                        out_signature='ay')
+    def ReadValue(self, options):
         print ('Default ReadValue called, returning error')
         raise NotSupportedException()
 
-    @dbus.service.method(GATT_DESC_IFACE, in_signature='ay')
-    def WriteValue(self, value):
+    @dbus.service.method(GATT_DESC_IFACE, in_signature='aya{sv}')
+    def WriteValue(self, value, options):
         print('Default WriteValue called, returning error')
         raise NotSupportedException()
 
@@ -317,7 +321,7 @@ class BodySensorLocationChrc(Characteristic):
                 ['read'],
                 service)
 
-    def ReadValue(self):
+    def ReadValue(self, options):
         # Return 'Chest' as the sensor location.
         return [ 0x01 ]
 
@@ -331,7 +335,7 @@ class HeartRateControlPointChrc(Characteristic):
                 ['write'],
                 service)
 
-    def WriteValue(self, value):
+    def WriteValue(self, value, options):
         print('Heart Rate Control Point WriteValue called')
 
         if len(value) != 1:
@@ -393,7 +397,7 @@ class BatteryLevelCharacteristic(Characteristic):
         self.notify_battery_level()
         return True
 
-    def ReadValue(self):
+    def ReadValue(self, options):
         print('Battery Level read: ' + repr(self.battery_lvl))
         return [dbus.Byte(self.battery_lvl)]
 
@@ -425,6 +429,7 @@ class TestService(Service):
         Service.__init__(self, bus, index, self.TEST_SVC_UUID, False)
         self.add_characteristic(TestCharacteristic(bus, 0, self))
         self.add_characteristic(TestEncryptCharacteristic(bus, 1, self))
+        self.add_characteristic(TestSecureCharacteristic(bus, 2, self))
 
 class TestCharacteristic(Characteristic):
     """
@@ -445,11 +450,11 @@ class TestCharacteristic(Characteristic):
         self.add_descriptor(
                 CharacteristicUserDescriptionDescriptor(bus, 1, self))
 
-    def ReadValue(self):
+    def ReadValue(self, options):
         print('TestCharacteristic Read: ' + repr(self.value))
         return self.value
 
-    def WriteValue(self, value):
+    def WriteValue(self, value, options):
         print('TestCharacteristic Write: ' + repr(value))
         self.value = value
 
@@ -468,7 +473,7 @@ class TestDescriptor(Descriptor):
                 ['read', 'write'],
                 characteristic)
 
-    def ReadValue(self):
+    def ReadValue(self, options):
         return [
                 dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
         ]
@@ -491,10 +496,10 @@ class CharacteristicUserDescriptionDescriptor(Descriptor):
                 ['read', 'write'],
                 characteristic)
 
-    def ReadValue(self):
+    def ReadValue(self, options):
         return self.value
 
-    def WriteValue(self, value):
+    def WriteValue(self, value, options):
         if not self.writable:
             raise NotPermittedException()
         self.value = value
@@ -517,11 +522,11 @@ class TestEncryptCharacteristic(Characteristic):
         self.add_descriptor(
                 CharacteristicUserDescriptionDescriptor(bus, 3, self))
 
-    def ReadValue(self):
+    def ReadValue(self, options):
         print('TestCharacteristic Read: ' + repr(self.value))
         return self.value
 
-    def WriteValue(self, value):
+    def WriteValue(self, value, options):
         print('TestCharacteristic Write: ' + repr(value))
         self.value = value
 
@@ -539,7 +544,54 @@ class TestEncryptDescriptor(Descriptor):
                 ['encrypt-read', 'encrypt-write'],
                 characteristic)
 
-    def ReadValue(self):
+    def ReadValue(self, options):
+        return [
+                dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
+        ]
+
+
+class TestSecureCharacteristic(Characteristic):
+    """
+    Dummy test characteristic requiring secure connection.
+
+    """
+    TEST_CHRC_UUID = '12345678-1234-5678-1234-56789abcdef5'
+
+    def __init__(self, bus, index, service):
+        Characteristic.__init__(
+                self, bus, index,
+                self.TEST_CHRC_UUID,
+                ['secure-read', 'secure-write'],
+                service)
+        self.value = []
+        self.add_descriptor(TestEncryptDescriptor(bus, 2, self))
+        self.add_descriptor(
+                CharacteristicUserDescriptionDescriptor(bus, 3, self))
+
+    def ReadValue(self, options):
+        print('TestCharacteristic Read: ' + repr(self.value))
+        return self.value
+
+    def WriteValue(self, value, options):
+        print('TestCharacteristic Write: ' + repr(value))
+        self.value = value
+
+
+class TestSecureDescriptor(Descriptor):
+    """
+    Dummy test descriptor requiring secure connection. Returns a static value.
+
+    """
+    TEST_DESC_UUID = '12345678-1234-5678-1234-56789abcdef6'
+
+    def __init__(self, bus, index, characteristic):
+        Descriptor.__init__(
+                self, bus, index,
+                self.TEST_DESC_UUID,
+                ['secure-read', 'secure-write'],
+                characteristic)
+
+    def ReadValue(self, options):
         return [
                 dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
         ]
diff --git a/test/test-gatt-profile b/test/test-gatt-profile
index ad320b1..995a659 100755
--- a/test/test-gatt-profile
+++ b/test/test-gatt-profile
@@ -15,46 +15,116 @@ except ImportError:
   import gobject as GObject
 import bluezutils
 
-class GattProfile(dbus.service.Object):
-	@dbus.service.method("org.bluez.GattProfile1",
-					in_signature="", out_signature="")
-	def Release(self):
-		print("Release")
-		mainloop.quit()
+BLUEZ_SERVICE_NAME = 'org.bluez'
+GATT_MANAGER_IFACE = 'org.bluez.GattManager1'
+DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
+DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
 
-if __name__ == '__main__':
-	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+GATT_PROFILE_IFACE = 'org.bluez.GattProfile1'
+
+
+class InvalidArgsException(dbus.exceptions.DBusException):
+    _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
+
+
+class Application(dbus.service.Object):
+    def __init__(self, bus):
+        self.path = '/'
+        self.profiles = []
+        dbus.service.Object.__init__(self, bus, self.path)
+
+    def get_path(self):
+        return dbus.ObjectPath(self.path)
+
+    def add_profile(self, profile):
+        self.profiles.append(profile)
+
+    @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
+    def GetManagedObjects(self):
+        response = {}
+        print('GetManagedObjects')
+
+        for profile in self.profiles:
+            response[profile.get_path()] = profile.get_properties()
+
+        return response
+
+
+class Profile(dbus.service.Object):
+    PATH_BASE = '/org/bluez/example/profile'
 
-	bus = dbus.SystemBus()
+    def __init__(self, bus, uuids):
+        self.path = self.PATH_BASE
+        self.bus = bus
+        self.uuids = uuids
+        dbus.service.Object.__init__(self, bus, self.path)
+
+    def get_properties(self):
+        return {
+            GATT_PROFILE_IFACE: {
+                'UUIDs': self.uuids,
+            }
+        }
+
+    def get_path(self):
+        return dbus.ObjectPath(self.path)
+
+    @dbus.service.method(GATT_PROFILE_IFACE,
+                        in_signature="",
+                        out_signature="")
+    def Release(self):
+        print("Release")
+        mainloop.quit()
+
+    @dbus.service.method(DBUS_PROP_IFACE,
+                         in_signature='s',
+                         out_signature='a{sv}')
+    def GetAll(self, interface):
+        if interface != GATT_PROFILE_IFACE:
+            raise InvalidArgsException()
+
+        return self.get_properties[GATT_PROFILE_IFACE]
+
+
+def register_app_cb():
+    print('GATT application registered')
+
+
+def register_app_error_cb(error):
+    print('Failed to register application: ' + str(error))
+    mainloop.quit()
+
+if __name__ == '__main__':
+    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
 
-	path = bluezutils.find_adapter().object_path
+    bus = dbus.SystemBus()
 
-	manager = dbus.Interface(bus.get_object("org.bluez", path),
-				"org.bluez.GattManager1")
+    path = bluezutils.find_adapter().object_path
 
-	option_list = [
-			make_option("-u", "--uuid", action="store",
-					type="string", dest="uuid",
-					default=None),
-			make_option("-p", "--path", action="store",
-					type="string", dest="path",
-					default="/foo/bar/profile"),
-			]
+    manager = dbus.Interface(bus.get_object("org.bluez", path),
+                            GATT_MANAGER_IFACE)
 
-	opts = dbus.Dictionary({ }, signature='sv')
+    option_list = [make_option("-u", "--uuid", action="store",
+                                type="string", dest="uuid",
+                                default=None),
+    ]
 
-	parser = OptionParser(option_list=option_list)
+    opts = dbus.Dictionary({}, signature='sv')
 
-	(options, args) = parser.parse_args()
+    parser = OptionParser(option_list=option_list)
 
-	profile = GattProfile(bus, options.path)
+    (options, args) = parser.parse_args()
 
-	mainloop = GObject.MainLoop()
+    mainloop = GObject.MainLoop()
 
-	if not options.uuid:
-		options.uuid = str(uuid.uuid4())
+    if not options.uuid:
+        options.uuid = str(uuid.uuid4())
 
-	uuids = { options.uuid }
-	manager.RegisterProfile(options.path, uuids, opts)
+    app = Application(bus)
+    profile = Profile(bus, [options.uuid])
+    app.add_profile(profile)
+    manager.RegisterApplication(app.get_path(), {},
+                                reply_handler=register_app_cb,
+                                error_handler=register_app_error_cb)
 
-	mainloop.run()
+    mainloop.run()
-- 
2.5.5


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCHv2 BlueZ 5/6] client: Update to use new GATT API
  2016-05-09 13:51 [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue Luiz Augusto von Dentz
                   ` (2 preceding siblings ...)
  2016-05-09 13:51 ` [PATCHv2 BlueZ 4/6] test: Update GATT examples with the new API Luiz Augusto von Dentz
@ 2016-05-09 13:51 ` Luiz Augusto von Dentz
  2016-05-09 13:51 ` [PATCHv2 BlueZ 6/6] tools/gatt-service: " Luiz Augusto von Dentz
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2016-05-09 13:51 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This update use of ReadValue and WriteValue to include the options
introduced in the API.
---
 client/gatt.c | 27 +++++++++++++++++++++++++--
 1 file changed, 25 insertions(+), 2 deletions(-)

diff --git a/client/gatt.c b/client/gatt.c
index 7dd3c94..fee1cf9 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -379,9 +379,23 @@ static void read_reply(DBusMessage *message, void *user_data)
 	rl_hexdump(value, len);
 }
 
+static void read_setup(DBusMessageIter *iter, void *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);
+	/* TODO: Add offset support */
+	dbus_message_iter_close_container(iter, &dict);
+}
+
 static void read_attribute(GDBusProxy *proxy)
 {
-	if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply,
+	if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup, read_reply,
 							NULL, NULL) == FALSE) {
 		rl_printf("Failed to read\n");
 		return;
@@ -421,12 +435,21 @@ static void write_reply(DBusMessage *message, void *user_data)
 static void write_setup(DBusMessageIter *iter, void *user_data)
 {
 	struct iovec *iov = 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,
 						&iov->iov_base, iov->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);
+	/* TODO: Add offset support */
+	dbus_message_iter_close_container(iter, &dict);
 }
 
 static void write_attribute(GDBusProxy *proxy, char *arg)
-- 
2.5.5


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCHv2 BlueZ 6/6] tools/gatt-service: Update to use new GATT API
  2016-05-09 13:51 [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue Luiz Augusto von Dentz
                   ` (3 preceding siblings ...)
  2016-05-09 13:51 ` [PATCHv2 BlueZ 5/6] client: Update to use new GATT API Luiz Augusto von Dentz
@ 2016-05-09 13:51 ` Luiz Augusto von Dentz
  2016-05-09 22:57 ` [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue Vinicius Costa Gomes
       [not found] ` <CANCcNXdu8PZ7=iYeRO-4wir4hdmhuFCq_a7+VXdMgSXDNe_veA@mail.gmail.com>
  6 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2016-05-09 13:51 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This update use of ReadValue and WriteValue to include the options
introduced in the API.
---
 tools/gatt-service.c | 159 ++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 126 insertions(+), 33 deletions(-)

diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 329d1af..0c78c4d 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -29,6 +29,7 @@
 #include <stdio.h>
 #include <stdbool.h>
 #include <unistd.h>
+#include <string.h>
 #include <sys/signalfd.h>
 
 #include <glib.h>
@@ -127,32 +128,48 @@ static gboolean desc_get_value(const GDBusPropertyTable *property,
 	return desc_read(desc, iter);
 }
 
-static void desc_write(struct descriptor *desc, DBusMessageIter *iter)
+static void desc_write(struct descriptor *desc, const uint8_t *value, int len)
 {
-	DBusMessageIter array;
-	const uint8_t *value;
-	int vlen;
-
-	dbus_message_iter_recurse(iter, &array);
-	dbus_message_iter_get_fixed_array(&array, &value, &vlen);
-
 	g_free(desc->value);
-	desc->value = g_memdup(value, vlen);
-	desc->vlen = vlen;
+	desc->value = g_memdup(value, len);
+	desc->vlen = len;
 
 	g_dbus_emit_property_changed(connection, desc->path,
 					GATT_DESCRIPTOR_IFACE, "Value");
 }
 
+static int parse_value(DBusMessageIter *iter, const uint8_t **value, int *len)
+{
+	DBusMessageIter array;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return -EINVAL;
+
+	dbus_message_iter_recurse(iter, &array);
+	dbus_message_iter_get_fixed_array(&array, value, len);
+
+	return 0;
+}
+
 static void desc_set_value(const GDBusPropertyTable *property,
 				DBusMessageIter *iter,
 				GDBusPendingPropertySet id, void *user_data)
 {
 	struct descriptor *desc = user_data;
+	const uint8_t *value;
+	int len;
 
 	printf("Descriptor(%s): Set(\"Value\", ...)\n", desc->uuid);
 
-	desc_write(desc, iter);
+	if (parse_value(iter, &value, &len)) {
+		printf("Invalid value for Set('Value'...)\n");
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	desc_write(desc, value, len);
 
 	g_dbus_pending_property_success(id);
 }
@@ -249,15 +266,8 @@ static gboolean chr_get_props(const GDBusPropertyTable *property,
 	return TRUE;
 }
 
-static void chr_write(struct characteristic *chr, DBusMessageIter *iter)
+static void chr_write(struct characteristic *chr, const uint8_t *value, int len)
 {
-	DBusMessageIter array;
-	uint8_t *value;
-	int len;
-
-	dbus_message_iter_recurse(iter, &array);
-	dbus_message_iter_get_fixed_array(&array, &value, &len);
-
 	g_free(chr->value);
 	chr->value = g_memdup(value, len);
 	chr->vlen = len;
@@ -271,10 +281,12 @@ static void chr_set_value(const GDBusPropertyTable *property,
 				GDBusPendingPropertySet id, void *user_data)
 {
 	struct characteristic *chr = user_data;
+	const uint8_t *value;
+	int len;
 
 	printf("Characteristic(%s): Set('Value', ...)\n", chr->uuid);
 
-	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+	if (!parse_value(iter, &value, &len)) {
 		printf("Invalid value for Set('Value'...)\n");
 		g_dbus_pending_property_error(id,
 					ERROR_INTERFACE ".InvalidArguments",
@@ -282,7 +294,7 @@ static void chr_set_value(const GDBusPropertyTable *property,
 		return;
 	}
 
-	chr_write(chr, iter);
+	chr_write(chr, value, len);
 
 	g_dbus_pending_property_success(id);
 }
@@ -368,12 +380,53 @@ static void desc_iface_destroy(gpointer user_data)
 	g_free(desc);
 }
 
+static int parse_options(DBusMessageIter *iter, const char **device)
+{
+	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, "device") == 0) {
+			if (var != DBUS_TYPE_OBJECT_PATH)
+				return -EINVAL;
+			dbus_message_iter_get_basic(&value, device);
+			printf("Device: %s\n", *device);
+		}
+	}
+
+	return 0;
+}
+
 static DBusMessage *chr_read_value(DBusConnection *conn, DBusMessage *msg,
 							void *user_data)
 {
 	struct characteristic *chr = user_data;
 	DBusMessage *reply;
 	DBusMessageIter iter;
+	const char *device;
+
+	if (!dbus_message_iter_init(msg, &iter))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
+
+	if (parse_options(&iter, &device))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
 
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
@@ -392,10 +445,21 @@ static DBusMessage *chr_write_value(DBusConnection *conn, DBusMessage *msg,
 {
 	struct characteristic *chr = user_data;
 	DBusMessageIter iter;
+	const uint8_t *value;
+	int len;
+	const char *device;
 
 	dbus_message_iter_init(msg, &iter);
 
-	chr_write(chr, &iter);
+	if (parse_value(&iter, &value, &len))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
+
+	if (parse_options(&iter, &device))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
+
+	chr_write(chr, value, len);
 
 	return dbus_message_new_method_return(msg);
 }
@@ -415,10 +479,12 @@ static DBusMessage *chr_stop_notify(DBusConnection *conn, DBusMessage *msg,
 }
 
 static const GDBusMethodTable chr_methods[] = {
-	{ GDBUS_ASYNC_METHOD("ReadValue", NULL, GDBUS_ARGS({ "value", "ay" }),
-						chr_read_value) },
-	{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }),
-						NULL, chr_write_value) },
+	{ GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
+					GDBUS_ARGS({ "value", "ay" }),
+					chr_read_value) },
+	{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
+						{ "options", "a{sv}" }),
+					NULL, chr_write_value) },
 	{ GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chr_start_notify) },
 	{ GDBUS_METHOD("StopNotify", NULL, NULL, chr_stop_notify) },
 	{ }
@@ -430,6 +496,15 @@ static DBusMessage *desc_read_value(DBusConnection *conn, DBusMessage *msg,
 	struct descriptor *desc = user_data;
 	DBusMessage *reply;
 	DBusMessageIter iter;
+	const char *device;
+
+	if (!dbus_message_iter_init(msg, &iter))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
+
+	if (parse_options(&iter, &device))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
 
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
@@ -438,6 +513,10 @@ static DBusMessage *desc_read_value(DBusConnection *conn, DBusMessage *msg,
 
 	dbus_message_iter_init_append(reply, &iter);
 
+	if (parse_options(&iter, &device))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
+
 	desc_read(desc, &iter);
 
 	return reply;
@@ -448,20 +527,34 @@ static DBusMessage *desc_write_value(DBusConnection *conn, DBusMessage *msg,
 {
 	struct descriptor *desc = user_data;
 	DBusMessageIter iter;
+	const char *device;
+	const uint8_t *value;
+	int len;
 
-	dbus_message_iter_init(msg, &iter);
+	if (!dbus_message_iter_init(msg, &iter))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
+
+	if (parse_value(&iter, &value, &len))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
+
+	if (parse_options(&iter, &device))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
 
-	desc_write(desc, &iter);
+	desc_write(desc, value, len);
 
 	return dbus_message_new_method_return(msg);
 }
 
 static const GDBusMethodTable desc_methods[] = {
-	{ GDBUS_ASYNC_METHOD("ReadValue", NULL, GDBUS_ARGS({ "value", "ay" }),
-						desc_read_value) },
-	{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }),
-						NULL,
-						desc_write_value) },
+	{ GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
+					GDBUS_ARGS({ "value", "ay" }),
+					desc_read_value) },
+	{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
+						{ "options", "a{sv}" }),
+					NULL, desc_write_value) },
 	{ }
 };
 
-- 
2.5.5


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue
  2016-05-09 13:51 [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue Luiz Augusto von Dentz
                   ` (4 preceding siblings ...)
  2016-05-09 13:51 ` [PATCHv2 BlueZ 6/6] tools/gatt-service: " Luiz Augusto von Dentz
@ 2016-05-09 22:57 ` Vinicius Costa Gomes
  2016-05-17 14:21   ` Luiz Augusto von Dentz
       [not found] ` <CANCcNXdu8PZ7=iYeRO-4wir4hdmhuFCq_a7+VXdMgSXDNe_veA@mail.gmail.com>
  6 siblings, 1 reply; 13+ messages in thread
From: Vinicius Costa Gomes @ 2016-05-09 22:57 UTC (permalink / raw)
  To: Luiz Augusto von Dentz, linux-bluetooth

Hi Luiz,

Luiz Augusto von Dentz <luiz.dentz@gmail.com> writes:

> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>
> 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.
>

The series is looking good.


Cheers,
--
Vinicius

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue
  2016-05-09 22:57 ` [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue Vinicius Costa Gomes
@ 2016-05-17 14:21   ` Luiz Augusto von Dentz
  2016-05-18  7:59     ` Kasper Markus (ETAS-PSC/ECE1)
  0 siblings, 1 reply; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2016-05-17 14:21 UTC (permalink / raw)
  To: Vinicius Costa Gomes; +Cc: linux-bluetooth@vger.kernel.org

Hi,

On Tue, May 10, 2016 at 1:57 AM, Vinicius Costa Gomes
<vinicius.gomes@intel.com> wrote:
> Hi Luiz,
>
> Luiz Augusto von Dentz <luiz.dentz@gmail.com> writes:
>
>> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>>
>> 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.
>>
>
> The series is looking good.

If there are no comments to this set I will be pushing by the end of the day.

-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue
       [not found] ` <CANCcNXdu8PZ7=iYeRO-4wir4hdmhuFCq_a7+VXdMgSXDNe_veA@mail.gmail.com>
@ 2016-05-17 21:16   ` Puthikorn Voravootivat
  0 siblings, 0 replies; 13+ messages in thread
From: Puthikorn Voravootivat @ 2016-05-17 21:16 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth, Vinicius Costa Gomes

*resend in plain text

Hi.
I found a possible bug below. Comment in line.
Thanks

On Mon, May 9, 2016 at 6:51 AM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
>
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>
> 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 <stdbool.h>
>  #include <stdint.h>
> +#include <errno.h>
>
>  #include <dbus/dbus.h>
>
> @@ -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);
> +               }

Add dbus_message_iter_next(&dict); here?
>
> +       }
> +
> +       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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue
  2016-05-17 14:21   ` Luiz Augusto von Dentz
@ 2016-05-18  7:59     ` Kasper Markus (ETAS-PSC/ECE1)
  2016-05-18 13:55       ` Luiz Augusto von Dentz
  0 siblings, 1 reply; 13+ messages in thread
From: Kasper Markus (ETAS-PSC/ECE1) @ 2016-05-18  7:59 UTC (permalink / raw)
  To: linux-bluetooth@vger.kernel.org

SGksIA0KDQo+IC0tLS0tT3JpZ2luYWwgTWVzc2FnZS0tLS0tDQo+IEZyb206IGxpbnV4LWJsdWV0
b290aC1vd25lckB2Z2VyLmtlcm5lbC5vcmcgW21haWx0bzpsaW51eC1ibHVldG9vdGgtDQo+IG93
bmVyQHZnZXIua2VybmVsLm9yZ10gT24gQmVoYWxmIE9mIEx1aXogQXVndXN0byB2b24gRGVudHoN
Cj4gU2VudDogVHVlc2RheSwgTWF5IDE3LCAyMDE2IDQ6MjIgUE0NCj4gU3ViamVjdDogUmU6IFtQ
QVRDSHYyIEJsdWVaIDEvNl0gZG9jL2dhdHQtYXBpOiBBZGQgb3B0aW9ucyBkaWN0aW9uYXJ5IHRv
DQo+IFJlYWRWYWx1ZS9Xcml0ZVZhbHVlDQo+IA0KPiBIaSwNCj4gDQo+IE9uIFR1ZSwgTWF5IDEw
LCAyMDE2IGF0IDE6NTcgQU0sIFZpbmljaXVzIENvc3RhIEdvbWVzDQo+IDx2aW5pY2l1cy5nb21l
c0BpbnRlbC5jb20+IHdyb3RlOg0KPiA+IEhpIEx1aXosDQo+ID4NCj4gPiBMdWl6IEF1Z3VzdG8g
dm9uIERlbnR6IDxsdWl6LmRlbnR6QGdtYWlsLmNvbT4gd3JpdGVzOg0KPiA+DQo+ID4+IEZyb206
IEx1aXogQXVndXN0byB2b24gRGVudHogPGx1aXoudm9uLmRlbnR6QGludGVsLmNvbT4NCj4gPj4N
Cj4gPj4gVGhpcyBhZGRzIHRoZSBwb3NzaWJpbGl0eSB0byBwYXNzIGFuIG9mZnNldCB0byB0aGVz
ZSBvcGVyYXRpb25zLCBhbmQNCj4gPj4gYWxzbyBpbiB0aGUgc2VydmVyIGNhc2UgdG8gZ2l2ZSB0
aGUgZGV2aWNlIG9iamVjdC4NCj4gPj4gLS0tDQo+ID4+IHYyOiBGaXggVmluaWNpdXMgY29tbWVu
dHMsIGFkZCBuZWNlc3NhcnkgY2hhbmdlcyB0byBvdGhlciB0b29scyBhZmZlY3RlZC4NCj4gPj4N
Cj4gPg0KPiA+IFRoZSBzZXJpZXMgaXMgbG9va2luZyBnb29kLg0KPiANCj4gSWYgdGhlcmUgYXJl
IG5vIGNvbW1lbnRzIHRvIHRoaXMgc2V0IEkgd2lsbCBiZSBwdXNoaW5nIGJ5IHRoZSBlbmQgb2Yg
dGhlIGRheS4NCg0KQ3VycmVudGx5IHRoZSB3cml0ZSBjb21tYW5kIGluaGVyZW50bHkgc2VsZWN0
cyBhIHdyaXRlIG1ldGhvZCBiYXNlZCBvbiB0aGUgY2hhcmFjdGVyaXN0aWNzIHByb3BlcnRpZXMg
KHdyaXRlLXdpdGhvdXQtcmVzcG9uc2UsIHJlbGlhYmxlLXdyaXRlLi4uKS4gDQpUaGUgb3B0aW9u
IGRpY3Rpb25hcnkgd2l0aGluIHRoZSBBUEkgYWxsb3dzIGZvciBhZGRpbmcgYW5vdGhlciBvcHRp
b24gdG8gb3ZlcnJpZGUgdGhlIGRlZmF1bHQgc2VsZWN0aW9uIG9mIHRoZSB3cml0ZSBtZXRob2Qg
Zm9yIGNoYXJhY3RlcmlzdGljcyBzdXBwb3J0aW5nIG11bHRpcGxlIHdyaXRlIG9wdGlvbnMuIA0K
SSB0aHVzIGNvbnNpZGVyIHRoaXMgcGF0Y2ggYSBtb3JlIGdlbmVyaWMgZW5hYmxlciB3aXRoaW4g
dGhlIGdpdmVuIEFQSSB0byBwYXRoIHRoZSB3YXkgZm9yIGZ1cnRoZXIgaW1wcm92ZW1lbnRzLg0K
DQpCZXN0IHJlZ2FyZHMsDQpNYXJrdXMNCg0K

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue
  2016-05-18  7:59     ` Kasper Markus (ETAS-PSC/ECE1)
@ 2016-05-18 13:55       ` Luiz Augusto von Dentz
  2016-05-18 13:56         ` Luiz Augusto von Dentz
  0 siblings, 1 reply; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2016-05-18 13:55 UTC (permalink / raw)
  To: Kasper Markus (ETAS-PSC/ECE1); +Cc: linux-bluetooth@vger.kernel.org

Hi Markus,

On Wed, May 18, 2016 at 10:59 AM, Kasper Markus (ETAS-PSC/ECE1)
<Markus.Kasper@escrypt.com> wrote:
> Hi,
>
>> -----Original Message-----
>> From: linux-bluetooth-owner@vger.kernel.org [mailto:linux-bluetooth-
>> owner@vger.kernel.org] On Behalf Of Luiz Augusto von Dentz
>> Sent: Tuesday, May 17, 2016 4:22 PM
>> Subject: Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to
>> ReadValue/WriteValue
>>
>> Hi,
>>
>> On Tue, May 10, 2016 at 1:57 AM, Vinicius Costa Gomes
>> <vinicius.gomes@intel.com> wrote:
>> > Hi Luiz,
>> >
>> > Luiz Augusto von Dentz <luiz.dentz@gmail.com> writes:
>> >
>> >> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>> >>
>> >> 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.
>> >>
>> >
>> > The series is looking good.
>>
>> If there are no comments to this set I will be pushing by the end of the day.
>
> Currently the write command inherently selects a write method based on the characteristics properties (write-without-response, reliable-write...).
> The option dictionary within the API allows for adding another option to override the default selection of the write method for characteristics supporting multiple write options.
> I thus consider this patch a more generic enabler within the given API to path the way for further improvements.

Yes, I don't know where using a specific method would actually be
recommended since it the server should be able to indicate the proper
operation to use via properties but in case there is a real case where
the server has the properties somewhat broken then perhaps we can
include such feature.

-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue
  2016-05-18 13:55       ` Luiz Augusto von Dentz
@ 2016-05-18 13:56         ` Luiz Augusto von Dentz
  2016-05-18 15:31           ` Kasper Markus (ETAS-PSC/ECE1)
  0 siblings, 1 reply; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2016-05-18 13:56 UTC (permalink / raw)
  To: Kasper Markus (ETAS-PSC/ECE1); +Cc: linux-bluetooth@vger.kernel.org

Hi,

On Wed, May 18, 2016 at 4:55 PM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> Hi Markus,
>
> On Wed, May 18, 2016 at 10:59 AM, Kasper Markus (ETAS-PSC/ECE1)
> <Markus.Kasper@escrypt.com> wrote:
>> Hi,
>>
>>> -----Original Message-----
>>> From: linux-bluetooth-owner@vger.kernel.org [mailto:linux-bluetooth-
>>> owner@vger.kernel.org] On Behalf Of Luiz Augusto von Dentz
>>> Sent: Tuesday, May 17, 2016 4:22 PM
>>> Subject: Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to
>>> ReadValue/WriteValue
>>>
>>> Hi,
>>>
>>> On Tue, May 10, 2016 at 1:57 AM, Vinicius Costa Gomes
>>> <vinicius.gomes@intel.com> wrote:
>>> > Hi Luiz,
>>> >
>>> > Luiz Augusto von Dentz <luiz.dentz@gmail.com> writes:
>>> >
>>> >> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>>> >>
>>> >> 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.
>>> >>
>>> >
>>> > The series is looking good.
>>>
>>> If there are no comments to this set I will be pushing by the end of the day.
>>
>> Currently the write command inherently selects a write method based on the characteristics properties (write-without-response, reliable-write...).
>> The option dictionary within the API allows for adding another option to override the default selection of the write method for characteristics supporting multiple write options.
>> I thus consider this patch a more generic enabler within the given API to path the way for further improvements.
>
> Yes, I don't know where using a specific method would actually be
> recommended since it the server should be able to indicate the proper
> operation to use via properties but in case there is a real case where
> the server has the properties somewhat broken then perhaps we can
> include such feature.
>
> --
> Luiz Augusto von Dentz

Btw, these patches are now upstream.

-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue
  2016-05-18 13:56         ` Luiz Augusto von Dentz
@ 2016-05-18 15:31           ` Kasper Markus (ETAS-PSC/ECE1)
  0 siblings, 0 replies; 13+ messages in thread
From: Kasper Markus (ETAS-PSC/ECE1) @ 2016-05-18 15:31 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth@vger.kernel.org

Hi Luiz,

> -----Original Message-----
> From: Luiz Augusto von Dentz [mailto:luiz.dentz@gmail.com]
> Sent: Wednesday, May 18, 2016 3:56 PM
> To: Kasper Markus (ETAS-PSC/ECE1) <Markus.Kasper@escrypt.com>
> Cc: linux-bluetooth@vger.kernel.org
> Subject: Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to
> ReadValue/WriteValue
> 
> Hi,
> 
> On Wed, May 18, 2016 at 4:55 PM, Luiz Augusto von Dentz
> <luiz.dentz@gmail.com> wrote:
> > Hi Markus,
> >
> > On Wed, May 18, 2016 at 10:59 AM, Kasper Markus (ETAS-PSC/ECE1)
> > <Markus.Kasper@escrypt.com> wrote:
> >> Hi,
> >>
> >>> -----Original Message-----
> >>> From: linux-bluetooth-owner@vger.kernel.org [mailto:linux-bluetooth-
> >>> owner@vger.kernel.org] On Behalf Of Luiz Augusto von Dentz
> >>> Sent: Tuesday, May 17, 2016 4:22 PM
> >>> Subject: Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options
> >>> dictionary to ReadValue/WriteValue
> >>>
> >>> Hi,
> >>>
> >>> On Tue, May 10, 2016 at 1:57 AM, Vinicius Costa Gomes
> >>> <vinicius.gomes@intel.com> wrote:
> >>> > Hi Luiz,
> >>> >
> >>> > Luiz Augusto von Dentz <luiz.dentz@gmail.com> writes:
> >>> >
> >>> >> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> >>> >>
> >>> >> 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.
> >>> >>
> >>> >
> >>> > The series is looking good.
> >>>
> >>> If there are no comments to this set I will be pushing by the end of the day.
> >>
> >> Currently the write command inherently selects a write method based on the
> characteristics properties (write-without-response, reliable-write...).
> >> The option dictionary within the API allows for adding another option to
> override the default selection of the write method for characteristics supporting
> multiple write options.
> >> I thus consider this patch a more generic enabler within the given API to path
> the way for further improvements.
> >
> > Yes, I don't know where using a specific method would actually be
> > recommended since it the server should be able to indicate the proper
> > operation to use via properties but in case there is a real case where
> > the server has the properties somewhat broken then perhaps we can
> > include such feature.
> >
> > --
> > Luiz Augusto von Dentz
> 
> Btw, these patches are now upstream.
> 

I'm facing this problem for an iOS peripheral, where the properties of a characteristic are set to "write-without-response" only. The iOS stack adds a "reliable-write" descriptor to these characteristics, although write is not supported. 
While I'm quite sure the Apple stack does not behave correctly when advertising the "reliable-write" extended property for a characteristic without "write" property, having control over bluez's writes would be the easiest way to handle this. 
My current fix is a patched bluez: I modified the condition for a reliable write to check for both flags: "write" & "reliable-write" before using the reliable-write code.

Markus

> --
> Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2016-05-18 15:31 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-05-09 13:51 [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue Luiz Augusto von Dentz
2016-05-09 13:51 ` [PATCHv2 BlueZ 2/6] doc/gatt-api: Merge RegisterProfile with RegisterApplication Luiz Augusto von Dentz
2016-05-09 13:51 ` [PATCHv2 BlueZ 3/6] doc/gatt-api: Add secure flags Luiz Augusto von Dentz
2016-05-09 13:51 ` [PATCHv2 BlueZ 4/6] test: Update GATT examples with the new API Luiz Augusto von Dentz
2016-05-09 13:51 ` [PATCHv2 BlueZ 5/6] client: Update to use new GATT API Luiz Augusto von Dentz
2016-05-09 13:51 ` [PATCHv2 BlueZ 6/6] tools/gatt-service: " Luiz Augusto von Dentz
2016-05-09 22:57 ` [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue Vinicius Costa Gomes
2016-05-17 14:21   ` Luiz Augusto von Dentz
2016-05-18  7:59     ` Kasper Markus (ETAS-PSC/ECE1)
2016-05-18 13:55       ` Luiz Augusto von Dentz
2016-05-18 13:56         ` Luiz Augusto von Dentz
2016-05-18 15:31           ` Kasper Markus (ETAS-PSC/ECE1)
     [not found] ` <CANCcNXdu8PZ7=iYeRO-4wir4hdmhuFCq_a7+VXdMgSXDNe_veA@mail.gmail.com>
2016-05-17 21:16   ` Puthikorn Voravootivat

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.