2014-10-06 21:01:40

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ v1 0/5] Introduce shared/gatt-server.

*v1:
- Make gatt-db external to gatt-server, as initially discussed.
- Also implement Read By Group Type request.

Arman Uguray (5):
Revert "plugins/sixaxis: Add a set_leds_sysfs() function"
shared/att: Drop the connection is a request is received while one is
pending.
shared/gatt-server: Introduce shared/gatt-server.
shared/gatt-server: Support Exchange MTU request.
shared/gatt-server: Support Read By Group Type request.

Makefile.am | 1 +
configure.ac | 4 +-
plugins/sixaxis.c | 89 +-----------
src/shared/att.c | 101 ++++++++------
src/shared/gatt-server.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++
src/shared/gatt-server.h | 40 ++++++
6 files changed, 455 insertions(+), 127 deletions(-)
create mode 100644 src/shared/gatt-server.c
create mode 100644 src/shared/gatt-server.h

--
2.1.0.rc2.206.gedb03e5



2014-10-13 20:57:46

by Arman Uguray

[permalink] [raw]
Subject: Re: [PATCH BlueZ v1 4/4] shared/gatt-server: Support Read By Group Type request.

Hi Luiz,


>> + if (queue_isempty(q)) {
>> + ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND;
>> + goto error;
>> + }
>
> This function is quite long, perhaps using queue_foreach be better
> than iterating inline since you destroy the queue at end but it would
> be a problem if one of items fails to be encoded, the funny thing is
> that it is possible to do the same with queue_find and return true to
> interrupt in case of failure but it would be a obvious abuse of the
> API so perhaps queue_foreach_func_t could have a return as well and in
> case it return false we stop.
>

I would have to modify too many places that use queue_foreach by
adding a somewhat suspicious "return true" with no obvious meaning, so
I decided not to do that for this patch set. For v2 I went ahead and
broke the encoding bit into its own helper which I plan to extend in
the future for other read requests. Hopefully this'll make the code
more readable although it doesn't call queue_foreach.

Cheers,
Arman

2014-10-09 08:29:19

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ v1 0/5] Introduce shared/gatt-server.

Hi Arman,

On Wed, Oct 8, 2014 at 6:14 PM, Arman Uguray <[email protected]> wrote:
> On Mon, Oct 6, 2014 at 5:01 PM, Arman Uguray <[email protected]> wrote:
>> *v1:
>> - Make gatt-db external to gatt-server, as initially discussed.
>> - Also implement Read By Group Type request.
>>
>> Arman Uguray (5):
>> Revert "plugins/sixaxis: Add a set_leds_sysfs() function"
>> shared/att: Drop the connection is a request is received while one is
>> pending.
>> shared/gatt-server: Introduce shared/gatt-server.
>> shared/gatt-server: Support Exchange MTU request.
>> shared/gatt-server: Support Read By Group Type request.
>>
>> Makefile.am | 1 +
>> configure.ac | 4 +-
>> plugins/sixaxis.c | 89 +-----------
>> src/shared/att.c | 101 ++++++++------
>> src/shared/gatt-server.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++
>> src/shared/gatt-server.h | 40 ++++++
>> 6 files changed, 455 insertions(+), 127 deletions(-)
>> create mode 100644 src/shared/gatt-server.c
>> create mode 100644 src/shared/gatt-server.h
>>
>> --
>> 2.1.0.rc2.206.gedb03e5
>>
>
> ping.
>
> Also ignore the bit about 'Revert "plugins/sixaxis: Add a
> set_leds_sysfs() function"'. I revert it locally since it gives me
> some dependency issues when compiling for Chromium OS; it's not part
> of the patch set I uploaded.

Apart from the comments of the last patch Im fine with this set.


--
Luiz Augusto von Dentz

2014-10-09 08:24:51

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ v1 4/4] shared/gatt-server: Support Read By Group Type request.

Hi Arman,

