2017-09-18 12:39:51

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 00/10] gatt: Add server support for AcquireWrite and AcquireNotify

From: Luiz Augusto von Dentz <[email protected]>

This implements similar mechanist to use dedicated file descriptors
for IO bypassing D-Bus.

Note: The current implementation opens just one fd per characteristic,
though it could be possible to have one fd per device.

Luiz Augusto von Dentz (10):
gatt: Remove useless debug
client: Rework variables for AcquireWrite/AcquireNotify
doc/gatt-api: Add server support for AcquireWrite and AcquireNotify
shared/gatt-server: Add bt_gatt_server_get_mtu
shared/gatt-db: Add gatt_db_attribute_get_user_data
gatt: Implement AcquireWrite for server
client: Implement AcquireWrite for server
gatt: Implement AcquireNotify for server
client: Implement AcquireNotify for server
gatt: Update signature of AcquireWrite and AcquireNotify

client/gatt.c | 319 ++++++++++++++++++++++++++++++++++++++++-------
doc/gatt-api.txt | 22 +++-
mesh/gatt.c | 23 +++-
src/gatt-client.c | 8 +-
src/gatt-database.c | 260 +++++++++++++++++++++++++++++++++++++-
src/shared/gatt-db.c | 8 ++
src/shared/gatt-db.h | 2 +
src/shared/gatt-server.c | 8 ++
src/shared/gatt-server.h | 1 +
9 files changed, 590 insertions(+), 61 deletions(-)

--
2.13.5



2017-09-19 09:57:14

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ 07/10] client: Implement AcquireWrite for server

Hi Yunhan,

On Tue, Sep 19, 2017 at 7:14 AM, Yunhan Wang <[email protected]> wrote:
> Hi, Luiz
>
> On Mon, Sep 18, 2017 at 5:39 AM, Luiz Augusto von Dentz
> <[email protected]> wrote:
>> From: Luiz Augusto von Dentz <[email protected]>
>>
>> This enables IO via file descriptors using AcquireWrite if server
>> implements it.
>> ---
>> client/gatt.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
>> 1 file changed, 158 insertions(+), 8 deletions(-)
>>
>> diff --git a/client/gatt.c b/client/gatt.c
>> index ab74aa695..6401fbd1a 100644
>> --- a/client/gatt.c
>> +++ b/client/gatt.c
>> @@ -32,6 +32,7 @@
>> #include <stdbool.h>
>> #include <sys/uio.h>
>> #include <wordexp.h>
>> +#include <fcntl.h>
>>
>> #include <readline/readline.h>
>> #include <readline/history.h>
>> @@ -73,6 +74,8 @@ struct chrc {
>> GList *descs;
>> int value_len;
>> uint8_t *value;
>> + uint16_t mtu;
>> + struct io *write_io;
>> };
>>
>> struct service {
>> @@ -683,19 +686,25 @@ void gatt_write_attribute(GDBusProxy *proxy, const char *arg)
>>
>> static bool pipe_read(struct io *io, void *user_data)
>> {
>> + struct chrc *chrc = user_data;
>> uint8_t buf[512];
>> int fd = io_get_fd(io);
>> ssize_t bytes_read;
>>
>> - if (io != notify_io.io)
>> + if (io != notify_io.io && !chrc)
>> 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_io.proxy));
>> + if (chrc)
>> + rl_printf("[" COLORED_CHG "] Attribute %s written:\n",
>> + chrc->path);
>> + else
>> + rl_printf("[" COLORED_CHG "] %s Notification:\n",
>> + g_dbus_proxy_get_path(notify_io.proxy));
>> +
>> rl_hexdump(buf, bytes_read);
>>
>> return true;
>> @@ -703,6 +712,17 @@ static bool pipe_read(struct io *io, void *user_data)
>>
>> static bool pipe_hup(struct io *io, void *user_data)
>> {
>> + struct chrc *chrc = user_data;
>> +
>> + if (chrc) {
>> + rl_printf("Attribute %s Write pipe closed\n", chrc->path);
>> + if (chrc->write_io) {
>> + io_destroy(chrc->write_io);
>> + chrc->write_io = NULL;
>> + }
>> + return false;
>> + }
>> +
>> rl_printf("%s closed\n", io == notify_io.io ? "Notify" : "Write");
>>
>> if (io == notify_io.io)
>> @@ -713,7 +733,7 @@ static bool pipe_hup(struct io *io, void *user_data)
>> return false;
>> }
>>
>> -static struct io *pipe_io_new(int fd)
>> +static struct io *pipe_io_new(int fd, void *user_data)
>> {
>> struct io *io;
>>
>> @@ -721,9 +741,9 @@ static struct io *pipe_io_new(int fd)
>>
>> io_set_close_on_destroy(io, true);
>>
>> - io_set_read_handler(io, pipe_read, NULL, NULL);
>> + io_set_read_handler(io, pipe_read, user_data, NULL);
>>
>> - io_set_disconnect_handler(io, pipe_hup, NULL, NULL);
>> + io_set_disconnect_handler(io, pipe_hup, user_data, NULL);
>>
>> return io;
>> }
>> @@ -754,7 +774,7 @@ static void acquire_write_reply(DBusMessage *message, void *user_data)
>>
>> rl_printf("AcquireWrite success: fd %d MTU %u\n", fd, write_io.mtu);
>>
>> - write_io.io = pipe_io_new(fd);
>> + write_io.io = pipe_io_new(fd, NULL);
>> }
>>
>> void gatt_acquire_write(GDBusProxy *proxy, const char *arg)
>> @@ -817,7 +837,7 @@ static void acquire_notify_reply(DBusMessage *message, void *user_data)
>>
>> rl_printf("AcquireNotify success: fd %d MTU %u\n", fd, notify_io.mtu);
>
> What is the difference about MTU obtained from acquire_write and
> acquire_notify? Same?

They should be the same if bearer is LE, and you can acquire them
individually so it is not required to acquire them both together.

