All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH BlueZ 00/10] gatt: Add AcquireWrite and AcquireNotify
@ 2017-07-03 12:11 Luiz Augusto von Dentz
  2017-07-03 12:11 ` [PATCH BlueZ 01/10] doc/gatt-api: " Luiz Augusto von Dentz
                   ` (9 more replies)
  0 siblings, 10 replies; 11+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-03 12:11 UTC (permalink / raw)
  To: linux-bluetooth

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

This set adds 2 new methods to GattCharacteristics1, thse deal with
attributes that attempts to emulate a byte stream transfer, or a protocol
on top of GATT, using write without response and notifications.

Luiz Augusto von Dentz (10):
  doc/gatt-api: Add AcquireWrite and AcquireNotify
  shared/gatt-client: Allow multiple ready callbacks
  gatt: Add implementation of AcquireWrite
  gatt: Add implementation of WriteLocked
  client: Add acquire-write command
  client: Add release-write command
  gatt: Add implementation of AcquireNotify
  gatt: Add implementation of NotifyLocked
  client: Add acquire-notify command
  client: Add release-notify command

 Makefile.tools           |   4 +-
 client/gatt.c            | 209 ++++++++++++++++++++++++++++
 client/gatt.h            |   6 +
 client/main.c            |  48 +++++++
 doc/gatt-api.txt         |  55 ++++++++
 peripheral/gatt.c        |   4 +-
 src/device.c             |  16 ++-
 src/gatt-client.c        | 347 +++++++++++++++++++++++++++++++++++++++++++++++
 src/shared/gatt-client.c |  60 ++++++--
 src/shared/gatt-client.h |   4 +-
 tools/btgatt-client.c    |   2 +-
 unit/test-gatt.c         |   4 +-
 12 files changed, 732 insertions(+), 27 deletions(-)

-- 
2.9.4


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