On Tue, Oct 7, 2014 at 12:01 AM, Arman Uguray <[email protected]> wrote:
> This patch adds handling for the Read By Group Type request.
> ---
> src/shared/gatt-server.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 169 insertions(+), 1 deletion(-)
>
> diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
> index c7b0873..3ea8c59 100644
> --- a/src/shared/gatt-server.c
> +++ b/src/shared/gatt-server.c
> @@ -45,6 +45,7 @@ struct bt_gatt_server {
> uint16_t mtu;
>
> unsigned int mtu_id;
> + unsigned int read_by_grp_type_id;
>
> bt_gatt_server_debug_func_t debug_callback;
> bt_gatt_server_destroy_func_t debug_destroy;
> @@ -54,6 +55,7 @@ struct bt_gatt_server {
> static void gatt_server_unregister_handlers(struct bt_gatt_server *server)
> {
> bt_att_unregister(server->att, server->mtu_id);
> + bt_att_unregister(server->att, server->read_by_grp_type_id);
> }
>
> static void gatt_server_cleanup(struct bt_gatt_server *server)
> @@ -71,6 +73,157 @@ static void encode_error_rsp(uint8_t opcode, uint16_t handle, uint8_t ecode,
> put_le16(handle, pdu + 1);
> }
>
> +static bool get_uuid_le(const uint8_t *uuid, size_t len, bt_uuid_t *out_uuid)
> +{
> + uint128_t u128;
> +
> + switch (len) {
> + case 2:
> + bt_uuid16_create(out_uuid, get_le16(uuid));
> + return true;
> + case 16:
> + bswap_128(uuid, &u128.data);
> + bt_uuid128_create(out_uuid, u128);
> + return true;
> + default:
> + return false;
> + }
> +
> + return false;
> +}
> +
> +static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
> + uint16_t length, void *user_data)
> +{
> + struct bt_gatt_server *server = user_data;
> + uint16_t start, end;
> + bt_uuid_t type;
> + bt_uuid_t prim, snd;
> + uint16_t mtu = bt_att_get_mtu(server->att);
> + uint8_t rsp_pdu[mtu];
> + uint16_t rsp_len;
> + uint8_t rsp_opcode;
> + uint8_t ecode = 0;
> + uint16_t ehandle = 0;
> + struct queue *q = NULL;
> + int iter = 0;
> + uint8_t data_val_len;
> +
> + if (length != 6 && length != 20) {
> + ecode = BT_ATT_ERROR_INVALID_PDU;
> + goto error;
> + }
> +
> + q = queue_new();
> + if (!q) {
> + ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
> + goto error;
> + }
> +
> + start = get_le16(pdu);
> + end = get_le16(pdu + 2);
> + get_uuid_le(pdu + 4, length - 4, &type);
> +
> + util_debug(server->debug_callback, server->debug_data,
> + "Read By Grp Type - start: 0x%04x end: 0x%04x",
> + start, end);
> +
> + if (!start || !end) {
> + ecode = BT_ATT_ERROR_INVALID_HANDLE;
> + goto error;
> + }
> +
> + ehandle = start;
> +
> + if (start > end) {
> + ecode = BT_ATT_ERROR_INVALID_HANDLE;
> + goto error;
> + }
> +
> + /* GATT defines that only the <<Primary Service>> and
> + * <<Secondary Service>> group types can be used for the
> + * "Read By Group Type" request (Core v4.1, Vol 3, sec 2.5.3). Return an
> + * error if any other group type is given.
> + */
> + bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID);
> + bt_uuid16_create(&snd, GATT_SND_SVC_UUID);
> + if (bt_uuid_cmp(&type, &prim) && bt_uuid_cmp(&type, &snd)) {
> + ecode = BT_ATT_ERROR_UNSUPPORTED_GROUP_TYPE;
> + goto error;
> + }
> +
> + gatt_db_read_by_group_type(server->db, start, end, type, q);
> +
> + if (queue_isempty(q)) {
> + ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND;
> + goto error;
> + }

This function is quite long, perhaps using queue_foreach be better
than iterating inline since you destroy the queue at end but it would
be a problem if one of items fails to be encoded, the funny thing is
that it is possible to do the same with queue_find and return true to
interrupt in case of failure but it would be a obvious abuse of the
API so perhaps queue_foreach_func_t could have a return as well and in
case it return false we stop.

> + rsp_len = 0;
> + while (queue_peek_head(q)) {
> + uint16_t start_handle = PTR_TO_UINT(queue_pop_head(q));
> + uint16_t end_handle;
> + uint8_t *value = NULL;
> + int value_len = 0;
> +
> + /* TODO: Figure out how to check permissions based on security
> + * level on a bt_att, which sits on a generic io.
> + */
> +
> + if (!gatt_db_read(server->db, start_handle, 0, opcode, NULL,
> + &value, &value_len)) {
> + ecode = BT_ATT_ERROR_UNLIKELY;
> + goto error;
> + }
> +
> + if (value_len < 0) {
> + /* This should never happen for primary/secondary
> + * service declarations.
> + */
> + ecode = BT_ATT_ERROR_UNLIKELY;
> + goto error;
> + }
> +
> + /* Use the first attribute to determine the length of each
> + * attribute data unit. Stop the list when a different attribute
> + * value is seen.
> + */
> + if (iter == 0) {
> + data_val_len = value_len;
> + rsp_pdu[0] = data_val_len + 4;
> + iter++;
> + } else if (value_len != data_val_len)
> + break;
> +
> + /* Stop if this unit would surpass the MTU */
> + if (iter + data_val_len + 4 > mtu)
> + break;
> +
> + end_handle = gatt_db_get_end_handle(server->db, start_handle);
> +
> + put_le16(start_handle, rsp_pdu + iter);
> + put_le16(end_handle, rsp_pdu + iter + 2);
> + memcpy(rsp_pdu + iter + 4, value, value_len);
> +
> + iter += data_val_len + 4;
> + }
> +
> + rsp_opcode = BT_ATT_OP_READ_BY_GRP_TYPE_RSP;
> + rsp_len = iter;
> +
> + goto done;
> +
> +error:
> + rsp_opcode = BT_ATT_OP_ERROR_RSP;
> + rsp_len = 4;
> + encode_error_rsp(opcode, ehandle, ecode, rsp_pdu);
> +
> +done:
> + queue_destroy(q, NULL);
> + bt_att_send(server->att, rsp_opcode, rsp_pdu, rsp_len,
> + NULL, NULL, NULL);
> +}
> +
> static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
> uint16_t length, void *user_data)
> {
> @@ -97,18 +250,33 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
> /* Set MTU to be the minimum */
> server->mtu = final_mtu;
> bt_att_set_mtu(server->att, final_mtu);
> +
> + util_debug(server->debug_callback, server->debug_data,
> + "MTU exchange complete, with MTU: %u", final_mtu);
> }
>
> static bool gatt_server_register_att_handlers(struct bt_gatt_server *server)
> {
> - /* EXCHANGE MTU request */
> + /* Exchange MTU */
> server->mtu_id = bt_att_register(server->att, BT_ATT_OP_MTU_REQ,
> exchange_mtu_cb,
> server, NULL);
> if (!server->mtu_id)
> return false;
>
> + /* Read By Group Type */
> + server->read_by_grp_type_id = bt_att_register(server->att,
> + BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
> + read_by_grp_type_cb,
> + server, NULL);
> + if (!server->read_by_grp_type_id)
> + goto fail;
> +
> return true;
> +
> +fail:
> + gatt_server_unregister_handlers(server);
> + return false;
> }
>
> struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
> --
> 2.1.0.rc2.206.gedb03e5
>
> --
> 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