>>
>> - notify_io.io = pipe_io_new(fd);
>> + notify_io.io = pipe_io_new(fd, NULL);
>> }
>>
>> void gatt_acquire_notify(GDBusProxy *proxy, const char *arg)
>> @@ -1302,12 +1322,41 @@ static gboolean chrc_get_flags(const GDBusPropertyTable *property,
>> return TRUE;
>> }
>>
>> +static gboolean chrc_get_write_acquired(const GDBusPropertyTable *property,
>> + DBusMessageIter *iter, void *data)
>> +{
>> + struct chrc *chrc = data;
>> + dbus_bool_t value;
>> +
>> + value = chrc->write_io ? TRUE : FALSE;
>> +
>> + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
>> +
>> + return TRUE;
>> +}
>> +
>> +static gboolean chrc_write_acquired_exists(const GDBusPropertyTable *property,
>> + void *data)
>> +{
>> + struct chrc *chrc = data;
>> + int i;
>> +
>> + for (i = 0; chrc->flags[i]; i++) {
>> + if (!strcmp("write-without-response", chrc->flags[i]))
>
> Why acquire_write only works with write-without-response? If my
> application is using characteristic with Write, how can I reuse this
> acquire_write?

The fd IO don't have a reply to confirm delivery, though for server we
may assume if the remote has WriteAcquired for an Attribute it means
it will accept any data written via fd, that is in fact what the
daemon is doing though the bluetoothctl only enables WriteAcquired
with write-without-response so we can state this in the documentation
so the server can tweak this to its needs.


>
>> + return TRUE;
>> + }
>> +
>> + return FALSE;
>> +}
>> +
>> static const GDBusPropertyTable chrc_properties[] = {
>> { "UUID", "s", chrc_get_uuid, NULL, NULL },
>> { "Service", "o", chrc_get_service, NULL, NULL },
>> { "Value", "ay", chrc_get_value, NULL, NULL },
>> { "Notifying", "b", chrc_get_notifying, NULL, NULL },
>> { "Flags", "as", chrc_get_flags, NULL, NULL },
>> + { "WriteAcquired", "b", chrc_get_write_acquired, NULL,
>> + chrc_write_acquired_exists },
>> { }
>> };
>>
>> @@ -1369,6 +1418,105 @@ static DBusMessage *chrc_write_value(DBusConnection *conn, DBusMessage *msg,
>> return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
>> }
>>
>> +static int parse_options(DBusMessageIter *iter, struct chrc *chrc)
>> +{
>> + DBusMessageIter dict;
>> +
>> + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
>> + return -EINVAL;
>> +
>> + dbus_message_iter_recurse(iter, &dict);
>> +
>> + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
>> + const char *key;
>> + DBusMessageIter value, entry;
>> + int var;
>> +
>> + dbus_message_iter_recurse(&dict, &entry);
>> + dbus_message_iter_get_basic(&entry, &key);
>> +
>> + dbus_message_iter_next(&entry);
>> + dbus_message_iter_recurse(&entry, &value);
>> +
>> + var = dbus_message_iter_get_arg_type(&value);
>> + if (strcasecmp(key, "Device") == 0) {
>> + if (var != DBUS_TYPE_OBJECT_PATH)
>> + return -EINVAL;
>> + } else if (strcasecmp(key, "MTU") == 0) {
>> + if (var != DBUS_TYPE_UINT16)
>> + return -EINVAL;
>> + dbus_message_iter_get_basic(&value, &chrc->mtu);
>> + }
>> +
>> + dbus_message_iter_next(&dict);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static DBusMessage *chrc_create_pipe(struct chrc *chrc, DBusMessage *msg)
>> +{
>> + int pipefd[2];
>> + struct io *io;
>> + bool dir;
>> + DBusMessage *reply;
>> +
>> + if (pipe2(pipefd, O_DIRECT | O_NONBLOCK | O_CLOEXEC) < 0)
>> + return g_dbus_create_error(msg, "org.bluez.Error.Failed", "%s",
>> + strerror(errno));
>> +
>> + dir = dbus_message_has_member(msg, "AcquireWrite");
>> +
>> + io = pipe_io_new(pipefd[!dir], chrc);
>> + if (!io) {
>> + close(pipefd[0]);
>> + close(pipefd[1]);
>> + return g_dbus_create_error(msg, "org.bluez.Error.Failed", "%s",
>> + strerror(errno));
>> + }
>> +
>> + reply = g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD, &pipefd[dir],
>> + DBUS_TYPE_UINT16, &chrc->mtu,
>> + DBUS_TYPE_INVALID);
>> +
>> + close(pipefd[dir]);
>> +
>> + chrc->write_io = io;
>> +
>> + rl_printf("[" COLORED_CHG "] Attribute %s Write pipe acquired\n",
>> + chrc->path);
>> +
>> + return reply;
>> +}
>> +
>> +static DBusMessage *chrc_acquire_write(DBusConnection *conn, DBusMessage *msg,
>> + void *user_data)
>> +{
>> + struct chrc *chrc = user_data;
>> + DBusMessageIter iter;
>> + DBusMessage *reply;
>> +
>> + dbus_message_iter_init(msg, &iter);
>> +
>> + if (chrc->write_io)
>> + return g_dbus_create_error(msg,
>> + "org.bluez.Error.NotPermitted",
>> + NULL);
>> +
>> + if (parse_options(&iter, chrc))
>> + return g_dbus_create_error(msg,
>> + "org.bluez.Error.InvalidArguments",
>> + NULL);
>> +
>> + reply = chrc_create_pipe(chrc, msg);
>> +
>> + if (chrc->write_io)
>> + g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
>> + "WriteAcquired");
>> +
>> + return reply;
>> +}
>> +
>> static DBusMessage *chrc_start_notify(DBusConnection *conn, DBusMessage *msg,
>> void *user_data)
>> {
>> @@ -1420,6 +1568,8 @@ static const GDBusMethodTable chrc_methods[] = {
>> { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
>> { "options", "a{sv}" }),
>> NULL, chrc_write_value) },
>> + { GDBUS_METHOD("AcquireWrite", GDBUS_ARGS({ "options", "a{sv}" }),
>> + NULL, chrc_acquire_write) },
>> { GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chrc_start_notify) },
>> { GDBUS_METHOD("StopNotify", NULL, NULL, chrc_stop_notify) },
>> { GDBUS_METHOD("Confirm", NULL, NULL, chrc_confirm) },
>> --
>> 2.13.5
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
>> the body of a message to [email protected]
>> More majordomo info at http://vger.kernel.org/majordomo-info.html



--
Luiz Augusto von Dentz

2017-09-19 04:14:10

by Yunhan Wang

[permalink] [raw]
Subject: Re: [PATCH BlueZ 07/10] client: Implement AcquireWrite for server

Hi, Luiz

On Mon, Sep 18, 2017 at 5:39 AM, Luiz Augusto von Dentz
<[email protected]> wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> This enables IO via file descriptors using AcquireWrite if server
> implements it.
> ---
> client/gatt.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 158 insertions(+), 8 deletions(-)
>
> diff --git a/client/gatt.c b/client/gatt.c
> index ab74aa695..6401fbd1a 100644
> --- a/client/gatt.c
> +++ b/client/gatt.c
> @@ -32,6 +32,7 @@
> #include <stdbool.h>
> #include <sys/uio.h>
> #include <wordexp.h>
> +#include <fcntl.h>
>
> #include <readline/readline.h>
> #include <readline/history.h>
> @@ -73,6 +74,8 @@ struct chrc {
> GList *descs;
> int value_len;
> uint8_t *value;
> + uint16_t mtu;
> + struct io *write_io;
> };
>
> struct service {
> @@ -683,19 +686,25 @@ void gatt_write_attribute(GDBusProxy *proxy, const char *arg)
>
> static bool pipe_read(struct io *io, void *user_data)
> {
> + struct chrc *chrc = user_data;
> uint8_t buf[512];
> int fd = io_get_fd(io);
> ssize_t bytes_read;
>
> - if (io != notify_io.io)
> + if (io != notify_io.io && !chrc)
> 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_io.proxy));
> + if (chrc)
> + rl_printf("[" COLORED_CHG "] Attribute %s written:\n",
> + chrc->path);
> + else
> + rl_printf("[" COLORED_CHG "] %s Notification:\n",
> + g_dbus_proxy_get_path(notify_io.proxy));
> +
> rl_hexdump(buf, bytes_read);
>
> return true;
> @@ -703,6 +712,17 @@ static bool pipe_read(struct io *io, void *user_data)
>
> static bool pipe_hup(struct io *io, void *user_data)
> {
> + struct chrc *chrc = user_data;
> +
> + if (chrc) {
> + rl_printf("Attribute %s Write pipe closed\n", chrc->path);
> + if (chrc->write_io) {
> + io_destroy(chrc->write_io);
> + chrc->write_io = NULL;
> + }
> + return false;
> + }
> +
> rl_printf("%s closed\n", io == notify_io.io ? "Notify" : "Write");
>
> if (io == notify_io.io)
> @@ -713,7 +733,7 @@ static bool pipe_hup(struct io *io, void *user_data)
> return false;
> }
>
> -static struct io *pipe_io_new(int fd)
> +static struct io *pipe_io_new(int fd, void *user_data)
> {
> struct io *io;
>
> @@ -721,9 +741,9 @@ static struct io *pipe_io_new(int fd)
>
> io_set_close_on_destroy(io, true);
>
> - io_set_read_handler(io, pipe_read, NULL, NULL);
> + io_set_read_handler(io, pipe_read, user_data, NULL);
>
> - io_set_disconnect_handler(io, pipe_hup, NULL, NULL);
> + io_set_disconnect_handler(io, pipe_hup, user_data, NULL);
>
> return io;
> }
> @@ -754,7 +774,7 @@ static void acquire_write_reply(DBusMessage *message, void *user_data)
>
> rl_printf("AcquireWrite success: fd %d MTU %u\n", fd, write_io.mtu);
>
> - write_io.io = pipe_io_new(fd);
> + write_io.io = pipe_io_new(fd, NULL);
> }
>
> void gatt_acquire_write(GDBusProxy *proxy, const char *arg)
> @@ -817,7 +837,7 @@ static void acquire_notify_reply(DBusMessage *message, void *user_data)
>
> rl_printf("AcquireNotify success: fd %d MTU %u\n", fd, notify_io.mtu);