* [PATCH BlueZ 01/10] doc/gatt-api: Add AcquireWrite and AcquireNotify
  2017-07-03 12:11 [PATCH BlueZ 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
@ 2017-07-03 12:11 ` Luiz Augusto von Dentz
  2017-07-03 12:11 ` [PATCH BlueZ 02/10] shared/gatt-client: Allow multiple ready callbacks Luiz Augusto von Dentz
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-03 12:11 UTC (permalink / raw)
  To: linux-bluetooth

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

This enables write and notify exclusive access via file descriptors in
case the characteristic is actually trying to emulate a byte stream
transfer or have a protocol on top of GATT.
---
 doc/gatt-api.txt | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
index 6c98b87..02f389c 100644
--- a/doc/gatt-api.txt
+++ b/doc/gatt-api.txt
@@ -91,6 +91,51 @@ Methods		array{byte} ReadValue(dict options)
 					 org.bluez.Error.NotAuthorized
 					 org.bluez.Error.NotSupported
 
+		fd, uint16 AcquireWrite() [experimental] (Client only)
+
+			Acquire write file descriptor and MTU and lock
+			write access to the characteristic.
+
+			Only works with characteristic that has
+			write-without-response Flag.
+
+			To release the lock the client shall close the file
+			descriptor, a HUP is generated in case the device
+			is disconnected.
+
+			Note: the MTU can only be negotiated once and is
+			symmetric therefore this method may be delayed in
+			order to have the exchange MTU completed, because of
+			that the file descriptor is closed during
+			reconnections as the MTU has to be renegotiated.
+
+			Possible Errors: org.bluez.Error.Failed
+					 org.bluez.Error.NotSupported
+
+		fd, uint16 AcquireNotify() [experimental] (Client only)
+
+			Acquire notify file descriptor and MTU and lock
+			notifications to the characteristic.
+
+			Only works with characteristic that has notify
+			flag and no other client have called StartNotify.
+
+			Notification are enabled during this procedure so
+			StartNotify shall not be called.
+
+			To release the lock the client shall close the file
+			descriptor, a HUP is generated in case the device
+			is disconnected.
+
+			Note: the MTU can only be negotiated once and is
+			symmetric therefore this method may be delayed in
+			order to have the exchange MTU completed, because of
+			that the file descriptor is closed during
+			reconnections as the MTU has to be renegotiated.
+
+			Possible Errors: org.bluez.Error.Failed
+					 org.bluez.Error.NotSupported
+
 		void StartNotify()
 
 			Starts a notification session from this characteristic
@@ -125,6 +170,16 @@ Properties	string UUID [read-only]
 			when a notification or indication is received, upon
 			which a PropertiesChanged signal will be emitted.
 
+		boolean WriteLocked [read-only]
+
+			True, if this characteristic has been acquire by any
+			client using AcquireWrite.
+
+		boolean NotifyLocked [read-only]
+
+			True, if this characteristic has been acquire by any
+			client using AcquireNotify.
+
 		boolean Notifying [read-only, optional]
 
 			True, if notifications or indications on this
-- 
2.9.4


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

* [PATCH BlueZ 02/10] shared/gatt-client: Allow multiple ready callbacks
  2017-07-03 12:11 [PATCH BlueZ 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
  2017-07-03 12:11 ` [PATCH BlueZ 01/10] doc/gatt-api: " Luiz Augusto von Dentz
@ 2017-07-03 12:11 ` Luiz Augusto von Dentz
  2017-07-03 12:11 ` [PATCH BlueZ 03/10] gatt: Add implementation of AcquireWrite Luiz Augusto von Dentz
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-03 12:11 UTC (permalink / raw)
  To: linux-bluetooth

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

This makes the ready callbacks much more convenient to track when the
client is ready since its is now possible to notify more than on client
at the same time.
---
 peripheral/gatt.c        |  4 ++--
 src/device.c             | 10 ++++----
 src/shared/gatt-client.c | 60 ++++++++++++++++++++++++++++++++++++------------
 src/shared/gatt-client.h |  4 +++-
 tools/btgatt-client.c    |  2 +-
 unit/test-gatt.c         |  4 ++--
 6 files changed, 59 insertions(+), 25 deletions(-)

diff --git a/peripheral/gatt.c b/peripheral/gatt.c
index 4c5531d..5ae19a8 100644
--- a/peripheral/gatt.c
+++ b/peripheral/gatt.c
@@ -145,8 +145,8 @@ static struct gatt_conn *gatt_conn_new(int fd)
 		return NULL;
 	}
 
-	bt_gatt_client_set_ready_handler(conn->client,
-				client_ready_callback, conn, NULL);
+	bt_gatt_client_ready_register(conn->client, client_ready_callback,
+								conn, NULL);
 	bt_gatt_client_set_service_changed(conn->client,
 				client_service_changed_callback, conn, NULL);
 
diff --git a/src/device.c b/src/device.c
index def192f..8cb79df 100644
--- a/src/device.c
+++ b/src/device.c
@@ -226,6 +226,7 @@ struct btd_device {
 	struct gatt_db *db;			/* GATT db cache */
 	struct bt_gatt_client *client;		/* GATT client instance */
 	struct bt_gatt_server *server;		/* GATT server instance */
+	unsigned int gatt_ready_id;
 
 	struct btd_gatt_client *client_dbus;
 
@@ -550,7 +551,7 @@ static void gatt_client_cleanup(struct btd_device *device)
 
 	gatt_cache_cleanup(device);
 	bt_gatt_client_set_service_changed(device->client, NULL, NULL, NULL);
-	bt_gatt_client_set_ready_handler(device->client, NULL, NULL, NULL);
+	bt_gatt_client_ready_unregister(device->client, device->gatt_ready_id);
 	bt_gatt_client_unref(device->client);
 	device->client = NULL;
 }
@@ -4718,10 +4719,11 @@ static void gatt_client_init(struct btd_device *device)
 	 */
 	device_accept_gatt_profiles(device);
 
-	if (!bt_gatt_client_set_ready_handler(device->client,
+	device->gatt_ready_id = bt_gatt_client_ready_register(device->client,
 							gatt_client_ready_cb,
-							device, NULL)) {
-		DBG("Failed to set ready handler");
+							device, NULL);
+	if (!device->gatt_ready_id) {
+		DBG("Failed to register GATT ready callback");
 		gatt_client_cleanup(device);
 		return;
 	}
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index fbf90ff..a34b001 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -51,6 +51,12 @@
 #define GATT_SVC_UUID	0x1801
 #define SVC_CHNGD_UUID	0x2a05
 
+struct ready_cb {
+	bt_gatt_client_callback_t callback;
+	bt_gatt_client_destroy_func_t destroy;
+	void *data;
+};
+
 struct bt_gatt_client {
 	struct bt_att *att;
 	int ref_count;
@@ -58,9 +64,7 @@ struct bt_gatt_client {
 	struct bt_gatt_client *parent;
 	struct queue *clones;
 
-	bt_gatt_client_callback_t ready_callback;
-	bt_gatt_client_destroy_func_t ready_destroy;
-	void *ready_data;
+	struct queue *ready_cbs;
 
 	bt_gatt_client_service_changed_callback_t svc_chngd_callback;
 	bt_gatt_client_destroy_func_t svc_chngd_destroy;
@@ -1097,17 +1101,35 @@ done:
 	discovery_op_complete(op, success, att_ecode);
 }
 
+static void ready_destroy(void *data)
+{
+	struct ready_cb *ready = data;
+
+	if (ready->destroy)
+		ready->destroy(ready->data);
+
+	free(ready);
+}
+
 static void notify_client_ready(struct bt_gatt_client *client, bool success,
 							uint8_t att_ecode)
 {
 	const struct queue_entry *entry;
 
-	if (!client->ready_callback || client->ready)
+	if (client->ready)
 		return;
 
 	bt_gatt_client_ref(client);
 	client->ready = success;
-	client->ready_callback(success, att_ecode, client->ready_data);
+
+	for (entry = queue_get_entries(client->ready_cbs); entry;
+							entry = entry->next) {
+		struct ready_cb *ready = entry->data;
+
+		ready->callback(success, att_ecode, ready->data);
+	}
+
+	queue_remove_all(client->ready_cbs, NULL, NULL, ready_destroy);
 
 	/* Notify clones */
 	for (entry = queue_get_entries(client->clones); entry;
@@ -1731,8 +1753,7 @@ static void bt_gatt_client_free(struct bt_gatt_client *client)
 
 	queue_destroy(client->notify_list, notify_data_cleanup);
 
-	if (client->ready_destroy)
-		client->ready_destroy(client->ready_data);
+	queue_destroy(client->ready_cbs, ready_destroy);
 
 	if (client->debug_destroy)
 		client->debug_destroy(client->debug_data);
@@ -1789,6 +1810,7 @@ static struct bt_gatt_client *gatt_client_new(struct gatt_db *db,
 		goto fail;
 
 	client->clones = queue_new();
+	client->ready_cbs = queue_new();
 	client->long_write_queue = queue_new();
 	client->svc_chngd_queue = queue_new();
 	client->notify_list = queue_new();
@@ -1886,22 +1908,30 @@ bool bt_gatt_client_is_ready(struct bt_gatt_client *client)
 	return (client && client->ready);
 }
 
-bool bt_gatt_client_set_ready_handler(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_ready_register(struct bt_gatt_client *client,
 					bt_gatt_client_callback_t callback,
 					void *user_data,
 					bt_gatt_client_destroy_func_t destroy)
 {
+	struct ready_cb *ready;
+
 	if (!client)
-		return false;
+		return 0;
 
-	if (client->ready_destroy)
-		client->ready_destroy(client->ready_data);
+	ready = new0(struct ready_cb, 1);
+	ready->callback = callback;
+	ready->destroy = destroy;
+	ready->data = user_data;
 
-	client->ready_callback = callback;
-	client->ready_destroy = destroy;
-	client->ready_data = user_data;
+	queue_push_tail(client->ready_cbs, ready);
 
-	return true;
+	return PTR_TO_UINT(ready);
+}
+
+bool bt_gatt_client_ready_unregister(struct bt_gatt_client *client,
+						unsigned int id)
+{
+	return queue_remove(client->ready_cbs, UINT_TO_PTR(id));
 }
 
 bool bt_gatt_client_set_service_changed(struct bt_gatt_client *client,
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index aceb570..6d8bf80 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -57,10 +57,12 @@ typedef void (*bt_gatt_client_service_changed_callback_t)(uint16_t start_handle,
 							void *user_data);
 
 bool bt_gatt_client_is_ready(struct bt_gatt_client *client);
-bool bt_gatt_client_set_ready_handler(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_ready_register(struct bt_gatt_client *client,
 					bt_gatt_client_callback_t callback,
 					void *user_data,
 					bt_gatt_client_destroy_func_t destroy);
+bool bt_gatt_client_ready_unregister(struct bt_gatt_client *client,
+						unsigned int id);
 bool bt_gatt_client_set_service_changed(struct bt_gatt_client *client,
 			bt_gatt_client_service_changed_callback_t callback,
 			void *user_data,
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index f97963e..51bc362 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -235,7 +235,7 @@ static struct client *client_create(int fd, uint16_t mtu)
 									NULL);
 	}
 
-	bt_gatt_client_set_ready_handler(cli->gatt, ready_cb, cli, NULL);
+	bt_gatt_client_ready_register(cli->gatt, ready_cb, cli, NULL);
 	bt_gatt_client_set_service_changed(cli->gatt, service_changed_cb, cli,
 									NULL);
 
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 15638dc..5d79e94 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -684,8 +684,8 @@ static struct context *create_context(uint16_t mtu, gconstpointer data)
 		bt_gatt_client_set_debug(context->client, print_debug,
 						"bt_gatt_client:", NULL);
 
-		bt_gatt_client_set_ready_handler(context->client,
-						client_ready_cb, context, NULL);
+		bt_gatt_client_ready_register(context->client, client_ready_cb,
+								context, NULL);
 		break;
 	default:
 		break;
-- 
2.9.4


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

* [PATCH BlueZ 03/10] gatt: Add implementation of AcquireWrite
  2017-07-03 12:11 [PATCH BlueZ 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
  2017-07-03 12:11 ` [PATCH BlueZ 01/10] doc/gatt-api: " Luiz Augusto von Dentz
  2017-07-03 12:11 ` [PATCH BlueZ 02/10] shared/gatt-client: Allow multiple ready callbacks Luiz Augusto von Dentz
@ 2017-07-03 12:11 ` Luiz Augusto von Dentz
  2017-07-03 12:11 ` [PATCH BlueZ 04/10] gatt: Add implementation of WriteLocked Luiz Augusto von Dentz
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-03 12:11 UTC (permalink / raw)
  To: linux-bluetooth

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

This implements AcquireWrite creating a pipe and passing the write fd
to the application requesting it:

bluetoothd[29915]: src/gatt-client.c:characteristic_create_pipe() AcquireWrite: sender :1.378 io 0x89cdfe0

The fd is monitored and in case the client decides close it, or exit/crash,
the daemon detects the HUP and cleanup properly:

bluetoothd[29915]: src/gatt-client.c:characteristic_pipe_hup() /org/bluez/hci1/dev_00_1B_DC_07_31_88/service001f/char0020: io 0x89cdfe0
---
 src/device.c      |   8 ++-
 src/gatt-client.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 168 insertions(+), 1 deletion(-)

diff --git a/src/device.c b/src/device.c
index 8cb79df..6a9936a 100644
--- a/src/device.c
+++ b/src/device.c
@@ -551,7 +551,13 @@ static void gatt_client_cleanup(struct btd_device *device)
 
 	gatt_cache_cleanup(device);
 	bt_gatt_client_set_service_changed(device->client, NULL, NULL, NULL);
-	bt_gatt_client_ready_unregister(device->client, device->gatt_ready_id);
+
+	if (device->gatt_ready_id > 0) {
+		bt_gatt_client_ready_unregister(device->client,
+						device->gatt_ready_id);
+		device->gatt_ready_id = 0;
+	}
+
 	bt_gatt_client_unref(device->client);
 	device->client = NULL;
 }
diff --git a/src/gatt-client.c b/src/gatt-client.c
index 6c67841..b66ee4e 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -24,6 +24,8 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
 
 #include <dbus/dbus.h>
 
@@ -37,6 +39,7 @@
 #include "error.h"
 #include "adapter.h"
 #include "device.h"
+#include "src/shared/io.h"
 #include "src/shared/queue.h"
 #include "src/shared/att.h"
 #include "src/shared/gatt-db.h"
@@ -97,6 +100,10 @@ struct characteristic {
 	bt_uuid_t uuid;
 	char *path;
 
+	unsigned int ready_id;
+	DBusMessage *acquire_write;
+	struct io *write_io;
+
 	struct async_dbus_op *read_op;
 	struct async_dbus_op *write_op;
 
@@ -990,6 +997,151 @@ fail:
 	return btd_error_not_supported(msg);
 }
 
+static bool chrc_pipe_read(struct io *io, void *user_data)
+{
+	struct characteristic *chrc = user_data;
+	struct bt_gatt_client *gatt = chrc->service->client->gatt;
+	uint8_t buf[512];
+	int fd = io_get_fd(io);
+	ssize_t bytes_read;
+
+	bytes_read = read(fd, buf, sizeof(buf));
+	if (bytes_read < 0)
+		return false;
+
+	if (!gatt)
+		return false;
+
+	bt_gatt_client_write_without_response(gatt, chrc->value_handle,
+					chrc->props & BT_GATT_CHRC_PROP_AUTH,
+					buf, bytes_read);
+
+	return true;
+}
+
+static void characteristic_destroy_pipe(struct characteristic *chrc,
+							struct io *io)
+{
+	if (io == chrc->write_io) {
+		io_destroy(chrc->write_io);
+		chrc->write_io = NULL;
+	}
+}
+
+static bool characteristic_pipe_hup(struct io *io, void *user_data)
+{
+	struct characteristic *chrc = user_data;
+
+	DBG("%s: io %p", chrc->path, io);
+
+	characteristic_destroy_pipe(chrc, io);
+
+	return false;
+}
+
+static DBusMessage *characteristic_create_pipe(struct characteristic *chrc,
+						DBusMessage *msg)
+{
+	struct bt_gatt_client *gatt = chrc->service->client->gatt;
+	int pipefd[2];
+	struct io *io;
+	bool dir;
+	uint16_t mtu;
+	DBusMessage *reply;
+
+	if (!gatt || !bt_gatt_client_is_ready(gatt))
+		return btd_error_failed(msg, "Not connected");
+
+	if (pipe2(pipefd, O_DIRECT) < 0)
+		return btd_error_failed(msg, strerror(errno));
+
+	dir = dbus_message_has_member(msg, "AcquireWrite");
+
+	io = io_new(pipefd[!dir]);
+	if (!io) {
+		close(pipefd[0]);
+		close(pipefd[1]);
+		return btd_error_failed(msg, strerror(EIO));
+	}
+
+	io_set_close_on_destroy(io, true);
+
+	if (!io_set_read_handler(io, chrc_pipe_read, chrc, NULL))
+		goto fail;
+
+	if (!io_set_disconnect_handler(io, characteristic_pipe_hup, chrc, NULL))
+		goto fail;
+
+	mtu = bt_gatt_client_get_mtu(gatt);
+
+	reply = g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD, &pipefd[dir],
+					DBUS_TYPE_UINT16, &mtu,
+					DBUS_TYPE_INVALID);
+
+	close(pipefd[dir]);
+
+	if (dir)
+		chrc->write_io = io;
+
+	DBG("%s: sender %s io %p", dbus_message_get_member(msg),
+					dbus_message_get_sender(msg), io);
+
+	return reply;
+
+fail:
+	io_destroy(io);
+	close(pipefd[dir]);
+	return btd_error_failed(msg, strerror(EIO));
+}
+
+static void characteristic_ready(bool success, uint8_t ecode, void *user_data)
+{
+	struct characteristic *chrc = user_data;
+	DBusMessage *reply;
+
+	chrc->ready_id = 0;
+
+	if (chrc->acquire_write) {
+		reply = characteristic_create_pipe(chrc, chrc->acquire_write);
+
+		g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+		dbus_message_unref(chrc->acquire_write);
+		chrc->acquire_write = NULL;
+	}
+}
+
+static DBusMessage *characteristic_acquire_write(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct characteristic *chrc = user_data;
+	struct bt_gatt_client *gatt = chrc->service->client->gatt;
+
+	if (!gatt)
+		return btd_error_failed(msg, "Not connected");
+
+	if (chrc->write_io || chrc->acquire_write)
+		return btd_error_failed(msg, strerror(EBUSY));
+
+	if (chrc->write_op)
+		return btd_error_in_progress(msg);
+
+	if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP))
+		return btd_error_not_supported(msg);
+
+	if (!bt_gatt_client_is_ready(gatt)) {
+		DBG("GATT not ready, wait until it becomes read");
+		if (!chrc->ready_id)
+			chrc->ready_id = bt_gatt_client_ready_register(gatt,
+							characteristic_ready,
+							chrc, NULL);
+		chrc->acquire_write = dbus_message_ref(msg);
+		return NULL;
+	}
+
+	return characteristic_create_pipe(chrc, msg);
+}
+
 struct notify_client {
 	struct characteristic *chrc;
 	int ref_count;
@@ -1265,6 +1417,10 @@ static const GDBusMethodTable characteristic_methods[] = {
 						{ "options", "a{sv}" }),
 					NULL,
 					characteristic_write_value) },
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("AcquireWrite", NULL,
+					GDBUS_ARGS({ "fd", "h" },
+						{ "mtu", "q" }),
+					characteristic_acquire_write) },
 	{ GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL,
 					characteristic_start_notify) },
 	{ GDBUS_METHOD("StopNotify", NULL, NULL,
@@ -1280,6 +1436,11 @@ static void characteristic_free(void *data)
 	queue_destroy(chrc->descs, NULL);
 	queue_destroy(chrc->notify_clients, NULL);
 
+	io_destroy(chrc->write_io);
+
+	if (chrc->acquire_write)
+		dbus_message_unref(chrc->acquire_write);
+
 	g_free(chrc->path);
 	free(chrc);
 }
-- 
2.9.4


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

* [PATCH BlueZ 04/10] gatt: Add implementation of WriteLocked
  2017-07-03 12:11 [PATCH BlueZ 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
                   ` (2 preceding siblings ...)
  2017-07-03 12:11 ` [PATCH BlueZ 03/10] gatt: Add implementation of AcquireWrite Luiz Augusto von Dentz
@ 2017-07-03 12:11 ` Luiz Augusto von Dentz
  2017-07-03 12:11 ` [PATCH BlueZ 05/10] client: Add acquire-write command Luiz Augusto von Dentz
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-03 12:11 UTC (permalink / raw)
  To: linux-bluetooth

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

This property indicate if any client has acquire write thus locking the
access to WriteValue.
---
 src/gatt-client.c | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/src/gatt-client.c b/src/gatt-client.c
index b66ee4e..12bdbdc 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -818,6 +818,18 @@ static gboolean characteristic_get_flags(const GDBusPropertyTable *property,
 	return TRUE;
 }
 
+static gboolean
+characteristic_get_write_locked(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct characteristic *chrc = data;
+	dbus_bool_t locked = chrc->write_io ? TRUE : FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &locked);
+
+	return TRUE;
+}
+
 static void write_characteristic_cb(struct gatt_db_attribute *attr, int err,
 								void *user_data)
 {
@@ -1025,6 +1037,10 @@ static void characteristic_destroy_pipe(struct characteristic *chrc,
 	if (io == chrc->write_io) {
 		io_destroy(chrc->write_io);
 		chrc->write_io = NULL;
+		g_dbus_emit_property_changed(btd_get_dbus_connection(),
+						chrc->path,
+						GATT_CHARACTERISTIC_IFACE,
+						"WriteLocked");
 	}
 }
 
@@ -1080,8 +1096,13 @@ static DBusMessage *characteristic_create_pipe(struct characteristic *chrc,
 
 	close(pipefd[dir]);
 
-	if (dir)
+	if (dir) {
 		chrc->write_io = io;
+		g_dbus_emit_property_changed(btd_get_dbus_connection(),
+						chrc->path,
+						GATT_CHARACTERISTIC_IFACE,
+						"WriteLocked");
+	}
 
 	DBG("%s: sender %s io %p", dbus_message_get_member(msg),
 					dbus_message_get_sender(msg), io);
@@ -1406,6 +1427,8 @@ static const GDBusPropertyTable characteristic_properties[] = {
 	{ "Notifying", "b", characteristic_get_notifying, NULL,
 					characteristic_notifying_exists },
 	{ "Flags", "as", characteristic_get_flags, NULL, NULL },
+	{ "WriteLocked", "b", characteristic_get_write_locked, NULL, NULL,
+				G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 	{ }
 };
 
-- 
2.9.4


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

* [PATCH BlueZ 05/10] client: Add acquire-write command
  2017-07-03 12:11 [PATCH BlueZ 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
                   ` (3 preceding siblings ...)
  2017-07-03 12:11 ` [PATCH BlueZ 04/10] gatt: Add implementation of WriteLocked Luiz Augusto von Dentz
@ 2017-07-03 12:11 ` Luiz Augusto von Dentz
  2017-07-03 12:11 ` [PATCH BlueZ 06/10] client: Add release-write command Luiz Augusto von Dentz
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-03 12:11 UTC (permalink / raw)
  To: linux-bluetooth

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

This adds acquire-write command which uses D-Bus AcquireWrite methods
to acquire a file descriptor to write to locking the attribute:

[Test peripheral:/service001f/char0020]# acquire-write
[CHG] Attribute /org/bluez/hci1/dev_00_1B_DC_07_31_88/service001f/char0020 WriteLocked: yes
AcquireWrite success: fd 7 MTU 65
[Test peripheral:/service001f/char0020]# write 00
Attempting to write fd 7

< ACL Data TX: Handle 3585 flags 0x00 dlen 8
      ATT: Write Command (0x52) len 3
        Handle: 0x0021
          Data: 00
---
 client/gatt.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 client/gatt.h |  2 ++
 client/main.c | 12 ++++++++++
 3 files changed, 87 insertions(+)

diff --git a/client/gatt.c b/client/gatt.c
index 8a4491a..e6f37ae 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -89,6 +89,10 @@ static GList *descriptors;
 static GList *managers;
 static GList *uuids;
 
+static GDBusProxy *write_proxy;
+static int write_fd = -1;
+static uint16_t write_mtu;
+
 static void print_service(struct service *service, const char *description)
 {
 	const char *text;
@@ -236,6 +240,15 @@ void gatt_remove_characteristic(GDBusProxy *proxy)
 	characteristics = g_list_delete_link(characteristics, l);
 
 	print_characteristic(proxy, COLORED_DEL);
+
+	if (write_proxy == proxy) {
+		write_proxy = NULL;
+		write_mtu = 0;
+		if (write_fd > 0) {
+			close(write_fd);
+			write_fd = -1;
+		}
+	}
 }
 
 static void print_desc(struct desc *desc, const char *description)
@@ -619,6 +632,16 @@ static void write_attribute(GDBusProxy *proxy, char *arg)
 	iov.iov_base = value;
 	iov.iov_len = i;
 
+	/* Write using the fd if it has been acquired and fit the MTU */
+	if (proxy == write_proxy && (write_fd > 0 && write_mtu >= i)) {
+		rl_printf("Attempting to write fd %d\n", write_fd);
+		if (writev(write_fd, &iov, 1) < 0) {
+			rl_printf("Failed to write: %s", strerror(errno));
+			return;
+		}
+		return;
+	}
+
 	if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup,
 					write_reply, &iov, NULL) == FALSE) {
 		rl_printf("Failed to write\n");
@@ -643,6 +666,56 @@ void gatt_write_attribute(GDBusProxy *proxy, const char *arg)
 						g_dbus_proxy_get_path(proxy));
 }
 
+static void acquire_write_reply(DBusMessage *message, void *user_data)
+{
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		rl_printf("Failed to acquire write: %s\n", error.name);
+		dbus_error_free(&error);
+		write_proxy = NULL;
+		return;
+	}
+
+	if (write_fd > 0) {
+		close(write_fd);
+		write_fd = -1;
+	}
+
+	write_mtu = 0;
+
+	if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &write_fd,
+					DBUS_TYPE_UINT16, &write_mtu,
+					DBUS_TYPE_INVALID) == false)) {
+		rl_printf("Invalid AcquireWrite response\n");
+		return;
+	}
+
+	rl_printf("AcquireWrite success: fd %d MTU %u\n", write_fd, write_mtu);
+}
+
+void gatt_acquire_write(GDBusProxy *proxy, const char *arg)
+{
+	const char *iface;
+
+	iface = g_dbus_proxy_get_interface(proxy);
+	if (strcmp(iface, "org.bluez.GattCharacteristic1")) {
+		rl_printf("Unable to acquire write: %s not a characteristic\n",
+						g_dbus_proxy_get_path(proxy));
+		return;
+	}
+
+	if (g_dbus_proxy_method_call(proxy, "AcquireWrite", NULL,
+				acquire_write_reply, NULL, NULL) == FALSE) {
+		rl_printf("Failed to AcquireWrite\n");
+		return;
+	}
+
+	write_proxy = proxy;
+}
+
 static void notify_reply(DBusMessage *message, void *user_data)
 {
 	bool enable = GPOINTER_TO_UINT(user_data);
diff --git a/client/gatt.h b/client/gatt.h
index 8031a46..713d34f 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -38,6 +38,8 @@ void gatt_read_attribute(GDBusProxy *proxy);
 void gatt_write_attribute(GDBusProxy *proxy, const char *arg);
 void gatt_notify_attribute(GDBusProxy *proxy, bool enable);
 
+void gatt_acquire_write(GDBusProxy *proxy, const char *arg);
+
 void gatt_add_manager(GDBusProxy *proxy);
 void gatt_remove_manager(GDBusProxy *proxy);
 
diff --git a/client/main.c b/client/main.c
index 3af533e..79f61a1 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1799,6 +1799,16 @@ static void cmd_write(const char *arg)
 	gatt_write_attribute(default_attr, arg);
 }
 
+static void cmd_acquire_write(const char *arg)
+{
+	if (!default_attr) {
+		rl_printf("No attribute selected\n");
+		return;
+	}
+
+	gatt_acquire_write(default_attr, arg);
+}
+
 static void cmd_notify(const char *arg)
 {
 	dbus_bool_t enable;
@@ -2274,6 +2284,8 @@ static const struct {
 	{ "read",         NULL,       cmd_read, "Read attribute value" },
 	{ "write",        "<data=[xx xx ...]>", cmd_write,
 						"Write attribute value" },
+	{ "acquire-write", NULL, cmd_acquire_write,
+					"Acquire Write file descriptor" },
 	{ "notify",       "<on/off>", cmd_notify, "Notify attribute value" },
 	{ "register-application", "[UUID ...]", cmd_register_app,
 						"Register profile to connect" },
-- 
2.9.4


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

* [PATCH BlueZ 06/10] client: Add release-write command
  2017-07-03 12:11 [PATCH BlueZ 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
                   ` (4 preceding siblings ...)
  2017-07-03 12:11 ` [PATCH BlueZ 05/10] client: Add acquire-write command Luiz Augusto von Dentz
@ 2017-07-03 12:11 ` Luiz Augusto von Dentz
  2017-07-03 12:11 ` [PATCH BlueZ 07/10] gatt: Add implementation of AcquireNotify Luiz Augusto von Dentz
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-03 12:11 UTC (permalink / raw)
  To: linux-bluetooth

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

This adds release-write command which closes an existing fd unlocking
the attribute:

[Test peripheral:/service001f/char0020]# release-write
[CHG] Attribute /org/bluez/hci1/dev_00_1B_DC_07_31_88/service001f/char0020 WriteLocked: no
---
 client/gatt.c | 13 +++++++++++++
 client/gatt.h |  1 +
 client/main.c | 12 ++++++++++++
 3 files changed, 26 insertions(+)

diff --git a/client/gatt.c b/client/gatt.c
index e6f37ae..2756efd 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -716,6 +716,19 @@ void gatt_acquire_write(GDBusProxy *proxy, const char *arg)
 	write_proxy = proxy;
 }
 
+void gatt_release_write(GDBusProxy *proxy, const char *arg)
+{
+	if (proxy != write_proxy || write_fd < 0) {
+		rl_printf("Write not acquired\n");
+		return;
+	}
+
+	write_proxy = NULL;
+	close(write_fd);
+	write_fd = -1;
+	write_mtu = 0;
+}
+
 static void notify_reply(DBusMessage *message, void *user_data)
 {
 	bool enable = GPOINTER_TO_UINT(user_data);
diff --git a/client/gatt.h b/client/gatt.h
index 713d34f..6827057 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -39,6 +39,7 @@ void gatt_write_attribute(GDBusProxy *proxy, const char *arg);
 void gatt_notify_attribute(GDBusProxy *proxy, bool enable);
 
 void gatt_acquire_write(GDBusProxy *proxy, const char *arg);
+void gatt_release_write(GDBusProxy *proxy, const char *arg);
 
 void gatt_add_manager(GDBusProxy *proxy);
 void gatt_remove_manager(GDBusProxy *proxy);
diff --git a/client/main.c b/client/main.c
index 79f61a1..7688a23 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1809,6 +1809,16 @@ static void cmd_acquire_write(const char *arg)
 	gatt_acquire_write(default_attr, arg);
 }
 
+static void cmd_release_write(const char *arg)
+{
+	if (!default_attr) {
+		rl_printf("No attribute selected\n");
+		return;
+	}
+
+	gatt_release_write(default_attr, arg);
+}
+
 static void cmd_notify(const char *arg)
 {
 	dbus_bool_t enable;
@@ -2286,6 +2296,8 @@ static const struct {
 						"Write attribute value" },
 	{ "acquire-write", NULL, cmd_acquire_write,
 					"Acquire Write file descriptor" },
+	{ "release-write", NULL, cmd_release_write,
+					"Release Write file descriptor" },
 	{ "notify",       "<on/off>", cmd_notify, "Notify attribute value" },
 	{ "register-application", "[UUID ...]", cmd_register_app,
 						"Register profile to connect" },
-- 
2.9.4


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

* [PATCH BlueZ 07/10] gatt: Add implementation of AcquireNotify
  2017-07-03 12:11 [PATCH BlueZ 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
                   ` (5 preceding siblings ...)
  2017-07-03 12:11 ` [PATCH BlueZ 06/10] client: Add release-write command Luiz Augusto von Dentz
@ 2017-07-03 12:11 ` Luiz Augusto von Dentz
  2017-07-03 12:11 ` [PATCH BlueZ 08/10] gatt: Add implementation of NotifyLocked Luiz Augusto von Dentz
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-03 12:11 UTC (permalink / raw)
  To: linux-bluetooth

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

This implements AcquireNotify creating a pipe and passing the read fd
to the application requesting it, at same time subscribe for
notifications:

bluetoothd[7279]: src/gatt-client.c:notify_client_ref() owner :1.461
bluetoothd[7279]: src/gatt-client.c:characteristic_create_pipe() AcquireNotify: sender :1.461 io 0x8a60540
---
 src/gatt-client.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 155 insertions(+), 15 deletions(-)

diff --git a/src/gatt-client.c b/src/gatt-client.c
index 12bdbdc..6bff87b 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -89,6 +89,13 @@ struct async_dbus_op {
 	async_dbus_op_complete_t complete;
 };
 
+struct pipe_io {
+	DBusMessage *msg;
+	struct io *io;
+	void (*destroy)(void *data);
+	void *data;
+};
+
 struct characteristic {
 	struct service *service;
 	struct gatt_db_attribute *attr;
@@ -101,8 +108,8 @@ struct characteristic {
 	char *path;
 
 	unsigned int ready_id;
-	DBusMessage *acquire_write;
-	struct io *write_io;
+	struct pipe_io *write_io;
+	struct pipe_io *notify_io;
 
 	struct async_dbus_op *read_op;
 	struct async_dbus_op *write_op;
@@ -1031,16 +1038,31 @@ static bool chrc_pipe_read(struct io *io, void *user_data)
 	return true;
 }
 
+static void pipe_io_destroy(struct pipe_io *io)
+{
+	if (io->destroy)
+		io->destroy(io->data);
+
+	if (io->msg)
+		dbus_message_unref(io->msg);
+
+	io_destroy(io->io);
+	free(io);
+}
+
 static void characteristic_destroy_pipe(struct characteristic *chrc,
 							struct io *io)
 {
-	if (io == chrc->write_io) {
-		io_destroy(chrc->write_io);
+	if (chrc->write_io && io == chrc->write_io->io) {
+		pipe_io_destroy(chrc->write_io);
 		chrc->write_io = NULL;
 		g_dbus_emit_property_changed(btd_get_dbus_connection(),
 						chrc->path,
 						GATT_CHARACTERISTIC_IFACE,
 						"WriteLocked");
+	} else if (chrc->notify_io) {
+		pipe_io_destroy(chrc->notify_io);
+		chrc->notify_io = NULL;
 	}
 }
 
@@ -1097,12 +1119,13 @@ static DBusMessage *characteristic_create_pipe(struct characteristic *chrc,
 	close(pipefd[dir]);
 
 	if (dir) {
-		chrc->write_io = io;
+		chrc->write_io->io = io;
 		g_dbus_emit_property_changed(btd_get_dbus_connection(),
 						chrc->path,
 						GATT_CHARACTERISTIC_IFACE,
 						"WriteLocked");
-	}
+	} else
+		chrc->notify_io->io = io;
 
 	DBG("%s: sender %s io %p", dbus_message_get_member(msg),
 					dbus_message_get_sender(msg), io);
@@ -1122,13 +1145,22 @@ static void characteristic_ready(bool success, uint8_t ecode, void *user_data)
 
 	chrc->ready_id = 0;
 
-	if (chrc->acquire_write) {
-		reply = characteristic_create_pipe(chrc, chrc->acquire_write);
+	if (chrc->write_io->msg) {
+		reply = characteristic_create_pipe(chrc, chrc->write_io->msg);
 
 		g_dbus_send_message(btd_get_dbus_connection(), reply);
 
-		dbus_message_unref(chrc->acquire_write);
-		chrc->acquire_write = NULL;
+		dbus_message_unref(chrc->write_io->msg);
+		chrc->write_io->msg = NULL;
+	}
+
+	if (chrc->notify_io->msg) {
+		reply = characteristic_create_pipe(chrc, chrc->notify_io->msg);
+
+		g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+		dbus_message_unref(chrc->notify_io->msg);
+		chrc->notify_io->msg = NULL;
 	}
 }
 
@@ -1141,7 +1173,7 @@ static DBusMessage *characteristic_acquire_write(DBusConnection *conn,
 	if (!gatt)
 		return btd_error_failed(msg, "Not connected");
 
-	if (chrc->write_io || chrc->acquire_write)
+	if (chrc->write_io)
 		return btd_error_failed(msg, strerror(EBUSY));
 
 	if (chrc->write_op)
@@ -1150,13 +1182,15 @@ static DBusMessage *characteristic_acquire_write(DBusConnection *conn,
 	if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP))
 		return btd_error_not_supported(msg);
 
+	chrc->write_io = new0(struct pipe_io, 1);
+
 	if (!bt_gatt_client_is_ready(gatt)) {
 		DBG("GATT not ready, wait until it becomes read");
 		if (!chrc->ready_id)
 			chrc->ready_id = bt_gatt_client_ready_register(gatt,
 							characteristic_ready,
 							chrc, NULL);
-		chrc->acquire_write = dbus_message_ref(msg);
+		chrc->write_io->msg = dbus_message_ref(msg);
 		return NULL;
 	}
 
@@ -1329,6 +1363,98 @@ static void register_notify_cb(uint16_t att_ecode, void *user_data)
 	create_notify_reply(op, true, 0);
 }
 
+static void notify_io_cb(uint16_t value_handle, const uint8_t *value,
+					uint16_t length, void *user_data)
+{
+	struct iovec iov;
+	struct notify_client *client = user_data;
+	struct characteristic *chrc = client->chrc;
+	int err;
+
+	/* Drop notification if the pipe is not ready */
+	if (!chrc->notify_io->io)
+		return;
+
+	iov.iov_base = (void *) value;
+	iov.iov_len = length;
+
+	err = io_send(chrc->notify_io->io, &iov, 1);
+	if (err < 0)
+		error("io_send: %s", strerror(-err));
+}
+
+static void register_notify_io_cb(uint16_t att_ecode, void *user_data)
+{
+	struct notify_client *client = user_data;
+	struct characteristic *chrc = client->chrc;
+
+	if (!att_ecode)
+		return;
+
+	queue_remove(chrc->notify_clients, client);
+	notify_client_free(client);
+
+	pipe_io_destroy(chrc->notify_io);
+	chrc->notify_io = NULL;
+}
+
+static void notify_io_destroy(void *data)
+{
+	struct notify_client *client = data;
+
+	queue_remove(client->chrc->notify_clients, client);
+	notify_client_unref(client);
+}
+
+static DBusMessage *characteristic_acquire_notify(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct characteristic *chrc = user_data;
+	struct bt_gatt_client *gatt = chrc->service->client->gatt;
+	const char *sender = dbus_message_get_sender(msg);
+	struct notify_client *client;
+
+	if (!gatt)
+		return btd_error_failed(msg, "Not connected");
+
+	/* Each client can only have one active notify session. */
+	if (chrc->notify_io || !queue_isempty(chrc->notify_clients))
+		return btd_error_failed(msg, strerror(EBUSY));
+
+	if (!(chrc->props & BT_GATT_CHRC_PROP_NOTIFY ||
+				chrc->props & BT_GATT_CHRC_PROP_INDICATE))
+		return btd_error_not_supported(msg);
+
+	client = notify_client_create(chrc, sender);
+	if (!client)
+		return btd_error_failed(msg, "Failed allocate notify session");
+
+	client->notify_id = bt_gatt_client_register_notify(gatt,
+						chrc->value_handle,
+						register_notify_io_cb,
+						notify_io_cb,
+						client, NULL);
+	if (!client->notify_id)
+		return btd_error_failed(msg, "Failed to subscribe");
+
+	queue_push_tail(chrc->notify_clients, client);
+
+	chrc->notify_io = new0(struct pipe_io, 1);
+	chrc->notify_io->data = client;
+	chrc->notify_io->destroy = notify_io_destroy;
+
+	if (!bt_gatt_client_is_ready(gatt)) {
+		if (!chrc->ready_id)
+			chrc->ready_id = bt_gatt_client_ready_register(gatt,
+							characteristic_ready,
+							chrc, NULL);
+		chrc->notify_io->msg = dbus_message_ref(msg);
+		return NULL;
+	}
+
+	return characteristic_create_pipe(chrc, msg);
+}
+
 static DBusMessage *characteristic_start_notify(DBusConnection *conn,
 					DBusMessage *msg, void *user_data)
 {
@@ -1338,6 +1464,9 @@ static DBusMessage *characteristic_start_notify(DBusConnection *conn,
 	struct async_dbus_op *op;
 	struct notify_client *client;
 
+	if (chrc->notify_io)
+		return btd_error_failed(msg, strerror(EBUSY));
+
 	if (!(chrc->props & BT_GATT_CHRC_PROP_NOTIFY ||
 				chrc->props & BT_GATT_CHRC_PROP_INDICATE))
 		return btd_error_not_supported(msg);
@@ -1410,6 +1539,12 @@ static DBusMessage *characteristic_stop_notify(DBusConnection *conn,
 	if (!client)
 		return btd_error_failed(msg, "No notify session started");
 
+	if (chrc->notify_io) {
+		pipe_io_destroy(chrc->notify_io);
+		chrc->notify_io = NULL;
+		return dbus_message_new_method_return(msg);
+	}
+
 	queue_remove(chrc->service->client->all_notify_clients, client);
 	bt_gatt_client_unregister_notify(gatt, client->notify_id);
 	update_notifying(chrc);
@@ -1444,6 +1579,10 @@ static const GDBusMethodTable characteristic_methods[] = {
 					GDBUS_ARGS({ "fd", "h" },
 						{ "mtu", "q" }),
 					characteristic_acquire_write) },
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("AcquireNotify", NULL,
+					GDBUS_ARGS({ "fd", "h" },
+						{ "mtu", "q" }),
+					characteristic_acquire_notify) },
 	{ GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL,
 					characteristic_start_notify) },
 	{ GDBUS_METHOD("StopNotify", NULL, NULL,
@@ -1459,10 +1598,11 @@ static void characteristic_free(void *data)
 	queue_destroy(chrc->descs, NULL);
 	queue_destroy(chrc->notify_clients, NULL);
 
-	io_destroy(chrc->write_io);
+	if (chrc->write_io)
+		pipe_io_destroy(chrc->write_io);
 
-	if (chrc->acquire_write)
-		dbus_message_unref(chrc->acquire_write);
+	if (chrc->notify_io)
+		pipe_io_destroy(chrc->notify_io);
 
 	g_free(chrc->path);
 	free(chrc);
-- 
2.9.4


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

* [PATCH BlueZ 08/10] gatt: Add implementation of NotifyLocked
  2017-07-03 12:11 [PATCH BlueZ 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
                   ` (6 preceding siblings ...)
  2017-07-03 12:11 ` [PATCH BlueZ 07/10] gatt: Add implementation of AcquireNotify Luiz Augusto von Dentz
@ 2017-07-03 12:11 ` Luiz Augusto von Dentz
  2017-07-03 12:11 ` [PATCH BlueZ 09/10] client: Add acquire-notify command Luiz Augusto von Dentz
  2017-07-03 12:11 ` [PATCH BlueZ 10/10] client: Add release-notify command Luiz Augusto von Dentz
  9 siblings, 0 replies; 11+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-03 12:11 UTC (permalink / raw)
  To: linux-bluetooth

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

This property indicate if any client has acquire notify thus locking the
access to StartNotify.
---
 src/gatt-client.c | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/src/gatt-client.c b/src/gatt-client.c
index 6bff87b..85d71e2 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -837,6 +837,18 @@ characteristic_get_write_locked(const GDBusPropertyTable *property,
 	return TRUE;
 }
 
+static gboolean
+characteristic_get_notify_locked(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct characteristic *chrc = data;
+	dbus_bool_t locked = chrc->notify_io ? TRUE : FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &locked);
+
+	return TRUE;
+}
+
 static void write_characteristic_cb(struct gatt_db_attribute *attr, int err,
 								void *user_data)
 {
@@ -1063,6 +1075,10 @@ static void characteristic_destroy_pipe(struct characteristic *chrc,
 	} else if (chrc->notify_io) {
 		pipe_io_destroy(chrc->notify_io);
 		chrc->notify_io = NULL;
+		g_dbus_emit_property_changed(btd_get_dbus_connection(),
+						chrc->path,
+						GATT_CHARACTERISTIC_IFACE,
+						"NotifyLocked");
 	}
 }
 
@@ -1124,8 +1140,13 @@ static DBusMessage *characteristic_create_pipe(struct characteristic *chrc,
 						chrc->path,
 						GATT_CHARACTERISTIC_IFACE,
 						"WriteLocked");
-	} else
+	} else {
+		g_dbus_emit_property_changed(btd_get_dbus_connection(),
+						chrc->path,
+						GATT_CHARACTERISTIC_IFACE,
+						"NotifyLocked");
 		chrc->notify_io->io = io;
+	}
 
 	DBG("%s: sender %s io %p", dbus_message_get_member(msg),
 					dbus_message_get_sender(msg), io);