2014-10-09 08:18:23

by Marcin Kraglak

[permalink] [raw]
Subject: Re: [PATCH BlueZ v1 2/4] shared/gatt-server: Introduce shared/gatt-server.

Hi Arman,

On 6 October 2014 23:01, Arman Uguray <[email protected]> wrote:
> This patch introduces bt_gatt_server which will implement the server-side of the
> ATT protocol over a bt_att structure and construct responses based on a gatt_db
> structure.
> ---
> Makefile.am | 1 +
> src/shared/gatt-server.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++
> src/shared/gatt-server.h | 40 +++++++++++++++
> 3 files changed, 166 insertions(+)
> create mode 100644 src/shared/gatt-server.c
> create mode 100644 src/shared/gatt-server.h
>
> diff --git a/Makefile.am b/Makefile.am
> index 1a1a43f..2dfea28 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -114,6 +114,7 @@ shared_sources = src/shared/io.h src/shared/timeout.h \
> src/shared/att.h src/shared/att.c \
> src/shared/gatt-helpers.h src/shared/gatt-helpers.c \
> src/shared/gatt-client.h src/shared/gatt-client.c \
> + src/shared/gatt-server.h src/shared/gatt-server.c \
> src/shared/gatt-db.h src/shared/gatt-db.c \
> src/shared/gap.h src/shared/gap.c
>
> diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
> new file mode 100644
> index 0000000..544e60e
> --- /dev/null
> +++ b/src/shared/gatt-server.c
> @@ -0,0 +1,125 @@
> +/*
> + *
> + * BlueZ - Bluetooth protocol stack for Linux
> + *
> + * Copyright (C) 2014 Google Inc.
> + *
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
> + *
> + */
> +
> +#include "src/shared/att.h"
> +#include "lib/uuid.h"
> +#include "src/shared/queue.h"
> +#include "src/shared/gatt-db.h"
> +#include "src/shared/gatt-server.h"
> +#include "src/shared/gatt-helpers.h"
> +#include "src/shared/att-types.h"
> +#include "src/shared/util.h"
> +
> +#ifndef MAX
> +#define MAX(a, b) ((a) > (b) ? (a) : (b))
> +#endif
> +
> +struct bt_gatt_server {
> + struct gatt_db *db;
> + struct bt_att *att;
> + int ref_count;
> + uint16_t mtu;
> +
> + bt_gatt_server_debug_func_t debug_callback;
> + bt_gatt_server_destroy_func_t debug_destroy;
> + void *debug_data;
> +};
> +
> +static bool gatt_server_register_att_handlers(struct bt_gatt_server *server)
> +{
> + /* TODO */
> + return true;
> +}
> +
> +static void gatt_server_cleanup(struct bt_gatt_server *server)
> +{
> + bt_att_unref(server->att);
> + server->att = NULL;
> +}
> +
> +struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
> + struct bt_att *att, uint16_t mtu)
> +{
> + struct bt_gatt_server *server;
> +
> + if (!att)
> + return NULL;
> +
> + server = new0(struct bt_gatt_server, 1);
> + if (!server)
> + return NULL;
> +
> + server->db = db;
> + server->att = bt_att_ref(att);
> + server->mtu = MAX(mtu, BT_ATT_DEFAULT_LE_MTU);
> +
> + if (!gatt_server_register_att_handlers(server)) {
> + gatt_db_destroy(server->db);
> + bt_att_unref(att);
> + free(server);
> + return NULL;
> + }
> +
> + return bt_gatt_server_ref(server);
> +}
> +
> +struct bt_gatt_server *bt_gatt_server_ref(struct bt_gatt_server *server)
> +{
> + if (!server)
> + return NULL;
> +
> + __sync_fetch_and_add(&server->ref_count, 1);
> +
> + return server;
> +}
> +
> +void bt_gatt_server_unref(struct bt_gatt_server *server)
> +{
> + if (__sync_sub_and_fetch(&server->ref_count, 1))
> + return;
> +
> + if (server->debug_destroy)
> + server->debug_destroy(server->debug_data);
> +
> + gatt_server_cleanup(server);
> +
> + free(server);
> +}
> +
> +bool bt_gatt_server_set_debug(struct bt_gatt_server *server,
> + bt_gatt_server_debug_func_t callback,
> + void *user_data,
> + bt_gatt_server_destroy_func_t destroy)
> +{
> + if (!server)
> + return false;
> +
> + if (server->debug_destroy)
> + server->debug_destroy(server->debug_data);
> +
> + server->debug_callback = callback;
> + server->debug_destroy = destroy;
> + server->debug_data = user_data;
> +
> + return true;
> +}
> diff --git a/src/shared/gatt-server.h b/src/shared/gatt-server.h
> new file mode 100644
> index 0000000..e3c4def
> --- /dev/null
> +++ b/src/shared/gatt-server.h
> @@ -0,0 +1,40 @@
> +/*
> + *
> + * BlueZ - Bluetooth protocol stack for Linux
> + *
> + * Copyright (C) 2014 Google Inc.
> + *
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
> + *
> + */
> +
> +#include <stdint.h>
> +
> +struct bt_gatt_server;
> +
> +struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
> + struct bt_att *att, uint16_t mtu);
> +
> +struct bt_gatt_server *bt_gatt_server_ref(struct bt_gatt_server *server);
> +void bt_gatt_server_unref(struct bt_gatt_server *server);
> +
> +typedef void (*bt_gatt_server_destroy_func_t)(void *user_data);
> +typedef void (*bt_gatt_server_debug_func_t)(const char *str, void *user_data);
> +
> +bool bt_gatt_server_set_debug(struct bt_gatt_server *server,
> + bt_gatt_server_debug_func_t callback,
> + void *user_data,
> + bt_gatt_server_destroy_func_t destroy);
> --
> 2.1.0.rc2.206.gedb03e5
>
> --
> 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