What is the difference about MTU obtained from acquire_write and
acquire_notify? Same?

>
> - notify_io.io = pipe_io_new(fd);
> + notify_io.io = pipe_io_new(fd, NULL);
> }
>
> void gatt_acquire_notify(GDBusProxy *proxy, const char *arg)
> @@ -1302,12 +1322,41 @@ static gboolean chrc_get_flags(const GDBusPropertyTable *property,
> return TRUE;
> }
>
> +static gboolean chrc_get_write_acquired(const GDBusPropertyTable *property,
> + DBusMessageIter *iter, void *data)
> +{
> + struct chrc *chrc = data;
> + dbus_bool_t value;
> +
> + value = chrc->write_io ? TRUE : FALSE;
> +
> + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
> +
> + return TRUE;
> +}
> +
> +static gboolean chrc_write_acquired_exists(const GDBusPropertyTable *property,
> + void *data)
> +{
> + struct chrc *chrc = data;
> + int i;
> +
> + for (i = 0; chrc->flags[i]; i++) {
> + if (!strcmp("write-without-response", chrc->flags[i]))

Why acquire_write only works with write-without-response? If my
application is using characteristic with Write, how can I reuse this
acquire_write?


> + return TRUE;
> + }
> +
> + return FALSE;
> +}
> +
> static const GDBusPropertyTable chrc_properties[] = {
> { "UUID", "s", chrc_get_uuid, NULL, NULL },
> { "Service", "o", chrc_get_service, NULL, NULL },
> { "Value", "ay", chrc_get_value, NULL, NULL },
> { "Notifying", "b", chrc_get_notifying, NULL, NULL },
> { "Flags", "as", chrc_get_flags, NULL, NULL },
> + { "WriteAcquired", "b", chrc_get_write_acquired, NULL,
> + chrc_write_acquired_exists },
> { }
> };
>
> @@ -1369,6 +1418,105 @@ static DBusMessage *chrc_write_value(DBusConnection *conn, DBusMessage *msg,
> return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
> }
>
> +static int parse_options(DBusMessageIter *iter, struct chrc *chrc)
> +{
> + DBusMessageIter dict;
> +
> + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
> + return -EINVAL;
> +
> + dbus_message_iter_recurse(iter, &dict);
> +
> + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
> + const char *key;
> + DBusMessageIter value, entry;
> + int var;
> +
> + dbus_message_iter_recurse(&dict, &entry);
> + dbus_message_iter_get_basic(&entry, &key);
> +
> + dbus_message_iter_next(&entry);
> + dbus_message_iter_recurse(&entry, &value);
> +
> + var = dbus_message_iter_get_arg_type(&value);
> + if (strcasecmp(key, "Device") == 0) {
> + if (var != DBUS_TYPE_OBJECT_PATH)
> + return -EINVAL;
> + } else if (strcasecmp(key, "MTU") == 0) {
> + if (var != DBUS_TYPE_UINT16)
> + return -EINVAL;
> + dbus_message_iter_get_basic(&value, &chrc->mtu);
> + }
> +
> + dbus_message_iter_next(&dict);
> + }
> +
> + return 0;
> +}
> +
> +static DBusMessage *chrc_create_pipe(struct chrc *chrc, DBusMessage *msg)
> +{
> + int pipefd[2];
> + struct io *io;
> + bool dir;
> + DBusMessage *reply;
> +
> + if (pipe2(pipefd, O_DIRECT | O_NONBLOCK | O_CLOEXEC) < 0)
> + return g_dbus_create_error(msg, "org.bluez.Error.Failed", "%s",
> + strerror(errno));
> +
> + dir = dbus_message_has_member(msg, "AcquireWrite");
> +
> + io = pipe_io_new(pipefd[!dir], chrc);
> + if (!io) {
> + close(pipefd[0]);
> + close(pipefd[1]);
> + return g_dbus_create_error(msg, "org.bluez.Error.Failed", "%s",
> + strerror(errno));
> + }
> +
> + reply = g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD, &pipefd[dir],
> + DBUS_TYPE_UINT16, &chrc->mtu,
> + DBUS_TYPE_INVALID);
> +
> + close(pipefd[dir]);
> +
> + chrc->write_io = io;
> +
> + rl_printf("[" COLORED_CHG "] Attribute %s Write pipe acquired\n",
> + chrc->path);
> +
> + return reply;
> +}
> +
> +static DBusMessage *chrc_acquire_write(DBusConnection *conn, DBusMessage *msg,
> + void *user_data)
> +{
> + struct chrc *chrc = user_data;
> + DBusMessageIter iter;
> + DBusMessage *reply;
> +
> + dbus_message_iter_init(msg, &iter);
> +
> + if (chrc->write_io)
> + return g_dbus_create_error(msg,
> + "org.bluez.Error.NotPermitted",
> + NULL);
> +
> + if (parse_options(&iter, chrc))
> + return g_dbus_create_error(msg,
> + "org.bluez.Error.InvalidArguments",
> + NULL);
> +
> + reply = chrc_create_pipe(chrc, msg);
> +
> + if (chrc->write_io)
> + g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
> + "WriteAcquired");
> +
> + return reply;
> +}
> +
> static DBusMessage *chrc_start_notify(DBusConnection *conn, DBusMessage *msg,
> void *user_data)
> {
> @@ -1420,6 +1568,8 @@ static const GDBusMethodTable chrc_methods[] = {
> { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
> { "options", "a{sv}" }),
> NULL, chrc_write_value) },
> + { GDBUS_METHOD("AcquireWrite", GDBUS_ARGS({ "options", "a{sv}" }),
> + NULL, chrc_acquire_write) },
> { GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chrc_start_notify) },
> { GDBUS_METHOD("StopNotify", NULL, NULL, chrc_stop_notify) },
> { GDBUS_METHOD("Confirm", NULL, NULL, chrc_confirm) },
> --
> 2.13.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2017-09-18 12:40:00

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 09/10] client: Implement AcquireNotify for server

From: Luiz Augusto von Dentz <[email protected]>

This enables IO via file descriptors using AcquireNotify if server
implements it.
---
client/gatt.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 66 insertions(+), 3 deletions(-)

diff --git a/client/gatt.c b/client/gatt.c
index 6401fbd1a..1ca1159ad 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -76,6 +76,7 @@ struct chrc {
uint8_t *value;
uint16_t mtu;
struct io *write_io;
+ struct io *notify_io;
};

