* [PATCH v2 01/10] doc/gatt-api: Add AcquireWrite and AcquireNotify
2017-07-04 15:09 [PATCH v2 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
@ 2017-07-04 15:09 ` Luiz Augusto von Dentz
2017-07-04 16:01 ` Marcel Holtmann
2017-07-04 15:09 ` [PATCH v2 02/10] shared/gatt-client: Allow multiple ready callbacks Luiz Augusto von Dentz
` (9 subsequent siblings)
10 siblings, 1 reply; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-04 15:09 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 | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
index 6c98b87..3f4a3ca 100644
--- a/doc/gatt-api.txt
+++ b/doc/gatt-api.txt
@@ -91,6 +91,56 @@ 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
+ WriteAcquired property which relies on
+ 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 NotifyAcquired
+ which relies on notify Flag and no other client have
+ called StartNotify.
+
+ Notification are enabled during this procedure so
+ StartNotify shall not be called, any notification
+ will be dispatched via file descriptor therefore the
+ Value property is no affected during the time where
+ notify has been acquired.
+
+ 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 +175,18 @@ Properties string UUID [read-only]
when a notification or indication is received, upon
which a PropertiesChanged signal will be emitted.
+ boolean WriteAcquired [read-only, optional]
+
+ True, if this characteristic has been acquire by any
+ client using AcquireWrite. This properties is ommited
+ in case write-without-response flag is not set.
+
+ boolean NotifyAcquired [read-only, optional]
+
+ True, if this characteristic has been acquire by any
+ client using AcquireNotify. This properties is ommited
+ in case notify flag is not set.
+
boolean Notifying [read-only, optional]
True, if notifications or indications on this
--
2.9.4
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH v2 01/10] doc/gatt-api: Add AcquireWrite and AcquireNotify
2017-07-04 15:09 ` [PATCH v2 01/10] doc/gatt-api: " Luiz Augusto von Dentz
@ 2017-07-04 16:01 ` Marcel Holtmann
0 siblings, 0 replies; 13+ messages in thread
From: Marcel Holtmann @ 2017-07-04 16:01 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
Hi Luiz,
> 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 | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 62 insertions(+)
>
> diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
> index 6c98b87..3f4a3ca 100644
> --- a/doc/gatt-api.txt
> +++ b/doc/gatt-api.txt
> @@ -91,6 +91,56 @@ 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.
Aquire file descriptor and MTU for writing. Usage of WriteValue will be locked ..
Also you need to add an appropriate error code to WriteValue.
> +
> + Only works with characteristic that has
> + WriteAcquired property which relies on
> + 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.
Same as above.
> +
> + Only works with characteristic that has NotifyAcquired
> + which relies on notify Flag and no other client have
> + called StartNotify.
> +
> + Notification are enabled during this procedure so
> + StartNotify shall not be called, any notification
> + will be dispatched via file descriptor therefore the
> + Value property is no affected during the time where
> + notify has been acquired.
> +
> + 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 +175,18 @@ Properties string UUID [read-only]
> when a notification or indication is received, upon
> which a PropertiesChanged signal will be emitted.
>
> + boolean WriteAcquired [read-only, optional]
> +
> + True, if this characteristic has been acquire by any
has been acquired ..
> + client using AcquireWrite. This properties is ommited
> + in case write-without-response flag is not set.
> +
> + boolean NotifyAcquired [read-only, optional]
> +
> + True, if this characteristic has been acquire by any
See above.
> + client using AcquireNotify. This properties is ommited
> + in case notify flag is not set.
> +
> boolean Notifying [read-only, optional]
>
> True, if notifications or indications on this
Otherwise, looks good. Lets get this patch set merged.
Regards
Marcel
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 02/10] shared/gatt-client: Allow multiple ready callbacks
2017-07-04 15:09 [PATCH v2 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
2017-07-04 15:09 ` [PATCH v2 01/10] doc/gatt-api: " Luiz Augusto von Dentz
@ 2017-07-04 15:09 ` Luiz Augusto von Dentz
2017-07-04 15:09 ` [PATCH v2 03/10] gatt: Add implementation of AcquireWrite Luiz Augusto von Dentz
` (8 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-04 15:09 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] 13+ messages in thread
* [PATCH v2 03/10] gatt: Add implementation of AcquireWrite
2017-07-04 15:09 [PATCH v2 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
2017-07-04 15:09 ` [PATCH v2 01/10] doc/gatt-api: " Luiz Augusto von Dentz
2017-07-04 15:09 ` [PATCH v2 02/10] shared/gatt-client: Allow multiple ready callbacks Luiz Augusto von Dentz
@ 2017-07-04 15:09 ` Luiz Augusto von Dentz
2017-07-04 15:09 ` [PATCH v2 04/10] gatt: Add implementation of WriteAcquired Luiz Augusto von Dentz
` (7 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-04 15:09 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..9a660df 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 | O_NONBLOCK | O_CLOEXEC) < 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] 13+ messages in thread
* [PATCH v2 04/10] gatt: Add implementation of WriteAcquired
2017-07-04 15:09 [PATCH v2 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
` (2 preceding siblings ...)
2017-07-04 15:09 ` [PATCH v2 03/10] gatt: Add implementation of AcquireWrite Luiz Augusto von Dentz
@ 2017-07-04 15:09 ` Luiz Augusto von Dentz
2017-07-04 15:09 ` [PATCH v2 05/10] client: Add acquire-write command Luiz Augusto von Dentz
` (6 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-04 15:09 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 | 35 ++++++++++++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/src/gatt-client.c b/src/gatt-client.c
index 9a660df..9ab5f96 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -818,6 +818,27 @@ static gboolean characteristic_get_flags(const GDBusPropertyTable *property,
return TRUE;
}
+static gboolean
+characteristic_get_write_acquired(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 gboolean
+characteristic_write_acquired_exists(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct characteristic *chrc = data;
+
+ return (chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP);
+}
+
static void write_characteristic_cb(struct gatt_db_attribute *attr, int err,
void *user_data)
{
@@ -1025,6 +1046,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,
+ "WriteAcquired");
}
}
@@ -1080,8 +1105,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,
+ "WriteAcquired");
+ }
DBG("%s: sender %s io %p", dbus_message_get_member(msg),
dbus_message_get_sender(msg), io);
@@ -1406,6 +1436,9 @@ static const GDBusPropertyTable characteristic_properties[] = {
{ "Notifying", "b", characteristic_get_notifying, NULL,
characteristic_notifying_exists },
{ "Flags", "as", characteristic_get_flags, NULL, NULL },
+ { "WriteAcquired", "b", characteristic_get_write_acquired, NULL,
+ characteristic_write_acquired_exists,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{ }
};
--
2.9.4
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 05/10] client: Add acquire-write command
2017-07-04 15:09 [PATCH v2 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
` (3 preceding siblings ...)
2017-07-04 15:09 ` [PATCH v2 04/10] gatt: Add implementation of WriteAcquired Luiz Augusto von Dentz
@ 2017-07-04 15:09 ` Luiz Augusto von Dentz
2017-07-04 15:09 ` [PATCH v2 06/10] client: Add release-write command Luiz Augusto von Dentz
` (5 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-04 15:09 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 WriteAcquired: 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] 13+ messages in thread
* [PATCH v2 06/10] client: Add release-write command
2017-07-04 15:09 [PATCH v2 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
` (4 preceding siblings ...)
2017-07-04 15:09 ` [PATCH v2 05/10] client: Add acquire-write command Luiz Augusto von Dentz
@ 2017-07-04 15:09 ` Luiz Augusto von Dentz
2017-07-04 15:09 ` [PATCH v2 07/10] gatt: Add implementation of AcquireNotify Luiz Augusto von Dentz
` (4 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-04 15:09 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 WriteAcquired: 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] 13+ messages in thread
* [PATCH v2 07/10] gatt: Add implementation of AcquireNotify
2017-07-04 15:09 [PATCH v2 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
` (5 preceding siblings ...)
2017-07-04 15:09 ` [PATCH v2 06/10] client: Add release-write command Luiz Augusto von Dentz
@ 2017-07-04 15:09 ` Luiz Augusto von Dentz
2017-07-04 15:09 ` [PATCH v2 08/10] gatt: Add implementation of NotifyAcquired Luiz Augusto von Dentz
` (3 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-04 15:09 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 9ab5f96..571d310 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;
@@ -1040,16 +1047,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,
"WriteAcquired");
+ } else if (chrc->notify_io) {
+ pipe_io_destroy(chrc->notify_io);
+ chrc->notify_io = NULL;
}
}
@@ -1106,12 +1128,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,
"WriteAcquired");
- }
+ } else
+ chrc->notify_io->io = io;
DBG("%s: sender %s io %p", dbus_message_get_member(msg),
dbus_message_get_sender(msg), io);
@@ -1131,13 +1154,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;
}
}
@@ -1150,7 +1182,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)
@@ -1159,13 +1191,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;
}
@@ -1338,6 +1372,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)
{
@@ -1347,6 +1473,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);
@@ -1419,6 +1548,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);
@@ -1454,6 +1589,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,
@@ -1469,10 +1608,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] 13+ messages in thread
* [PATCH v2 08/10] gatt: Add implementation of NotifyAcquired
2017-07-04 15:09 [PATCH v2 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
` (6 preceding siblings ...)
2017-07-04 15:09 ` [PATCH v2 07/10] gatt: Add implementation of AcquireNotify Luiz Augusto von Dentz
@ 2017-07-04 15:09 ` Luiz Augusto von Dentz
2017-07-04 15:09 ` [PATCH v2 09/10] client: Add acquire-notify command Luiz Augusto von Dentz
` (2 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-04 15:09 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 | 35 ++++++++++++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/src/gatt-client.c b/src/gatt-client.c
index 571d310..8f3e60c 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -846,6 +846,27 @@ characteristic_write_acquired_exists(const GDBusPropertyTable *property,
return (chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP);
}
+static gboolean
+characteristic_get_notify_acquired(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 gboolean
+characteristic_notify_acquired_exists(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct characteristic *chrc = data;
+
+ return (chrc->props & BT_GATT_CHRC_PROP_NOTIFY);
+}
+
static void write_characteristic_cb(struct gatt_db_attribute *attr, int err,
void *user_data)
{
@@ -1072,6 +1093,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,
+ "NotifyAcquired");
}
}
@@ -1133,8 +1158,13 @@ static DBusMessage *characteristic_create_pipe(struct characteristic *chrc,
chrc->path,
GATT_CHARACTERISTIC_IFACE,
"WriteAcquired");
- } else
+ } else {
chrc->notify_io->io = io;
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ chrc->path,
+ GATT_CHARACTERISTIC_IFACE,
+ "NotifyAcquired");
+ }
DBG("%s: sender %s io %p", dbus_message_get_member(msg),
dbus_message_get_sender(msg), io);
@@ -1574,6 +1604,9 @@ static const GDBusPropertyTable characteristic_properties[] = {
{ "WriteAcquired", "b", characteristic_get_write_acquired, NULL,
characteristic_write_acquired_exists,
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "NotifyAcquired", "b", characteristic_get_notify_acquired, NULL,
+ characteristic_notify_acquired_exists,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{ }
};
--
2.9.4
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 09/10] client: Add acquire-notify command
2017-07-04 15:09 [PATCH v2 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
` (7 preceding siblings ...)
2017-07-04 15:09 ` [PATCH v2 08/10] gatt: Add implementation of NotifyAcquired Luiz Augusto von Dentz
@ 2017-07-04 15:09 ` Luiz Augusto von Dentz
2017-07-04 15:09 ` [PATCH v2 10/10] client: Add release-notify command Luiz Augusto von Dentz
2017-07-05 11:34 ` [PATCH v2 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-04 15:09 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 NotifyAcquired: 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, ¬ify_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] 13+ messages in thread
* [PATCH v2 10/10] client: Add release-notify command
2017-07-04 15:09 [PATCH v2 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
` (8 preceding siblings ...)
2017-07-04 15:09 ` [PATCH v2 09/10] client: Add acquire-notify command Luiz Augusto von Dentz
@ 2017-07-04 15:09 ` Luiz Augusto von Dentz
2017-07-05 11:34 ` [PATCH v2 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-04 15:09 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 NotifyAcquired: 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] 13+ messages in thread
* Re: [PATCH v2 00/10] gatt: Add AcquireWrite and AcquireNotify
2017-07-04 15:09 [PATCH v2 00/10] gatt: Add AcquireWrite and AcquireNotify Luiz Augusto von Dentz
` (9 preceding siblings ...)
2017-07-04 15:09 ` [PATCH v2 10/10] client: Add release-notify command Luiz Augusto von Dentz
@ 2017-07-05 11:34 ` Luiz Augusto von Dentz
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-05 11:34 UTC (permalink / raw)
To: linux-bluetooth
Hi,
On Tue, Jul 4, 2017 at 6:09 PM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> 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.
>
> v2:
> - Rename WriteLocked and NotifyLocked to WriteAcquired and NotifyAcquired
> - Make WriteAcquired and NotifyAcquired optional so they only appear if
> the required flags are supported.
> - Make sure O_CLOEXEC and O_NONBLOCK are set in the flags passed to pipe2.
>
> 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 WriteAcquired
> client: Add acquire-write command
> client: Add release-write command
> gatt: Add implementation of AcquireNotify
> gatt: Add implementation of NotifyAcquired
> 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 | 62 ++++++++
> peripheral/gatt.c | 4 +-
> src/device.c | 16 ++-
> src/gatt-client.c | 367 +++++++++++++++++++++++++++++++++++++++++++++++
> 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, 759 insertions(+), 27 deletions(-)
>
> --
> 2.9.4
Applied after adjusting documentation according to the review comments.
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 13+ messages in thread