It looks good in context of future usage in Android

BR
Marcin

2014-10-08 15:14:06

by Arman Uguray

[permalink] [raw]
Subject: Re: [PATCH BlueZ v1 0/5] Introduce shared/gatt-server.

On Mon, Oct 6, 2014 at 5:01 PM, Arman Uguray <[email protected]> wrote:
> *v1:
> - Make gatt-db external to gatt-server, as initially discussed.
> - Also implement Read By Group Type request.
>
> Arman Uguray (5):
> Revert "plugins/sixaxis: Add a set_leds_sysfs() function"
> shared/att: Drop the connection is a request is received while one is
> pending.
> shared/gatt-server: Introduce shared/gatt-server.
> shared/gatt-server: Support Exchange MTU request.
> shared/gatt-server: Support Read By Group Type request.
>
> Makefile.am | 1 +
> configure.ac | 4 +-
> plugins/sixaxis.c | 89 +-----------
> src/shared/att.c | 101 ++++++++------
> src/shared/gatt-server.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++
> src/shared/gatt-server.h | 40 ++++++
> 6 files changed, 455 insertions(+), 127 deletions(-)
> create mode 100644 src/shared/gatt-server.c
> create mode 100644 src/shared/gatt-server.h
>
> --
> 2.1.0.rc2.206.gedb03e5
>

ping.

Also ignore the bit about 'Revert "plugins/sixaxis: Add a
set_leds_sysfs() function"'. I revert it locally since it gives me
some dependency issues when compiling for Chromium OS; it's not part
of the patch set I uploaded.