struct service {
@@ -1349,6 +1350,33 @@ static gboolean chrc_write_acquired_exists(const GDBusPropertyTable *property,
return FALSE;
}

+static gboolean chrc_get_notify_acquired(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct chrc *chrc = data;
+ dbus_bool_t value;
+
+ value = chrc->notify_io ? TRUE : FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+ return TRUE;
+}
+
+static gboolean chrc_notify_acquired_exists(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct chrc *chrc = data;
+ int i;
+
+ for (i = 0; chrc->flags[i]; i++) {
+ if (!strcmp("notify", chrc->flags[i]))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static const GDBusPropertyTable chrc_properties[] = {
{ "UUID", "s", chrc_get_uuid, NULL, NULL },
{ "Service", "o", chrc_get_service, NULL, NULL },
@@ -1357,6 +1385,8 @@ static const GDBusPropertyTable chrc_properties[] = {
{ "Flags", "as", chrc_get_flags, NULL, NULL },
{ "WriteAcquired", "b", chrc_get_write_acquired, NULL,
chrc_write_acquired_exists },
+ { "NotifyAcquired", "b", chrc_get_notify_acquired, NULL,
+ chrc_notify_acquired_exists },
{ }
};

@@ -1481,10 +1511,13 @@ static DBusMessage *chrc_create_pipe(struct chrc *chrc, DBusMessage *msg)

close(pipefd[dir]);

- chrc->write_io = io;
+ if (dir)
+ chrc->write_io = io;
+ else
+ chrc->notify_io = io;

- rl_printf("[" COLORED_CHG "] Attribute %s Write pipe acquired\n",
- chrc->path);
+ rl_printf("[" COLORED_CHG "] Attribute %s %s pipe acquired\n",
+ chrc->path, dir ? "Write" : "Notify");

return reply;
}
@@ -1517,6 +1550,34 @@ static DBusMessage *chrc_acquire_write(DBusConnection *conn, DBusMessage *msg,
return reply;
}

+static DBusMessage *chrc_acquire_notify(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct chrc *chrc = user_data;
+ DBusMessageIter iter;
+ DBusMessage *reply;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (chrc->notify_io)
+ return g_dbus_create_error(msg,
+ "org.bluez.Error.NotPermitted",
+ NULL);
+
+ if (parse_options(&iter, chrc))
+ return g_dbus_create_error(msg,
+ "org.bluez.Error.InvalidArguments",
+ NULL);
+
+ reply = chrc_create_pipe(chrc, msg);
+
+ if (chrc->notify_io)
+ g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
+ "NotifyAcquired");
+
+ return reply;
+}
+
static DBusMessage *chrc_start_notify(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
@@ -1570,6 +1631,8 @@ static const GDBusMethodTable chrc_methods[] = {
NULL, chrc_write_value) },
{ GDBUS_METHOD("AcquireWrite", GDBUS_ARGS({ "options", "a{sv}" }),
NULL, chrc_acquire_write) },
+ { GDBUS_METHOD("AcquireNotify", GDBUS_ARGS({ "options", "a{sv}" }),
+ NULL, chrc_acquire_notify) },
{ GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chrc_start_notify) },
{ GDBUS_METHOD("StopNotify", NULL, NULL, chrc_stop_notify) },
{ GDBUS_METHOD("Confirm", NULL, NULL, chrc_confirm) },
--
2.13.5


2017-09-18 12:40:01

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 10/10] gatt: Update signature of AcquireWrite and AcquireNotify

From: Luiz Augusto von Dentz <[email protected]>

It should now contain an argument for the options even though there
are not options defined for clients.
---
client/gatt.c | 18 ++++++++++++++++--
mesh/gatt.c | 23 ++++++++++++++++++++---
src/gatt-client.c | 6 ++++--
3 files changed, 40 insertions(+), 7 deletions(-)

diff --git a/client/gatt.c b/client/gatt.c
index 1ca1159ad..93aec92e7 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -778,6 +778,20 @@ static void acquire_write_reply(DBusMessage *message, void *user_data)
write_io.io = pipe_io_new(fd, NULL);
}