@@ -1564,6 +1585,8 @@ static const GDBusPropertyTable characteristic_properties[] = {
 	{ "Flags", "as", characteristic_get_flags, NULL, NULL },
 	{ "WriteLocked", "b", characteristic_get_write_locked, NULL, NULL,
 				G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "NotifyLocked", "b", characteristic_get_notify_locked, NULL, NULL,
+				G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 	{ }
 };
 
-- 
2.9.4


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

* [PATCH BlueZ 09/10] client: Add acquire-notify command
  2017-07-03 12:11 [PATCH BlueZ 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
                   ` (7 preceding siblings ...)
  2017-07-03 12:11 ` [PATCH BlueZ 08/10] gatt: Add implementation of NotifyLocked Luiz Augusto von Dentz
@ 2017-07-03 12:11 ` Luiz Augusto von Dentz
  2017-07-03 12:11 ` [PATCH BlueZ 10/10] client: Add release-notify command Luiz Augusto von Dentz
  9 siblings, 0 replies; 11+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-03 12:11 UTC (permalink / raw)
  To: linux-bluetooth

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

This adds acquire-notify command which uses D-Bus AcquireNotify method
to acquire a file descriptor to read notifications locking the attribute:

[Test peripheral:/service001f/char0020]# acquire-notify
[CHG] Attribute /org/bluez/hci1/dev_56_A0_AA_D0_12_FF/service001f/char0020 NotifyLocked: yes
AcquireNotify success: fd 7 MTU 65

< ACL Data TX: Handle 3585 flags 0x00 dlen 9
      ATT: Write Request (0x12) len 4
        Handle: 0x0022
          Data: 0200

[CHG] /org/bluez/hci1/dev_56_A0_AA_D0_12_FF/service001f/char0020 Notification:
  00

> ACL Data RX: Handle 3585 flags 0x02 dlen 8
      ATT: Handle Value Indication (0x1d) len 3
        Handle: 0x0021
          Data: 00
---
 Makefile.tools |   4 +-
 client/gatt.c  | 163 ++++++++++++++++++++++++++++++++++++++++++++++++---------
 client/gatt.h  |   2 +
 client/main.c  |  12 +++++
 4 files changed, 154 insertions(+), 27 deletions(-)

diff --git a/Makefile.tools b/Makefile.tools
index c0482fb..0fd6dec 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -9,8 +9,8 @@ client_bluetoothctl_SOURCES = client/main.c \
 					client/advertising.c \
 					client/gatt.h client/gatt.c \
 					monitor/uuid.h monitor/uuid.c
-client_bluetoothctl_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ \
-				-lreadline
+client_bluetoothctl_LDADD = gdbus/libgdbus-internal.la src/libshared-glib.la \
+				@GLIB_LIBS@ @DBUS_LIBS@ -lreadline
 endif
 
 if MONITOR
diff --git a/client/gatt.c b/client/gatt.c
index 2756efd..0533854 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -38,6 +38,7 @@
 #include <glib.h>
 
 #include "src/shared/queue.h"
+#include "src/shared/io.h"
 #include "gdbus/gdbus.h"
 #include "monitor/uuid.h"
 #include "display.h"
@@ -90,9 +91,13 @@ static GList *managers;
 static GList *uuids;
 
 static GDBusProxy *write_proxy;
-static int write_fd = -1;
+static struct io *write_io;
 static uint16_t write_mtu;
 
+static GDBusProxy *notify_proxy;
+static struct io *notify_io;
+static uint16_t notify_mtu;
+
 static void print_service(struct service *service, const char *description)
 {
 	const char *text;
@@ -229,6 +234,22 @@ void gatt_add_characteristic(GDBusProxy *proxy)
 	print_characteristic(proxy, COLORED_NEW);
 }
 
+static void notify_io_destroy(void)
+{
+	io_destroy(notify_io);
+	notify_io = NULL;
+	notify_proxy = NULL;
+	notify_mtu = 0;
+}
+
+static void write_io_destroy(void)
+{
+	io_destroy(write_io);
+	write_io = NULL;
+	write_proxy = NULL;
+	write_mtu = 0;
+}
+
 void gatt_remove_characteristic(GDBusProxy *proxy)
 {
 	GList *l;
@@ -241,14 +262,10 @@ void gatt_remove_characteristic(GDBusProxy *proxy)
 
 	print_characteristic(proxy, COLORED_DEL);
 
-	if (write_proxy == proxy) {
-		write_proxy = NULL;
-		write_mtu = 0;
-		if (write_fd > 0) {
-			close(write_fd);
-			write_fd = -1;
-		}
-	}
+	if (write_proxy == proxy)
+		write_io_destroy();
+	else if (notify_proxy == proxy)
+		notify_io_destroy();
 }
 
 static void print_desc(struct desc *desc, const char *description)
@@ -633,9 +650,9 @@ static void write_attribute(GDBusProxy *proxy, char *arg)
 	iov.iov_len = i;
 
 	/* Write using the fd if it has been acquired and fit the MTU */
-	if (proxy == write_proxy && (write_fd > 0 && write_mtu >= i)) {
-		rl_printf("Attempting to write fd %d\n", write_fd);
-		if (writev(write_fd, &iov, 1) < 0) {
+	if (proxy == write_proxy && (write_io && write_mtu >= i)) {
+		rl_printf("Attempting to write fd %d\n", io_get_fd(write_io));
+		if (io_send(write_io, &iov, 1) < 0) {
 			rl_printf("Failed to write: %s", strerror(errno));
 			return;
 		}
@@ -666,9 +683,57 @@ void gatt_write_attribute(GDBusProxy *proxy, const char *arg)
 						g_dbus_proxy_get_path(proxy));
 }
 
+static bool pipe_read(struct io *io, void *user_data)
+{
+	uint8_t buf[512];
+	int fd = io_get_fd(io);
+	ssize_t bytes_read;
+
+	if (io != notify_io)
+		return true;
+
+	bytes_read = read(fd, buf, sizeof(buf));
+	if (bytes_read < 0)
+		return false;
+
+	rl_printf("[" COLORED_CHG "] %s Notification:\n",
+			g_dbus_proxy_get_path(notify_proxy));
+	rl_hexdump(buf, bytes_read);
+
+	return true;
+}
+
+static bool pipe_hup(struct io *io, void *user_data)
+{
+	rl_printf("%s closed\n", io == notify_io ? "Notify" : "Write");
+
+	if (io == notify_io)
+		notify_io_destroy();
+	else
+		write_io_destroy();
+
+	return false;
+}
+
+static struct io *pipe_io_new(int fd)
+{
+	struct io *io;
+
+	io = io_new(fd);
+
+	io_set_close_on_destroy(io, true);
+
+	io_set_read_handler(io, pipe_read, NULL, NULL);
+
+	io_set_disconnect_handler(io, pipe_hup, NULL, NULL);
+
+	return io;
+}
+
 static void acquire_write_reply(DBusMessage *message, void *user_data)
 {
 	DBusError error;
+	int fd;
 
 	dbus_error_init(&error);
 
@@ -679,21 +744,19 @@ static void acquire_write_reply(DBusMessage *message, void *user_data)
 		return;
 	}
 
-	if (write_fd > 0) {
-		close(write_fd);
-		write_fd = -1;
-	}
+	if (write_io)
+		write_io_destroy();
 
-	write_mtu = 0;
-
-	if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &write_fd,
+	if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
 					DBUS_TYPE_UINT16, &write_mtu,
 					DBUS_TYPE_INVALID) == false)) {
 		rl_printf("Invalid AcquireWrite response\n");
 		return;
 	}
 