Cheers,
Arman

2014-10-06 21:01:42

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ v1 2/4] shared/gatt-server: Introduce shared/gatt-server.

This patch introduces bt_gatt_server which will implement the server-side of the
ATT protocol over a bt_att structure and construct responses based on a gatt_db
structure.
---
Makefile.am | 1 +
src/shared/gatt-server.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++
src/shared/gatt-server.h | 40 +++++++++++++++
3 files changed, 166 insertions(+)
create mode 100644 src/shared/gatt-server.c
create mode 100644 src/shared/gatt-server.h

diff --git a/Makefile.am b/Makefile.am
index 1a1a43f..2dfea28 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -114,6 +114,7 @@ shared_sources = src/shared/io.h src/shared/timeout.h \
src/shared/att.h src/shared/att.c \
src/shared/gatt-helpers.h src/shared/gatt-helpers.c \
src/shared/gatt-client.h src/shared/gatt-client.c \
+ src/shared/gatt-server.h src/shared/gatt-server.c \
src/shared/gatt-db.h src/shared/gatt-db.c \
src/shared/gap.h src/shared/gap.c

diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
new file mode 100644
index 0000000..544e60e
--- /dev/null
+++ b/src/shared/gatt-server.c
@@ -0,0 +1,125 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Google Inc.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "src/shared/att.h"
+#include "lib/uuid.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-server.h"
+#include "src/shared/gatt-helpers.h"
+#include "src/shared/att-types.h"
+#include "src/shared/util.h"
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+struct bt_gatt_server {
+ struct gatt_db *db;
+ struct bt_att *att;
+ int ref_count;
+ uint16_t mtu;
+
+ bt_gatt_server_debug_func_t debug_callback;
+ bt_gatt_server_destroy_func_t debug_destroy;
+ void *debug_data;
+};
+
+static bool gatt_server_register_att_handlers(struct bt_gatt_server *server)
+{
+ /* TODO */
+ return true;
+}
+
+static void gatt_server_cleanup(struct bt_gatt_server *server)
+{
+ bt_att_unref(server->att);
+ server->att = NULL;
+}
+
+struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
+ struct bt_att *att, uint16_t mtu)
+{
+ struct bt_gatt_server *server;
+
+ if (!att)
+ return NULL;
+
+ server = new0(struct bt_gatt_server, 1);
+ if (!server)
+ return NULL;
+
+ server->db = db;
+ server->att = bt_att_ref(att);
+ server->mtu = MAX(mtu, BT_ATT_DEFAULT_LE_MTU);
+
+ if (!gatt_server_register_att_handlers(server)) {
+ gatt_db_destroy(server->db);
+ bt_att_unref(att);
+ free(server);
+ return NULL;
+ }
+
+ return bt_gatt_server_ref(server);
+}
+
+struct bt_gatt_server *bt_gatt_server_ref(struct bt_gatt_server *server)
+{
+ if (!server)
+ return NULL;
+
+ __sync_fetch_and_add(&server->ref_count, 1);
+
+ return server;
+}
+
+void bt_gatt_server_unref(struct bt_gatt_server *server)
+{
+ if (__sync_sub_and_fetch(&server->ref_count, 1))
+ return;
+
+ if (server->debug_destroy)
+ server->debug_destroy(server->debug_data);
+
+ gatt_server_cleanup(server);
+
+ free(server);
+}
+
+bool bt_gatt_server_set_debug(struct bt_gatt_server *server,
+ bt_gatt_server_debug_func_t callback,
+ void *user_data,
+ bt_gatt_server_destroy_func_t destroy)
+{
+ if (!server)
+ return false;
+
+ if (server->debug_destroy)
+ server->debug_destroy(server->debug_data);
+
+ server->debug_callback = callback;
+ server->debug_destroy = destroy;
+ server->debug_data = user_data;
+
+ return true;
+}
diff --git a/src/shared/gatt-server.h b/src/shared/gatt-server.h
new file mode 100644
index 0000000..e3c4def
--- /dev/null
+++ b/src/shared/gatt-server.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Google Inc.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+
+struct bt_gatt_server;
+
+struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
+ struct bt_att *att, uint16_t mtu);
+
+struct bt_gatt_server *bt_gatt_server_ref(struct bt_gatt_server *server);
+void bt_gatt_server_unref(struct bt_gatt_server *server);
+
+typedef void (*bt_gatt_server_destroy_func_t)(void *user_data);
+typedef void (*bt_gatt_server_debug_func_t)(const char *str, void *user_data);
+
+bool bt_gatt_server_set_debug(struct bt_gatt_server *server,
+ bt_gatt_server_debug_func_t callback,
+ void *user_data,
+ bt_gatt_server_destroy_func_t destroy);
--
2.1.0.rc2.206.gedb03e5