+static void acquire_setup(DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter dict;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
void gatt_acquire_write(GDBusProxy *proxy, const char *arg)
{
const char *iface;
@@ -789,7 +803,7 @@ void gatt_acquire_write(GDBusProxy *proxy, const char *arg)
return;
}

- if (g_dbus_proxy_method_call(proxy, "AcquireWrite", NULL,
+ if (g_dbus_proxy_method_call(proxy, "AcquireWrite", acquire_setup,
acquire_write_reply, NULL, NULL) == FALSE) {
rl_printf("Failed to AcquireWrite\n");
return;
@@ -852,7 +866,7 @@ void gatt_acquire_notify(GDBusProxy *proxy, const char *arg)
return;
}

- if (g_dbus_proxy_method_call(proxy, "AcquireNotify", NULL,
+ if (g_dbus_proxy_method_call(proxy, "AcquireNotify", acquire_setup,
acquire_notify_reply, NULL, NULL) == FALSE) {
rl_printf("Failed to AcquireNotify\n");
return;
diff --git a/mesh/gatt.c b/mesh/gatt.c
index f9615b3e2..001eb17a8 100644
--- a/mesh/gatt.c
+++ b/mesh/gatt.c
@@ -386,6 +386,20 @@ static void acquire_write_reply(DBusMessage *message, void *user_data)
pipe_write(write_io, data);
}

+static void acquire_setup(DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter dict;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
bool mesh_gatt_write(GDBusProxy *proxy, uint8_t *buf, uint16_t len,
GDBusReturnFunction cb, void *user_data)
{
@@ -424,8 +438,9 @@ bool mesh_gatt_write(GDBusProxy *proxy, uint8_t *buf, uint16_t len,
return pipe_write(write_io, data);

if (g_dbus_proxy_get_property(proxy, "WriteAcquired", &iter)) {
- if (g_dbus_proxy_method_call(proxy, "AcquireWrite", NULL,
- acquire_write_reply, data, NULL) == FALSE) {
+ if (g_dbus_proxy_method_call(proxy, "AcquireWrite",
+ acquire_setup, acquire_write_reply,
+ data, NULL) == FALSE) {
rl_printf("Failed to AcquireWrite\n");
write_data_free(data);
return false;
@@ -573,6 +588,7 @@ bool mesh_gatt_notify(GDBusProxy *proxy, bool enable, GDBusReturnFunction cb,
struct notify_data *data;
DBusMessageIter iter;
const char *method;
+ GDBusSetupFunction setup = NULL;

data = g_new0(struct notify_data, 1);
data->proxy = proxy;
@@ -584,6 +600,7 @@ bool mesh_gatt_notify(GDBusProxy *proxy, bool enable, GDBusReturnFunction cb,
if (g_dbus_proxy_get_property(proxy, "NotifyAcquired", &iter)) {
method = "AcquireNotify";
cb = acquire_notify_reply;
+ setup = acquire_setup;
} else {
method = "StartNotify";
cb = notify_reply;
@@ -600,7 +617,7 @@ bool mesh_gatt_notify(GDBusProxy *proxy, bool enable, GDBusReturnFunction cb,
}
}

- if (g_dbus_proxy_method_call(proxy, method, NULL, cb,
+ if (g_dbus_proxy_method_call(proxy, method, setup, cb,
data, NULL) == FALSE) {
rl_printf("Failed to %s\n", method);
return false;
diff --git a/src/gatt-client.c b/src/gatt-client.c
index d523b883a..32b3a8783 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -1624,11 +1624,13 @@ static const GDBusMethodTable characteristic_methods[] = {
{ "options", "a{sv}" }),
NULL,
characteristic_write_value) },
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("AcquireWrite", NULL,
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("AcquireWrite",
+ GDBUS_ARGS({ "options", "a{sv}" }),
GDBUS_ARGS({ "fd", "h" },
{ "mtu", "q" }),
characteristic_acquire_write) },
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("AcquireNotify", NULL,
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("AcquireNotify",
+ GDBUS_ARGS({ "options", "a{sv}" }),
GDBUS_ARGS({ "fd", "h" },
{ "mtu", "q" }),
characteristic_acquire_notify) },
--
2.13.5


2017-09-18 12:39:58

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 07/10] client: Implement AcquireWrite for server

From: Luiz Augusto von Dentz <[email protected]>

This enables IO via file descriptors using AcquireWrite if server
implements it.
---
client/gatt.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 158 insertions(+), 8 deletions(-)

diff --git a/client/gatt.c b/client/gatt.c
index ab74aa695..6401fbd1a 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -32,6 +32,7 @@
#include <stdbool.h>
#include <sys/uio.h>
#include <wordexp.h>
+#include <fcntl.h>

#include <readline/readline.h>
#include <readline/history.h>
@@ -73,6 +74,8 @@ struct chrc {
GList *descs;
int value_len;
uint8_t *value;
+ uint16_t mtu;
+ struct io *write_io;
};

struct service {
@@ -683,19 +686,25 @@ void gatt_write_attribute(GDBusProxy *proxy, const char *arg)

static bool pipe_read(struct io *io, void *user_data)
{
+ struct chrc *chrc = user_data;
uint8_t buf[512];
int fd = io_get_fd(io);
ssize_t bytes_read;

- if (io != notify_io.io)
+ if (io != notify_io.io && !chrc)
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_io.proxy));
+ if (chrc)
+ rl_printf("[" COLORED_CHG "] Attribute %s written:\n",
+ chrc->path);
+ else
+ rl_printf("[" COLORED_CHG "] %s Notification:\n",
+ g_dbus_proxy_get_path(notify_io.proxy));
+
rl_hexdump(buf, bytes_read);

return true;
@@ -703,6 +712,17 @@ static bool pipe_read(struct io *io, void *user_data)

static bool pipe_hup(struct io *io, void *user_data)
{
+ struct chrc *chrc = user_data;
+
+ if (chrc) {
+ rl_printf("Attribute %s Write pipe closed\n", chrc->path);
+ if (chrc->write_io) {
+ io_destroy(chrc->write_io);
+ chrc->write_io = NULL;
+ }
+ return false;
+ }
+
rl_printf("%s closed\n", io == notify_io.io ? "Notify" : "Write");

if (io == notify_io.io)
@@ -713,7 +733,7 @@ static bool pipe_hup(struct io *io, void *user_data)
return false;
}

-static struct io *pipe_io_new(int fd)
+static struct io *pipe_io_new(int fd, void *user_data)
{
struct io *io;

@@ -721,9 +741,9 @@ static struct io *pipe_io_new(int fd)

io_set_close_on_destroy(io, true);

- io_set_read_handler(io, pipe_read, NULL, NULL);
+ io_set_read_handler(io, pipe_read, user_data, NULL);

- io_set_disconnect_handler(io, pipe_hup, NULL, NULL);
+ io_set_disconnect_handler(io, pipe_hup, user_data, NULL);

return io;
}
@@ -754,7 +774,7 @@ static void acquire_write_reply(DBusMessage *message, void *user_data)

rl_printf("AcquireWrite success: fd %d MTU %u\n", fd, write_io.mtu);

- write_io.io = pipe_io_new(fd);
+ write_io.io = pipe_io_new(fd, NULL);
}

void gatt_acquire_write(GDBusProxy *proxy, const char *arg)
@@ -817,7 +837,7 @@ static void acquire_notify_reply(DBusMessage *message, void *user_data)

rl_printf("AcquireNotify success: fd %d MTU %u\n", fd, notify_io.mtu);

- notify_io.io = pipe_io_new(fd);
+ notify_io.io = pipe_io_new(fd, NULL);
}

void gatt_acquire_notify(GDBusProxy *proxy, const char *arg)
@@ -1302,12 +1322,41 @@ static gboolean chrc_get_flags(const GDBusPropertyTable *property,
return TRUE;
}

+static gboolean chrc_get_write_acquired(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct chrc *chrc = data;
+ dbus_bool_t value;
+
+ value = chrc->write_io ? TRUE : FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+ return TRUE;
+}
+
+static gboolean chrc_write_acquired_exists(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct chrc *chrc = data;
+ int i;
+
+ for (i = 0; chrc->flags[i]; i++) {
+ if (!strcmp("write-without-response", chrc->flags[i]))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static const GDBusPropertyTable chrc_properties[] = {
{ "UUID", "s", chrc_get_uuid, NULL, NULL },
{ "Service", "o", chrc_get_service, NULL, NULL },
{ "Value", "ay", chrc_get_value, NULL, NULL },
{ "Notifying", "b", chrc_get_notifying, NULL, NULL },
{ "Flags", "as", chrc_get_flags, NULL, NULL },
+ { "WriteAcquired", "b", chrc_get_write_acquired, NULL,
+ chrc_write_acquired_exists },
{ }
};

@@ -1369,6 +1418,105 @@ static DBusMessage *chrc_write_value(DBusConnection *conn, DBusMessage *msg,
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}

+static int parse_options(DBusMessageIter *iter, struct chrc *chrc)
+{
+ DBusMessageIter dict;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(iter, &dict);
+
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter value, entry;
+ int var;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ var = dbus_message_iter_get_arg_type(&value);
+ if (strcasecmp(key, "Device") == 0) {
+ if (var != DBUS_TYPE_OBJECT_PATH)
+ return -EINVAL;
+ } else if (strcasecmp(key, "MTU") == 0) {
+ if (var != DBUS_TYPE_UINT16)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&value, &chrc->mtu);
+ }
+
+ dbus_message_iter_next(&dict);
+ }
+
+ return 0;
+}
+
+static DBusMessage *chrc_create_pipe(struct chrc *chrc, DBusMessage *msg)
+{
+ int pipefd[2];
+ struct io *io;
+ bool dir;
+ DBusMessage *reply;
+
+ if (pipe2(pipefd, O_DIRECT | O_NONBLOCK | O_CLOEXEC) < 0)
+ return g_dbus_create_error(msg, "org.bluez.Error.Failed", "%s",
+ strerror(errno));
+
+ dir = dbus_message_has_member(msg, "AcquireWrite");
+
+ io = pipe_io_new(pipefd[!dir], chrc);
+ if (!io) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return g_dbus_create_error(msg, "org.bluez.Error.Failed", "%s",
+ strerror(errno));
+ }
+
+ reply = g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD, &pipefd[dir],
+ DBUS_TYPE_UINT16, &chrc->mtu,
+ DBUS_TYPE_INVALID);
+
+ close(pipefd[dir]);
+
+ chrc->write_io = io;
+
+ rl_printf("[" COLORED_CHG "] Attribute %s Write pipe acquired\n",
+ chrc->path);
+
+ return reply;
+}
+
+static DBusMessage *chrc_acquire_write(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct chrc *chrc = user_data;
+ DBusMessageIter iter;
+ DBusMessage *reply;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (chrc->write_io)
+ return g_dbus_create_error(msg,
+ "org.bluez.Error.NotPermitted",
+ NULL);
+
+ if (parse_options(&iter, chrc))
+ return g_dbus_create_error(msg,
+ "org.bluez.Error.InvalidArguments",
+ NULL);
+
+ reply = chrc_create_pipe(chrc, msg);
+
+ if (chrc->write_io)
+ g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
+ "WriteAcquired");
+
+ return reply;
+}
+
static DBusMessage *chrc_start_notify(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
@@ -1420,6 +1568,8 @@ static const GDBusMethodTable chrc_methods[] = {
{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
{ "options", "a{sv}" }),
NULL, chrc_write_value) },
+ { GDBUS_METHOD("AcquireWrite", GDBUS_ARGS({ "options", "a{sv}" }),
+ NULL, chrc_acquire_write) },
{ GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chrc_start_notify) },
{ GDBUS_METHOD("StopNotify", NULL, NULL, chrc_stop_notify) },
{ GDBUS_METHOD("Confirm", NULL, NULL, chrc_confirm) },
--
2.13.5


2017-09-18 12:39:59

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 08/10] gatt: Implement AcquireNotify for server

From: Luiz Augusto von Dentz <[email protected]>

This enables IO via file descriptors using AcquireWrite if server
implements it.
---
src/gatt-database.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 111 insertions(+), 5 deletions(-)

diff --git a/src/gatt-database.c b/src/gatt-database.c
index 6a883c96d..3e91120fe 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -24,6 +24,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
+#include <unistd.h>

#include "lib/bluetooth.h"
#include "lib/sdp.h"
@@ -118,7 +119,9 @@ struct external_chrc {
uint8_t props;
uint8_t ext_props;
uint32_t perm;
+ uint16_t mtu;
struct io *write_io;
+ struct io *notify_io;
struct gatt_db_attribute *attrib;
struct gatt_db_attribute *ccc;
struct queue *pending_reads;
@@ -152,7 +155,8 @@ struct device_state {
struct queue *ccc_states;
};

-typedef uint8_t (*btd_gatt_database_ccc_write_t) (uint16_t value,
+typedef uint8_t (*btd_gatt_database_ccc_write_t) (struct bt_att *att,
+ uint16_t value,
void *user_data);
typedef void (*btd_gatt_database_destroy_t) (void *data);

@@ -328,6 +332,7 @@ static void chrc_free(void *data)
struct external_chrc *chrc = data;

io_destroy(chrc->write_io);
+ io_destroy(chrc->notify_io);

queue_destroy(chrc->pending_reads, cancel_pending_read);
queue_destroy(chrc->pending_writes, cancel_pending_write);
@@ -792,7 +797,8 @@ static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
goto done;

if (ccc_cb->callback)
- ecode = ccc_cb->callback(get_le16(value), ccc_cb->user_data);
+ ecode = ccc_cb->callback(att, get_le16(value),
+ ccc_cb->user_data);

if (!ecode) {
ccc->value[0] = value[0];
@@ -1801,12 +1807,34 @@ static bool pipe_hup(struct io *io, void *user_data)

if (io == chrc->write_io)
chrc->write_io = NULL;
+ else
+ chrc->notify_io = NULL;

io_destroy(io);

return false;
}

+static bool pipe_io_read(struct io *io, void *user_data)
+{
+ struct external_chrc *chrc = user_data;
+ 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;
+
+ send_notification_to_devices(chrc->service->app->database,
+ gatt_db_attribute_get_handle(chrc->attrib),
+ buf, bytes_read,
+ gatt_db_attribute_get_handle(chrc->ccc),
+ false, NULL);
+
+ return true;
+}
+
static struct io *pipe_io_new(int fd, void *user_data)
{
struct io *io;
@@ -1815,6 +1843,8 @@ static struct io *pipe_io_new(int fd, void *user_data)

io_set_close_on_destroy(io, true);

+ io_set_read_handler(io, pipe_io_read, user_data, NULL);
+
io_set_disconnect_handler(io, pipe_hup, user_data, NULL);

return io;
@@ -1915,9 +1945,64 @@ static struct pending_op *acquire_write(struct external_chrc *chrc,
return NULL;
}

-static uint8_t ccc_write_cb(uint16_t value, void *user_data)
+static void acquire_notify_reply(DBusMessage *message, void *user_data)
{
struct external_chrc *chrc = user_data;
+ DBusError err;
+ int fd;
+ uint16_t mtu;
+
+ dbus_error_init(&err);
+
+ if (dbus_set_error_from_message(&err, message) == TRUE) {
+ error("Failed to acquire notify: %s\n", err.name);
+ dbus_error_free(&err);
+ goto retry;
+ }
+
+ if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_UINT16, &mtu,
+ DBUS_TYPE_INVALID) == false)) {
+ error("Invalid AcquirNotify response\n");
+ goto retry;
+ }
+
+ DBG("AcquireNotify success: fd %d MTU %u\n", fd, mtu);
+
+ chrc->notify_io = pipe_io_new(fd, chrc);
+
+ __sync_fetch_and_add(&chrc->ntfy_cnt, 1);
+
+ return;
+
+retry:
+ g_dbus_proxy_method_call(chrc->proxy, "StartNotify", NULL, NULL,
+ NULL, NULL);
+
+ __sync_fetch_and_add(&chrc->ntfy_cnt, 1);
+}
+
+static void acquire_notify_setup(DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter dict;
+ struct external_chrc *chrc = user_data;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ dict_append_entry(&dict, "MTU", DBUS_TYPE_UINT16, &chrc->mtu);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static uint8_t ccc_write_cb(struct bt_att *att, uint16_t value, void *user_data)
+{
+ struct external_chrc *chrc = user_data;
+ DBusMessageIter iter;

DBG("External CCC write received with value: 0x%04x", value);

@@ -1929,6 +2014,12 @@ static uint8_t ccc_write_cb(uint16_t value, void *user_data)
if (__sync_sub_and_fetch(&chrc->ntfy_cnt, 1))
return 0;

+ if (chrc->notify_io) {
+ io_destroy(chrc->notify_io);
+ chrc->notify_io = NULL;
+ return 0;
+ }
+
/*
* Send request to stop notifying. This is best-effort
* operation, so simply ignore the return the value.
@@ -1949,12 +2040,27 @@ static uint8_t ccc_write_cb(uint16_t value, void *user_data)
(value == 2 && !(chrc->props & BT_GATT_CHRC_PROP_INDICATE)))
return BT_ERROR_CCC_IMPROPERLY_CONFIGURED;

+ if (chrc->notify_io) {
+ __sync_fetch_and_add(&chrc->ntfy_cnt, 1);
+ return 0;
+ }
+
+ chrc->mtu = bt_att_get_mtu(att);
+
+ /* Make use of AcquireNotify if supported */
+ if (g_dbus_proxy_get_property(chrc->proxy, "NotifyAcquired", &iter)) {
+ if (g_dbus_proxy_method_call(chrc->proxy, "AcquireNotify",
+ acquire_notify_setup,
+ acquire_notify_reply,
+ chrc, NULL))
+ return 0;
+ }
+
/*
* Always call StartNotify for an incoming enable and ignore the return
* value for now.
*/
- if (g_dbus_proxy_method_call(chrc->proxy,
- "StartNotify", NULL, NULL,
+ if (g_dbus_proxy_method_call(chrc->proxy, "StartNotify", NULL, NULL,
NULL, NULL) == FALSE)
return BT_ATT_ERROR_UNLIKELY;

--
2.13.5


2017-09-18 12:39:55

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 04/10] shared/gatt-server: Add bt_gatt_server_get_mtu

From: Luiz Augusto von Dentz <[email protected]>

This adds bt_gatt_server_get_mtu which can be used to read the current
MTU.
---
src/shared/gatt-db.h | 2 ++
src/shared/gatt-server.c | 8 ++++++++
src/shared/gatt-server.h | 1 +
3 files changed, 11 insertions(+)

diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index f4ec51cff..e2ac645f3 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -265,3 +265,5 @@ bool gatt_db_attribute_write_result(struct gatt_db_attribute *attrib,
unsigned int id, int err);

bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib);
+
+void *gatt_db_attribute_get_user_data(struct gatt_db_attribute *attrib);
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index dc3bb8ee9..a986c5295 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -1503,6 +1503,14 @@ struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
return bt_gatt_server_ref(server);
}

+uint16_t bt_gatt_server_get_mtu(struct bt_gatt_server *server)
+{
+ if (!server || !server->att)
+ return 0;
+
+ return bt_att_get_mtu(server->att);
+}
+
struct bt_gatt_server *bt_gatt_server_ref(struct bt_gatt_server *server)
{
if (!server)
diff --git a/src/shared/gatt-server.h b/src/shared/gatt-server.h
index 0e480e1b3..74a6c721e 100644
--- a/src/shared/gatt-server.h
+++ b/src/shared/gatt-server.h
@@ -27,6 +27,7 @@ struct bt_gatt_server;

struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
struct bt_att *att, uint16_t mtu);
+uint16_t bt_gatt_server_get_mtu(struct bt_gatt_server *server);

struct bt_gatt_server *bt_gatt_server_ref(struct bt_gatt_server *server);
void bt_gatt_server_unref(struct bt_gatt_server *server);
--
2.13.5


2017-09-18 12:39:57

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 06/10] gatt: Implement AcquireWrite for server

From: Luiz Augusto von Dentz <[email protected]>

This enables IO via file descriptors using AcquireWrite if server
implements it.
---
src/gatt-database.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 143 insertions(+), 1 deletion(-)

diff --git a/src/gatt-database.c b/src/gatt-database.c
index 61eed71d6..6a883c96d 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -33,6 +33,7 @@
#include "gdbus/gdbus.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"
+#include "src/shared/io.h"
#include "src/shared/att.h"
#include "src/shared/gatt-db.h"
#include "src/shared/gatt-server.h"
@@ -117,6 +118,7 @@ struct external_chrc {
uint8_t props;
uint8_t ext_props;
uint32_t perm;
+ struct io *write_io;
struct gatt_db_attribute *attrib;
struct gatt_db_attribute *ccc;
struct queue *pending_reads;
@@ -325,6 +327,8 @@ static void chrc_free(void *data)
{
struct external_chrc *chrc = data;

+ io_destroy(chrc->write_io);
+
queue_destroy(chrc->pending_reads, cancel_pending_read);
queue_destroy(chrc->pending_writes, cancel_pending_write);

@@ -1789,6 +1793,128 @@ static struct pending_op *send_write(struct btd_device *device,
return NULL;
}

+static bool pipe_hup(struct io *io, void *user_data)
+{
+ struct external_chrc *chrc = user_data;
+
+ DBG("%p closed\n", io);
+
+ if (io == chrc->write_io)
+ chrc->write_io = NULL;
+
+ io_destroy(io);
+
+ return false;
+}
+
+static struct io *pipe_io_new(int fd, void *user_data)
+{
+ struct io *io;
+
+ io = io_new(fd);
+
+ io_set_close_on_destroy(io, true);
+
+ io_set_disconnect_handler(io, pipe_hup, user_data, NULL);
+
+ return io;
+}
+
+static int pipe_io_send(struct io *io, const void *data, size_t len)
+{
+ struct iovec iov;
+
+ iov.iov_base = (void *) data;
+ iov.iov_len = len;
+
+ return io_send(io, &iov, 1);
+}
+
+static void acquire_write_reply(DBusMessage *message, void *user_data)
+{
+ struct pending_op *op = user_data;
+ struct external_chrc *chrc;
+ DBusError err;
+ int fd;
+ uint16_t mtu;
+
+ chrc = gatt_db_attribute_get_user_data(op->attrib);
+ dbus_error_init(&err);
+
+ if (dbus_set_error_from_message(&err, message) == TRUE) {
+ error("Failed to acquire write: %s\n", err.name);
+ dbus_error_free(&err);
+ goto retry;
+ }
+
+ if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_UINT16, &mtu,
+ DBUS_TYPE_INVALID) == false)) {
+ error("Invalid AcquireWrite response\n");
+ goto retry;
+ }
+
+ DBG("AcquireWrite success: fd %d MTU %u\n", fd, mtu);
+
+ chrc->write_io = pipe_io_new(fd, chrc);
+
+ if (pipe_io_send(chrc->write_io, op->data.iov_base,
+ op->data.iov_len) < 0)
+ goto retry;
+
+ return;
+
+retry:
+ send_write(op->device, op->attrib, chrc->proxy, NULL, op->id,
+ op->data.iov_base, op->data.iov_len, 0);
+}
+
+static void acquire_write_setup(DBusMessageIter *iter, void *user_data)
+{
+ struct pending_op *op = user_data;
+ DBusMessageIter dict;
+ struct bt_gatt_server *server;
+ uint16_t mtu;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ append_options(&dict, op);
+
+ server = btd_device_get_gatt_server(op->device);
+
+ mtu = bt_gatt_server_get_mtu(server);
+
+ dict_append_entry(&dict, "MTU", DBUS_TYPE_UINT16, &mtu);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static struct pending_op *acquire_write(struct external_chrc *chrc,
+ struct btd_device *device,
+ struct gatt_db_attribute *attrib,
+ unsigned int id,
+ const uint8_t *value, size_t len)
+{
+ struct pending_op *op;
+
+ op = pending_write_new(device, NULL, attrib, id, value, len, 0);
+
+ if (g_dbus_proxy_method_call(chrc->proxy, "AcquireWrite",
+ acquire_write_setup,
+ acquire_write_reply,
+ op, pending_op_free))
+ return op;
+
+ pending_op_free(op);
+
+ return NULL;
+}
+
static uint8_t ccc_write_cb(uint16_t value, void *user_data)
{
struct external_chrc *chrc = user_data;
@@ -2090,6 +2216,7 @@ static void chrc_write_cb(struct gatt_db_attribute *attrib,
struct external_chrc *chrc = user_data;
struct btd_device *device;
struct queue *queue;
+ DBusMessageIter iter;

if (chrc->attrib != attrib) {
error("Write callback called with incorrect attribute");
@@ -2102,9 +2229,24 @@ static void chrc_write_cb(struct gatt_db_attribute *attrib,
goto fail;
}

+ if (chrc->write_io) {
+ if (pipe_io_send(chrc->write_io, value, len) < 0) {
+ error("Unable to write: %s", strerror(errno));
+ goto fail;
+ }
+
+ gatt_db_attribute_write_result(attrib, id, 0);
+ return;
+ }
+
if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP))
queue = chrc->pending_writes;
- else
+ else if (g_dbus_proxy_get_property(chrc->proxy, "WriteAcquired",
+ &iter)) {
+ if (acquire_write(chrc, device, attrib, id, value, len))
+ return;
+ queue = NULL;
+ } else
queue = NULL;

if (send_write(device, attrib, chrc->proxy, queue, id, value, len,
--
2.13.5


2017-09-18 12:39:56

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 05/10] shared/gatt-db: Add gatt_db_attribute_get_user_data

From: Luiz Augusto von Dentz <[email protected]>

This adds gatt_db_attribute_get_user_data which can be used to retrieve
the user_data given at registration.
---
src/shared/gatt-db.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index f44cd1a07..2487584f3 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -1887,3 +1887,11 @@ bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib)

return true;
}
+
+void *gatt_db_attribute_get_user_data(struct gatt_db_attribute *attrib)
+{
+ if (!attrib)
+ return NULL;
+
+ return attrib->user_data;
+}
--
2.13.5


2017-09-18 12:39:54

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 03/10] doc/gatt-api: Add server support for AcquireWrite and AcquireNotify

From: Luiz Augusto von Dentz <[email protected]>

This enables servers to use the same mechanism to use packet based IO
using file descriptors bypassing D-Bus.

Note that the application is free to choose any type of medium that can
use file descriptors, thus this is not limited to pipe2 although that is
probably recommended due its simplicity.
---
doc/gatt-api.txt | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
index cdd15f301..6f82c9fc1 100644
--- a/doc/gatt-api.txt
+++ b/doc/gatt-api.txt
@@ -91,12 +91,15 @@ Methods array{byte} ReadValue(dict options)
org.bluez.Error.NotAuthorized
org.bluez.Error.NotSupported

- fd, uint16 AcquireWrite() [experimental] (Client only)
+ fd, uint16 AcquireWrite(dict options) [experimental, optional]

Acquire file descriptor and MTU for writing. Usage of
WriteValue will be locked causing it to return
NotPermitted error.

+ For server the MTU returned shall be equal or smaller
+ than the negotiated MTU.
+
Only works with characteristic that has
WriteAcquired property which relies on
write-without-response Flag.
@@ -111,15 +114,21 @@ Methods array{byte} ReadValue(dict options)
that the file descriptor is closed during
reconnections as the MTU has to be renegotiated.

+ Possible options: "device": Object Device (Server only)
+ "MTU": Exchanged MTU (Server only)
+
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.NotSupported

- fd, uint16 AcquireNotify() [experimental] (Client only)
+ fd, uint16 AcquireNotify(dict options) [experimental, optional]

Acquire file descriptor and MTU for notify. Usage of
StartNotify will be locked causing it to return
NotPermitted error.

+ For server the MTU returned shall be equal or smaller
+ than the negotiated MTU.
+
Only works with characteristic that has NotifyAcquired
which relies on notify Flag and no other client have
called StartNotify.
@@ -140,6 +149,9 @@ Methods array{byte} ReadValue(dict options)
that the file descriptor is closed during
reconnections as the MTU has to be renegotiated.

+ Possible options: "device": Object Device (Server only)
+ "MTU": Exchanged MTU (Server only)
+
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.NotSupported

@@ -191,12 +203,18 @@ Properties string UUID [read-only]
client using AcquireWrite. This properties is ommited
in case 'write-without-response' flag is not set.

+ For server the presence of this property indicates
+ that AcquireWrite is supported.
+
boolean NotifyAcquired [read-only, optional]

True, if this characteristic has been acquired by any
client using AcquireNotify. This properties is ommited
in case 'notify' flag is not set.

+ For server the presence of this property indicates
+ that AcquireNotify is supported.
+
boolean Notifying [read-only, optional]

True, if notifications or indications on this
--
2.13.5


2017-09-18 12:39:53

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 02/10] client: Rework variables for AcquireWrite/AcquireNotify

From: Luiz Augusto von Dentz <[email protected]>

This creates a struct with necessary fields which is easier to reset.
---
client/gatt.c | 80 +++++++++++++++++++++++++++++------------------------------
1 file changed, 39 insertions(+), 41 deletions(-)

diff --git a/client/gatt.c b/client/gatt.c
index 753038303..ab74aa695 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -90,13 +90,14 @@ static GList *descriptors;
static GList *managers;
static GList *uuids;

-static GDBusProxy *write_proxy;
-static struct io *write_io;
-static uint16_t write_mtu;
+struct pipe_io {
+ GDBusProxy *proxy;
+ struct io *io;
+ uint16_t mtu;
+};

-static GDBusProxy *notify_proxy;
-static struct io *notify_io;
-static uint16_t notify_mtu;
+static struct pipe_io write_io;
+static struct pipe_io notify_io;

static void print_service(struct service *service, const char *description)
{
@@ -236,18 +237,14 @@ void gatt_add_characteristic(GDBusProxy *proxy)

static void notify_io_destroy(void)
{
- io_destroy(notify_io);
- notify_io = NULL;
- notify_proxy = NULL;
- notify_mtu = 0;
+ io_destroy(notify_io.io);
+ memset(&notify_io, 0, sizeof(notify_io));
}

static void write_io_destroy(void)
{
- io_destroy(write_io);
- write_io = NULL;
- write_proxy = NULL;
- write_mtu = 0;
+ io_destroy(write_io.io);
+ memset(&write_io, 0, sizeof(write_io));
}

void gatt_remove_characteristic(GDBusProxy *proxy)
@@ -262,9 +259,9 @@ void gatt_remove_characteristic(GDBusProxy *proxy)

print_characteristic(proxy, COLORED_DEL);

- if (write_proxy == proxy)
+ if (write_io.proxy == proxy)
write_io_destroy();
- else if (notify_proxy == proxy)
+ else if (notify_io.proxy == proxy)
notify_io_destroy();
}

@@ -650,9 +647,10 @@ 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_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) {
+ if (proxy == write_io.proxy && (write_io.io && write_io.mtu >= i)) {
+ rl_printf("Attempting to write fd %d\n",
+ io_get_fd(write_io.io));
+ if (io_send(write_io.io, &iov, 1) < 0) {
rl_printf("Failed to write: %s", strerror(errno));
return;
}
@@ -689,7 +687,7 @@ static bool pipe_read(struct io *io, void *user_data)
int fd = io_get_fd(io);
ssize_t bytes_read;

- if (io != notify_io)
+ if (io != notify_io.io)
return true;

bytes_read = read(fd, buf, sizeof(buf));
@@ -697,7 +695,7 @@ static bool pipe_read(struct io *io, void *user_data)
return false;

rl_printf("[" COLORED_CHG "] %s Notification:\n",
- g_dbus_proxy_get_path(notify_proxy));
+ g_dbus_proxy_get_path(notify_io.proxy));
rl_hexdump(buf, bytes_read);

return true;
@@ -705,9 +703,9 @@ static bool pipe_read(struct io *io, void *user_data)

static bool pipe_hup(struct io *io, void *user_data)
{
- rl_printf("%s closed\n", io == notify_io ? "Notify" : "Write");
+ rl_printf("%s closed\n", io == notify_io.io ? "Notify" : "Write");

- if (io == notify_io)
+ if (io == notify_io.io)
notify_io_destroy();
else
write_io_destroy();
@@ -740,23 +738,23 @@ static void acquire_write_reply(DBusMessage *message, void *user_data)
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;
+ write_io.proxy = NULL;
return;
}

- if (write_io)
+ if (write_io.io)
write_io_destroy();

if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
- DBUS_TYPE_UINT16, &write_mtu,
+ DBUS_TYPE_UINT16, &write_io.mtu,
DBUS_TYPE_INVALID) == false)) {
rl_printf("Invalid AcquireWrite response\n");
return;
}

- rl_printf("AcquireWrite success: fd %d MTU %u\n", fd, write_mtu);
+ rl_printf("AcquireWrite success: fd %d MTU %u\n", fd, write_io.mtu);

- write_io = pipe_io_new(fd);
+ write_io.io = pipe_io_new(fd);
}