-	rl_printf("AcquireWrite success: fd %d MTU %u\n", write_fd, write_mtu);
+	rl_printf("AcquireWrite success: fd %d MTU %u\n", fd, write_mtu);
+
+	write_io = pipe_io_new(fd);
 }
 
 void gatt_acquire_write(GDBusProxy *proxy, const char *arg)
@@ -718,15 +781,65 @@ void gatt_acquire_write(GDBusProxy *proxy, const char *arg)
 
 void gatt_release_write(GDBusProxy *proxy, const char *arg)
 {
-	if (proxy != write_proxy || write_fd < 0) {
+	if (proxy != write_proxy || !write_io) {
 		rl_printf("Write not acquired\n");
 		return;
 	}
 
-	write_proxy = NULL;
-	close(write_fd);
-	write_fd = -1;
-	write_mtu = 0;
+	write_io_destroy();
+}
+
+static void acquire_notify_reply(DBusMessage *message, void *user_data)
+{
+	DBusError error;
+	int fd;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		rl_printf("Failed to acquire notify: %s\n", error.name);
+		dbus_error_free(&error);
+		write_proxy = NULL;
+		return;
+	}
+
+	if (notify_io) {
+		io_destroy(notify_io);
+		notify_io = NULL;
+	}
+
+	notify_mtu = 0;
+
+	if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
+					DBUS_TYPE_UINT16, &notify_mtu,
+					DBUS_TYPE_INVALID) == false)) {
+		rl_printf("Invalid AcquireWrite response\n");
+		return;
+	}
+
+	rl_printf("AcquireNotify success: fd %d MTU %u\n", fd, notify_mtu);
+
+	notify_io = pipe_io_new(fd);
+}
+
+void gatt_acquire_notify(GDBusProxy *proxy, const char *arg)
+{
+	const char *iface;
+
+	iface = g_dbus_proxy_get_interface(proxy);
+	if (strcmp(iface, "org.bluez.GattCharacteristic1")) {
+		rl_printf("Unable to acquire notify: %s not a characteristic\n",
+						g_dbus_proxy_get_path(proxy));
+		return;
+	}
+
+	if (g_dbus_proxy_method_call(proxy, "AcquireNotify", NULL,
+				acquire_notify_reply, NULL, NULL) == FALSE) {
+		rl_printf("Failed to AcquireNotify\n");
+		return;
+	}
+
+	notify_proxy = proxy;
 }
 
 static void notify_reply(DBusMessage *message, void *user_data)