2014-10-06 21:01:44

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ v1 4/4] shared/gatt-server: Support Read By Group Type request.

This patch adds handling for the Read By Group Type request.
---
src/shared/gatt-server.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 169 insertions(+), 1 deletion(-)

diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index c7b0873..3ea8c59 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -45,6 +45,7 @@ struct bt_gatt_server {
uint16_t mtu;

unsigned int mtu_id;
+ unsigned int read_by_grp_type_id;

bt_gatt_server_debug_func_t debug_callback;
bt_gatt_server_destroy_func_t debug_destroy;
@@ -54,6 +55,7 @@ struct bt_gatt_server {
static void gatt_server_unregister_handlers(struct bt_gatt_server *server)
{
bt_att_unregister(server->att, server->mtu_id);
+ bt_att_unregister(server->att, server->read_by_grp_type_id);
}

static void gatt_server_cleanup(struct bt_gatt_server *server)
@@ -71,6 +73,157 @@ static void encode_error_rsp(uint8_t opcode, uint16_t handle, uint8_t ecode,
put_le16(handle, pdu + 1);
}

+static bool get_uuid_le(const uint8_t *uuid, size_t len, bt_uuid_t *out_uuid)
+{
+ uint128_t u128;
+
+ switch (len) {
+ case 2:
+ bt_uuid16_create(out_uuid, get_le16(uuid));
+ return true;
+ case 16:
+ bswap_128(uuid, &u128.data);
+ bt_uuid128_create(out_uuid, u128);
+ return true;
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
+ uint16_t length, void *user_data)
+{
+ struct bt_gatt_server *server = user_data;
+ uint16_t start, end;
+ bt_uuid_t type;
+ bt_uuid_t prim, snd;
+ uint16_t mtu = bt_att_get_mtu(server->att);
+ uint8_t rsp_pdu[mtu];
+ uint16_t rsp_len;
+ uint8_t rsp_opcode;
+ uint8_t ecode = 0;
+ uint16_t ehandle = 0;
+ struct queue *q = NULL;
+ int iter = 0;
+ uint8_t data_val_len;
+
+ if (length != 6 && length != 20) {
+ ecode = BT_ATT_ERROR_INVALID_PDU;
+ goto error;
+ }
+
+ q = queue_new();
+ if (!q) {
+ ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
+ goto error;
+ }
+
+ start = get_le16(pdu);
+ end = get_le16(pdu + 2);
+ get_uuid_le(pdu + 4, length - 4, &type);
+
+ util_debug(server->debug_callback, server->debug_data,
+ "Read By Grp Type - start: 0x%04x end: 0x%04x",
+ start, end);
+
+ if (!start || !end) {
+ ecode = BT_ATT_ERROR_INVALID_HANDLE;
+ goto error;
+ }
+
+ ehandle = start;
+
+ if (start > end) {
+ ecode = BT_ATT_ERROR_INVALID_HANDLE;
+ goto error;
+ }
+
+ /* GATT defines that only the <<Primary Service>> and
+ * <<Secondary Service>> group types can be used for the
+ * "Read By Group Type" request (Core v4.1, Vol 3, sec 2.5.3). Return an
+ * error if any other group type is given.
+ */
+ bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID);
+ bt_uuid16_create(&snd, GATT_SND_SVC_UUID);
+ if (bt_uuid_cmp(&type, &prim) && bt_uuid_cmp(&type, &snd)) {
+ ecode = BT_ATT_ERROR_UNSUPPORTED_GROUP_TYPE;
+ goto error;
+ }
+
+ gatt_db_read_by_group_type(server->db, start, end, type, q);
+
+ if (queue_isempty(q)) {
+ ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND;
+ goto error;
+ }
+
+ rsp_len = 0;
+ while (queue_peek_head(q)) {
+ uint16_t start_handle = PTR_TO_UINT(queue_pop_head(q));
+ uint16_t end_handle;
+ uint8_t *value = NULL;
+ int value_len = 0;
+
+ /* TODO: Figure out how to check permissions based on security
+ * level on a bt_att, which sits on a generic io.
+ */
+
+ if (!gatt_db_read(server->db, start_handle, 0, opcode, NULL,
+ &value, &value_len)) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto error;
+ }
+
+ if (value_len < 0) {
+ /* This should never happen for primary/secondary
+ * service declarations.
+ */
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto error;
+ }
+
+ /* Use the first attribute to determine the length of each
+ * attribute data unit. Stop the list when a different attribute
+ * value is seen.
+ */
+ if (iter == 0) {
+ data_val_len = value_len;
+ rsp_pdu[0] = data_val_len + 4;
+ iter++;
+ } else if (value_len != data_val_len)
+ break;
+
+ /* Stop if this unit would surpass the MTU */
+ if (iter + data_val_len + 4 > mtu)
+ break;
+
+ end_handle = gatt_db_get_end_handle(server->db, start_handle);
+
+ put_le16(start_handle, rsp_pdu + iter);
+ put_le16(end_handle, rsp_pdu + iter + 2);
+ memcpy(rsp_pdu + iter + 4, value, value_len);
+
+ iter += data_val_len + 4;
+ }
+
+ rsp_opcode = BT_ATT_OP_READ_BY_GRP_TYPE_RSP;
+ rsp_len = iter;
+
+ goto done;
+
+error:
+ rsp_opcode = BT_ATT_OP_ERROR_RSP;
+ rsp_len = 4;
+ encode_error_rsp(opcode, ehandle, ecode, rsp_pdu);
+
+done:
+ queue_destroy(q, NULL);
+ bt_att_send(server->att, rsp_opcode, rsp_pdu, rsp_len,
+ NULL, NULL, NULL);
+}
+
static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
@@ -97,18 +250,33 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
/* Set MTU to be the minimum */
server->mtu = final_mtu;
bt_att_set_mtu(server->att, final_mtu);
+
+ util_debug(server->debug_callback, server->debug_data,
+ "MTU exchange complete, with MTU: %u", final_mtu);
}