void gatt_acquire_write(GDBusProxy *proxy, const char *arg)
@@ -776,12 +774,12 @@ void gatt_acquire_write(GDBusProxy *proxy, const char *arg)
return;
}

- write_proxy = proxy;
+ write_io.proxy = proxy;
}

void gatt_release_write(GDBusProxy *proxy, const char *arg)
{
- if (proxy != write_proxy || !write_io) {
+ if (proxy != write_io.proxy || !write_io.io) {
rl_printf("Write not acquired\n");
return;
}
@@ -799,27 +797,27 @@ static void acquire_notify_reply(DBusMessage *message, void *user_data)
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;
+ write_io.proxy = NULL;
return;
}

- if (notify_io) {
- io_destroy(notify_io);
- notify_io = NULL;
+ if (notify_io.io) {
+ io_destroy(notify_io.io);
+ notify_io.io = NULL;
}

- notify_mtu = 0;
+ notify_io.mtu = 0;

if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
- DBUS_TYPE_UINT16, &notify_mtu,
+ DBUS_TYPE_UINT16, &notify_io.mtu,
DBUS_TYPE_INVALID) == false)) {
rl_printf("Invalid AcquireNotify response\n");
return;
}

- rl_printf("AcquireNotify success: fd %d MTU %u\n", fd, notify_mtu);
+ rl_printf("AcquireNotify success: fd %d MTU %u\n", fd, notify_io.mtu);