diff --git a/client/gatt.h b/client/gatt.h
index 6827057..5dba26b 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -41,6 +41,8 @@ void gatt_notify_attribute(GDBusProxy *proxy, bool enable);
 void gatt_acquire_write(GDBusProxy *proxy, const char *arg);
 void gatt_release_write(GDBusProxy *proxy, const char *arg);
 
+void gatt_acquire_notify(GDBusProxy *proxy, const char *arg);
+
 void gatt_add_manager(GDBusProxy *proxy);
 void gatt_remove_manager(GDBusProxy *proxy);
 
diff --git a/client/main.c b/client/main.c
index 7688a23..e0a265d 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1819,6 +1819,16 @@ static void cmd_release_write(const char *arg)
 	gatt_release_write(default_attr, arg);
 }
 
+static void cmd_acquire_notify(const char *arg)
+{
+	if (!default_attr) {
+		rl_printf("No attribute selected\n");
+		return;
+	}
+
+	gatt_acquire_notify(default_attr, arg);
+}
+
 static void cmd_notify(const char *arg)
 {
 	dbus_bool_t enable;
@@ -2298,6 +2308,8 @@ static const struct {
 					"Acquire Write file descriptor" },
 	{ "release-write", NULL, cmd_release_write,
 					"Release Write file descriptor" },
+	{ "acquire-notify", NULL, cmd_acquire_notify,
+					"Acquire Notify file descriptor" },
 	{ "notify",       "<on/off>", cmd_notify, "Notify attribute value" },
 	{ "register-application", "[UUID ...]", cmd_register_app,
 						"Register profile to connect" },
-- 
2.9.4


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

* [PATCH BlueZ 10/10] client: Add release-notify command
  2017-07-03 12:11 [PATCH BlueZ 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
                   ` (8 preceding siblings ...)
  2017-07-03 12:11 ` [PATCH BlueZ 09/10] client: Add acquire-notify command Luiz Augusto von Dentz
@ 2017-07-03 12:11 ` Luiz Augusto von Dentz
  9 siblings, 0 replies; 11+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-03 12:11 UTC (permalink / raw)
  To: linux-bluetooth

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

This adds release-notify command which closes an existing fd unlocking
the attribute:

[Test peripheral:/service001f/char0020]# release-notify
[CHG] Attribute /org/bluez/hci1/dev_69_16_5B_9A_06_CD/service001f/char0020 NotifyLocked: no
---
 client/gatt.c | 10 ++++++++++
 client/gatt.h |  1 +
 client/main.c | 12 ++++++++++++
 3 files changed, 23 insertions(+)

diff --git a/client/gatt.c b/client/gatt.c
index 0533854..33e8936 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -842,6 +842,16 @@ void gatt_acquire_notify(GDBusProxy *proxy, const char *arg)
 	notify_proxy = proxy;
 }
 
+void gatt_release_notify(GDBusProxy *proxy, const char *arg)
+{
+	if (proxy != notify_proxy || !notify_io) {
+		rl_printf("Write not acquired\n");
+		return;
+	}
+
+	notify_io_destroy();
+}
+
 static void notify_reply(DBusMessage *message, void *user_data)
 {
 	bool enable = GPOINTER_TO_UINT(user_data);
diff --git a/client/gatt.h b/client/gatt.h
index 5dba26b..9bab429 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -42,6 +42,7 @@ void gatt_acquire_write(GDBusProxy *proxy, const char *arg);
 void gatt_release_write(GDBusProxy *proxy, const char *arg);
 
 void gatt_acquire_notify(GDBusProxy *proxy, const char *arg);
+void gatt_release_notify(GDBusProxy *proxy, const char *arg);
 
 void gatt_add_manager(GDBusProxy *proxy);
 void gatt_remove_manager(GDBusProxy *proxy);
diff --git a/client/main.c b/client/main.c
index e0a265d..8f75f0e 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1829,6 +1829,16 @@ static void cmd_acquire_notify(const char *arg)
 	gatt_acquire_notify(default_attr, arg);
 }
 
+static void cmd_release_notify(const char *arg)
+{
+	if (!default_attr) {
+		rl_printf("No attribute selected\n");
+		return;
+	}
+
+	gatt_release_notify(default_attr, arg);
+}
+
 static void cmd_notify(const char *arg)
 {
 	dbus_bool_t enable;
@@ -2310,6 +2320,8 @@ static const struct {
 					"Release Write file descriptor" },
 	{ "acquire-notify", NULL, cmd_acquire_notify,
 					"Acquire Notify file descriptor" },
+	{ "release-notify", NULL, cmd_release_notify,
+					"Release Notify file descriptor" },
 	{ "notify",       "<on/off>", cmd_notify, "Notify attribute value" },
 	{ "register-application", "[UUID ...]", cmd_register_app,
 						"Register profile to connect" },
-- 
2.9.4


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

end of thread, other threads:[~2017-07-03 12:11 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-03 12:11 [PATCH BlueZ 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
2017-07-03 12:11 ` [PATCH BlueZ 01/10] doc/gatt-api: " Luiz Augusto von Dentz
2017-07-03 12:11 ` [PATCH BlueZ 02/10] shared/gatt-client: Allow multiple ready callbacks Luiz Augusto von Dentz
2017-07-03 12:11 ` [PATCH BlueZ 03/10] gatt: Add implementation of AcquireWrite Luiz Augusto von Dentz
2017-07-03 12:11 ` [PATCH BlueZ 04/10] gatt: Add implementation of WriteLocked Luiz Augusto von Dentz
2017-07-03 12:11 ` [PATCH BlueZ 05/10] client: Add acquire-write command Luiz Augusto von Dentz
2017-07-03 12:11 ` [PATCH BlueZ 06/10] client: Add release-write command Luiz Augusto von Dentz
2017-07-03 12:11 ` [PATCH BlueZ 07/10] gatt: Add implementation of AcquireNotify Luiz Augusto von Dentz
2017-07-03 12:11 ` [PATCH BlueZ 08/10] gatt: Add implementation of NotifyLocked Luiz Augusto von Dentz
2017-07-03 12:11 ` [PATCH BlueZ 09/10] client: Add acquire-notify command Luiz Augusto von Dentz
2017-07-03 12:11 ` [PATCH BlueZ 10/10] client: Add release-notify command Luiz Augusto von Dentz

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.