static bool gatt_server_register_att_handlers(struct bt_gatt_server *server)
{
- /* EXCHANGE MTU request */
+ /* Exchange MTU */
server->mtu_id = bt_att_register(server->att, BT_ATT_OP_MTU_REQ,
exchange_mtu_cb,
server, NULL);
if (!server->mtu_id)
return false;

+ /* Read By Group Type */
+ server->read_by_grp_type_id = bt_att_register(server->att,
+ BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
+ read_by_grp_type_cb,
+ server, NULL);
+ if (!server->read_by_grp_type_id)
+ goto fail;
+
return true;
+
+fail:
+ gatt_server_unregister_handlers(server);
+ return false;
}

struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
--
2.1.0.rc2.206.gedb03e5


2014-10-06 21:01:43

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ v1 3/4] shared/gatt-server: Support Exchange MTU request.

This patch adds handling for the exchange MTU request.
---
src/shared/gatt-server.c | 60 +++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 57 insertions(+), 3 deletions(-)

diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index 544e60e..c7b0873 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -34,29 +34,83 @@
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif

+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
struct bt_gatt_server {
struct gatt_db *db;
struct bt_att *att;
int ref_count;
uint16_t mtu;

+ unsigned int mtu_id;
+
bt_gatt_server_debug_func_t debug_callback;
bt_gatt_server_destroy_func_t debug_destroy;
void *debug_data;
};

-static bool gatt_server_register_att_handlers(struct bt_gatt_server *server)
+static void gatt_server_unregister_handlers(struct bt_gatt_server *server)
{
- /* TODO */
- return true;
+ bt_att_unregister(server->att, server->mtu_id);
}

static void gatt_server_cleanup(struct bt_gatt_server *server)
{
+ gatt_server_unregister_handlers(server);
bt_att_unref(server->att);
server->att = NULL;
}