- notify_io = pipe_io_new(fd);
+ notify_io.io = pipe_io_new(fd);
}

void gatt_acquire_notify(GDBusProxy *proxy, const char *arg)
@@ -839,13 +837,13 @@ void gatt_acquire_notify(GDBusProxy *proxy, const char *arg)
return;
}

- notify_proxy = proxy;
+ notify_io.proxy = proxy;
}

void gatt_release_notify(GDBusProxy *proxy, const char *arg)
{
- if (proxy != notify_proxy || !notify_io) {
- rl_printf("Write not acquired\n");
+ if (proxy != notify_io.proxy || !notify_io.io) {
+ rl_printf("Notify not acquired\n");
return;
}

--
2.13.5


2017-09-18 12:39:52

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 01/10] gatt: Remove useless debug

From: Luiz Augusto von Dentz <[email protected]>

---
src/gatt-client.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/gatt-client.c b/src/gatt-client.c
index 419dadb99..d523b883a 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -1229,7 +1229,7 @@ static DBusMessage *characteristic_acquire_write(DBusConnection *conn,
chrc->write_io = new0(struct pipe_io, 1);

if (!bt_gatt_client_is_ready(gatt)) {
- DBG("GATT not ready, wait until it becomes read");
+ /* GATT not ready, wait until it becomes ready */
if (!chrc->ready_id)
chrc->ready_id = bt_gatt_client_ready_register(gatt,
characteristic_ready,
--
2.13.5