+static void encode_error_rsp(uint8_t opcode, uint16_t handle, uint8_t ecode,
+ uint8_t pdu[4])
+{
+ pdu[0] = opcode;
+ pdu[3] = ecode;
+ put_le16(handle, pdu + 1);
+}
+
+static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
+ uint16_t length, void *user_data)
+{
+ struct bt_gatt_server *server = user_data;
+ uint16_t client_rx_mtu;
+ uint16_t final_mtu;
+ uint8_t rsp_pdu[4];
+
+ if (length != 2) {
+ encode_error_rsp(opcode, 0, BT_ATT_ERROR_INVALID_PDU, rsp_pdu);
+ bt_att_send(server->att, BT_ATT_OP_ERROR_RSP, rsp_pdu,
+ sizeof(rsp_pdu), NULL, NULL, NULL);
+ return;
+ }
+
+ client_rx_mtu = get_le16(pdu);
+ final_mtu = MAX(MIN(client_rx_mtu, server->mtu), BT_ATT_DEFAULT_LE_MTU);
+
+ /* Respond with the server MTU */
+ put_le16(server->mtu, rsp_pdu);
+ bt_att_send(server->att, BT_ATT_OP_MTU_RSP, rsp_pdu, 2, NULL, NULL,
+ NULL);
+
+ /* Set MTU to be the minimum */
+ server->mtu = final_mtu;
+ bt_att_set_mtu(server->att, final_mtu);
+}
+
+static bool gatt_server_register_att_handlers(struct bt_gatt_server *server)
+{
+ /* EXCHANGE MTU request */
+ server->mtu_id = bt_att_register(server->att, BT_ATT_OP_MTU_REQ,
+ exchange_mtu_cb,
+ server, NULL);
+ if (!server->mtu_id)
+ return false;
+
+ return true;
+}
+
struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
struct bt_att *att, uint16_t mtu)
{
--
2.1.0.rc2.206.gedb03e5


2014-10-06 21:01:41

by Arman Uguray

[permalink] [raw]
Subject: [PATCH BlueZ v1 1/4] shared/att: Drop the connection is a request is received while one is pending.

With this patch, when bt_att is being used for the server role, it now makes
sure that a second request drops the connection unless a response for a previous
request has been sent.
---
src/shared/att.c | 101 +++++++++++++++++++++++++++++++++----------------------
1 file changed, 61 insertions(+), 40 deletions(-)

diff --git a/src/shared/att.c b/src/shared/att.c
index de35aef..96f34a3 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -64,6 +64,8 @@ struct bt_att {
bool in_disconn;
bool need_disconn_cleanup;

+ bool in_req; /* There's a pending incoming request */
+
uint8_t *buf;
uint16_t mtu;

@@ -460,6 +462,9 @@ static bool can_write_data(struct io *io, void *user_data)
case ATT_OP_TYPE_IND:
att->pending_ind = op;
break;
+ case ATT_OP_TYPE_RSP:
+ /* Set in_req to false to indicate that no request is pending */
+ att->in_req = false;
default:
destroy_att_send_op(op);
return true;
@@ -604,6 +609,46 @@ static void handle_notify(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
bt_att_unref(att);
}

+static void disconn_handler(void *data, void *user_data)
+{
+ struct att_disconn *disconn = data;
+
+ if (disconn->removed)
+ return;
+
+ if (disconn->callback)
+ disconn->callback(disconn->user_data);
+}
+
+static bool disconnect_cb(struct io *io, void *user_data)
+{
+ struct bt_att *att = user_data;
+
+ io_destroy(att->io);
+ att->io = NULL;
+
+ util_debug(att->debug_callback, att->debug_data,
+ "Physical link disconnected");
+
+ bt_att_ref(att);
+ att->in_disconn = true;
+ queue_foreach(att->disconn_list, disconn_handler, NULL);
+ att->in_disconn = false;
+
+ if (att->need_disconn_cleanup) {
+ queue_remove_all(att->disconn_list, match_disconn_removed, NULL,
+ destroy_att_disconn);
+ att->need_disconn_cleanup = false;
+ }
+
+ bt_att_cancel_all(att);
+ bt_att_unregister_all(att);
+
+ bt_att_unref(att);
+
+ return false;
+}
+
static bool can_read_data(struct io *io, void *user_data)
{
struct bt_att *att = user_data;
@@ -635,6 +680,22 @@ static bool can_read_data(struct io *io, void *user_data)
util_debug(att->debug_callback, att->debug_data,
"ATT opcode cannot be handled: 0x%02x", opcode);
break;
+ case ATT_OP_TYPE_REQ:
+ /* If a request is currently pending, then the sequential
+ * protocol was violated. Disconnect the bearer and notify the
+ * upper-layer.
+ */
+ if (att->in_req) {
+ util_debug(att->debug_callback, att->debug_data,
+ "Received request while another is "
+ "pending: 0x%02x", opcode);
+ disconnect_cb(att->io, att);
+ return false;
+ }
+
+ att->in_req = true;
+
+ /* Fall through to the next case */
default:
/* For all other opcodes notify the upper layer of the PDU and
* let them act on it.
@@ -648,46 +709,6 @@ static bool can_read_data(struct io *io, void *user_data)
return true;
}

-static void disconn_handler(void *data, void *user_data)
-{
- struct att_disconn *disconn = data;
-
- if (disconn->removed)
- return;
-
- if (disconn->callback)
- disconn->callback(disconn->user_data);
-}
-
-static bool disconnect_cb(struct io *io, void *user_data)
-{
- struct bt_att *att = user_data;
-
- io_destroy(att->io);
- att->io = NULL;
-
- util_debug(att->debug_callback, att->debug_data,
- "Physical link disconnected");
-
- bt_att_ref(att);
- att->in_disconn = true;
- queue_foreach(att->disconn_list, disconn_handler, NULL);
- att->in_disconn = false;
-
- if (att->need_disconn_cleanup) {
- queue_remove_all(att->disconn_list, match_disconn_removed, NULL,
- destroy_att_disconn);
- att->need_disconn_cleanup = false;
- }
-
- bt_att_cancel_all(att);
- bt_att_unregister_all(att);
-
- bt_att_unref(att);
-
- return false;
-}
-
struct bt_att *bt_att_new(int fd)
{
struct bt_att *att;
--
2.1.0.rc2.206.gedb03e5