*v1:
- bt_gatt_client_new now accepts a gatt_db. bt_gatt_client no longer
constructs a gatt_db directly.
- Other fixes suggested during reviews.
This patch set integrates shared/gatt-db into shared/gatt-client:
1. shared/gatt-client's internal service_list structure has been
removed entirely in favor of gatt-db.
2. The high level structures defined in gatt-client.h
(bt_gatt_service_t, bt_gatt_characteristic_t, bt_gatt_descriptor_t)
and the corresponding iterator structures and functions have been
removed.
3. Code using the iterators (unit/test-gatt, tools/btgatt*) now access
the client cache via gatt-client's gatt-db.
4. Small bug fixes in gatt-db.
Arman Uguray (8):
shared/gatt-db: Add gatt_db_isempty.
shared/gatt-client: Store services in gatt_db.
shared/gatt-client: Use gatt_db in bt_gatt_register_notify
tools/btgatt-server: Add the "services" command
tools/btgatt-client: Use gatt-db instead of iterators
unit/test-gatt: Use gatt-db for CLIENT tests
shared/gatt-client: Remove GATT structs and iterators
TODO: Add item for gatt-db service callbacks.
TODO | 6 +
src/shared/gatt-client.c | 1196 +++++++++++++++++++++-------------------------
src/shared/gatt-client.h | 78 +--
src/shared/gatt-db.c | 8 +
src/shared/gatt-db.h | 2 +
tools/btgatt-client.c | 184 ++++---
tools/btgatt-server.c | 90 ++++
unit/test-gatt.c | 262 ++++++----
8 files changed, 926 insertions(+), 900 deletions(-)
--
2.2.0.rc0.207.ga3a616c
Hi Arman,
On Wed, Dec 3, 2014 at 2:12 AM, Arman Uguray <[email protected]> wrote:
> *v1:
> - bt_gatt_client_new now accepts a gatt_db. bt_gatt_client no longer
> constructs a gatt_db directly.
> - Other fixes suggested during reviews.
>
> This patch set integrates shared/gatt-db into shared/gatt-client:
> 1. shared/gatt-client's internal service_list structure has been
> removed entirely in favor of gatt-db.
> 2. The high level structures defined in gatt-client.h
> (bt_gatt_service_t, bt_gatt_characteristic_t, bt_gatt_descriptor_t)
> and the corresponding iterator structures and functions have been
> removed.
> 3. Code using the iterators (unit/test-gatt, tools/btgatt*) now access
> the client cache via gatt-client's gatt-db.
> 4. Small bug fixes in gatt-db.
>
> Arman Uguray (8):
> shared/gatt-db: Add gatt_db_isempty.
> shared/gatt-client: Store services in gatt_db.
> shared/gatt-client: Use gatt_db in bt_gatt_register_notify
> tools/btgatt-server: Add the "services" command
> tools/btgatt-client: Use gatt-db instead of iterators
> unit/test-gatt: Use gatt-db for CLIENT tests
> shared/gatt-client: Remove GATT structs and iterators
> TODO: Add item for gatt-db service callbacks.
>
> TODO | 6 +
> src/shared/gatt-client.c | 1196 +++++++++++++++++++++-------------------------
> src/shared/gatt-client.h | 78 +--
> src/shared/gatt-db.c | 8 +
> src/shared/gatt-db.h | 2 +
> tools/btgatt-client.c | 184 ++++---
> tools/btgatt-server.c | 90 ++++
> unit/test-gatt.c | 262 ++++++----
> 8 files changed, 926 insertions(+), 900 deletions(-)
>
> --
> 2.2.0.rc0.207.ga3a616c
Applied after fixing the build, you should probably start using git
rebase --exec make, anyway thanks a lot for the patches.
--
Luiz Augusto von Dentz
This patch removes the high-level structs and iterators used by
shared/gatt-client in favor of gatt-db.
---
src/shared/gatt-client.c | 88 ------------------------------------------------
src/shared/gatt-client.h | 74 ++--------------------------------------
2 files changed, 3 insertions(+), 159 deletions(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 4cd8770..463de3b 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -1636,94 +1636,6 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
return true;
}
-bool bt_gatt_service_iter_init(struct bt_gatt_service_iter *iter,
- struct bt_gatt_client *client)
-{
- if (!iter || !client)
- return false;
-
- if (client->in_init || client->in_svc_chngd)
- return false;
-
- memset(iter, 0, sizeof(*iter));
- iter->client = client;
- iter->ptr = NULL;
-
- return true;
-}
-
-bool bt_gatt_service_iter_next(struct bt_gatt_service_iter *iter,
- const bt_gatt_service_t **service)
-{
- /* TODO: Remove iterator functions */
-
- return false;
-}
-
-bool bt_gatt_service_iter_next_by_handle(struct bt_gatt_service_iter *iter,
- uint16_t start_handle,
- const bt_gatt_service_t **service)
-{
- while (bt_gatt_service_iter_next(iter, service)) {
- if ((*service)->start_handle == start_handle)
- return true;
- }
-
- return false;
-}
-
-bool bt_gatt_service_iter_next_by_uuid(struct bt_gatt_service_iter *iter,
- const uint8_t uuid[BT_GATT_UUID_SIZE],
- const bt_gatt_service_t **service)
-{
- while (bt_gatt_service_iter_next(iter, service)) {
- if (memcmp((*service)->uuid, uuid, UUID_BYTES) == 0)
- return true;
- }
-
- return false;
-}
-
-bool bt_gatt_characteristic_iter_init(struct bt_gatt_characteristic_iter *iter,
- const bt_gatt_service_t *service)
-{
- if (!iter || !service)
- return false;
-
- memset(iter, 0, sizeof(*iter));
- iter->service = (void *) service;
-
- return true;
-}
-
-bool bt_gatt_characteristic_iter_next(struct bt_gatt_characteristic_iter *iter,
- const bt_gatt_characteristic_t **chrc)
-{
- /* TODO: Remove iterator functions */
-
- return false;
-}
-
-bool bt_gatt_include_service_iter_init(struct bt_gatt_incl_service_iter *iter,
- const bt_gatt_service_t *service)
-{
- if (!iter || !service)
- return false;
-
- memset(iter, 0, sizeof(*iter));
- iter->service = (void *) service;
-
- return true;
-}
-
-bool bt_gatt_include_service_iter_next(struct bt_gatt_incl_service_iter *iter,
- const bt_gatt_included_service_t **incl)
-{
- /* TODO: Remove iterator functions */
-
- return false;
-}
-
struct read_op {
bt_gatt_client_read_callback_t callback;
void *user_data;
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index a09c3b8..984c23f 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -40,6 +40,9 @@ typedef void (*bt_gatt_client_destroy_func_t)(void *user_data);
typedef void (*bt_gatt_client_callback_t)(bool success, uint8_t att_ecode,
void *user_data);
typedef void (*bt_gatt_client_debug_func_t)(const char *str, void *user_data);
+typedef void (*bt_gatt_client_read_callback_t)(bool success, uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data);
typedef void (*bt_gatt_client_write_long_callback_t)(bool success,
bool reliable_error, uint8_t att_ecode,
void *user_data);
@@ -67,75 +70,6 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
-typedef struct {
- bool primary;
- uint16_t start_handle;
- uint16_t end_handle;
- uint8_t uuid[BT_GATT_UUID_SIZE];
-} bt_gatt_service_t;
-
-typedef struct {
- uint16_t handle;
- uint8_t uuid[BT_GATT_UUID_SIZE];
-} bt_gatt_descriptor_t;
-
-typedef struct {
- uint16_t start_handle;
- uint16_t end_handle;
- uint16_t value_handle;
- uint8_t properties;
- uint8_t uuid[BT_GATT_UUID_SIZE];
- const bt_gatt_descriptor_t *descs;
- size_t num_descs;
-} bt_gatt_characteristic_t;
-
-typedef struct {
- uint16_t handle;
- uint16_t start_handle;
- uint16_t end_handle;
- uint8_t uuid[BT_GATT_UUID_SIZE];
-} bt_gatt_included_service_t;
-
-struct bt_gatt_service_iter {
- struct bt_gatt_client *client;
- void *ptr;
-};
-
-struct bt_gatt_characteristic_iter {
- void *service;
- size_t pos;
-};
-
-struct bt_gatt_incl_service_iter {
- void *service;
- size_t pos;
-};
-
-bool bt_gatt_service_iter_init(struct bt_gatt_service_iter *iter,
- struct bt_gatt_client *client);
-bool bt_gatt_service_iter_next(struct bt_gatt_service_iter *iter,
- const bt_gatt_service_t **service);
-bool bt_gatt_service_iter_next_by_handle(struct bt_gatt_service_iter *iter,
- uint16_t start_handle,
- const bt_gatt_service_t **service);
-bool bt_gatt_service_iter_next_by_uuid(struct bt_gatt_service_iter *iter,
- const uint8_t uuid[BT_GATT_UUID_SIZE],
- const bt_gatt_service_t **service);
-
-bool bt_gatt_characteristic_iter_init(struct bt_gatt_characteristic_iter *iter,
- const bt_gatt_service_t *service);
-bool bt_gatt_characteristic_iter_next(struct bt_gatt_characteristic_iter *iter,
- const bt_gatt_characteristic_t **chrc);
-
-bool bt_gatt_include_service_iter_init(struct bt_gatt_incl_service_iter *iter,
- const bt_gatt_service_t *service);
-bool bt_gatt_include_service_iter_next(struct bt_gatt_incl_service_iter *iter,
- const bt_gatt_included_service_t **inc);
-
-typedef void (*bt_gatt_client_read_callback_t)(bool success, uint8_t att_ecode,
- const uint8_t *value, uint16_t length,
- void *user_data);
-
bool bt_gatt_client_read_value(struct bt_gatt_client *client,
uint16_t value_handle,
bt_gatt_client_read_callback_t callback,
@@ -146,8 +80,6 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
bt_gatt_client_read_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
-
-
bool bt_gatt_client_read_multiple(struct bt_gatt_client *client,
uint16_t *handles, uint8_t num_handles,
bt_gatt_client_read_callback_t callback,
--
2.2.0.rc0.207.ga3a616c
btgatt-client now iterates services, characteristics, and descriptors
of the shared/gatt-client using gatt-db instead of the iterators.
---
tools/btgatt-client.c | 171 +++++++++++++++++++++-----------------------------
1 file changed, 73 insertions(+), 98 deletions(-)
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 6d1b5cb..1d98274 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -41,7 +41,9 @@
#include "monitor/mainloop.h"
#include "src/shared/util.h"
+#include "src/shared/queue.h"
#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
#include "src/shared/gatt-client.h"
#define ATT_CID 4
@@ -170,118 +172,111 @@ static void client_destroy(struct client *cli)
bt_gatt_client_unref(cli->gatt);
}
-static void print_uuid(const uint8_t uuid[16])
+static void print_uuid(const bt_uuid_t *uuid)
{
char uuid_str[MAX_LEN_UUID_STR];
- bt_uuid_t tmp;
+ bt_uuid_t uuid128;
- tmp.type = BT_UUID128;
- memcpy(tmp.value.u128.data, uuid, 16 * sizeof(uint8_t));
- bt_uuid_to_string(&tmp, uuid_str, sizeof(uuid_str));
+ bt_uuid_to_uuid128(uuid, &uuid128);
+ bt_uuid_to_string(&uuid128, uuid_str, sizeof(uuid_str));
printf("%s\n", uuid_str);
}
-static void print_service(const bt_gatt_service_t *service)
+static void print_incl(struct gatt_db_attribute *attr, void *user_data)
{
- struct bt_gatt_characteristic_iter iter;
- struct bt_gatt_incl_service_iter include_iter;
- const bt_gatt_characteristic_t *chrc;
- const bt_gatt_included_service_t *incl;
- size_t i;
-
- if (!bt_gatt_characteristic_iter_init(&iter, service)) {
- PRLOG("Failed to initialize characteristic iterator\n");
+ struct client *cli = user_data;
+ uint16_t handle, start, end;
+ struct gatt_db_attribute *service;
+ bt_uuid_t uuid;
+
+ if (!gatt_db_attribute_get_incl_data(attr, &handle, &start, &end))
return;
- }
- if (!bt_gatt_include_service_iter_init(&include_iter, service)) {
- PRLOG("Failed to initialize include service iterator\n");
+ service = gatt_db_get_attribute(cli->db, start);
+ if (!service)
return;
- }
- printf(COLOR_RED "service" COLOR_OFF " - start: 0x%04x, "
- "end: 0x%04x, type: %s, uuid: ",
- service->start_handle, service->end_handle,
- service->primary ? "primary" : "secondary");
- print_uuid(service->uuid);
+ gatt_db_attribute_get_service_uuid(service, &uuid);
- while (bt_gatt_include_service_iter_next(&include_iter, &incl)) {
- printf("\t " COLOR_GREEN "include" COLOR_OFF " - handle: "
+ printf("\t " COLOR_GREEN "include" COLOR_OFF " - handle: "
"0x%04x, - start: 0x%04x, end: 0x%04x,"
- "uuid: ", incl->handle,
- incl->start_handle, incl->end_handle);
- print_uuid(incl->uuid);
- }
-
- while (bt_gatt_characteristic_iter_next(&iter, &chrc)) {
- printf("\t " COLOR_YELLOW "charac" COLOR_OFF
- " - start: 0x%04x, end: 0x%04x, "
- "value: 0x%04x, props: 0x%02x, uuid: ",
- chrc->start_handle,
- chrc->end_handle,
- chrc->value_handle,
- chrc->properties);
- print_uuid(chrc->uuid);
-
- for (i = 0; i < chrc->num_descs; i++) {
- printf("\t\t " COLOR_MAGENTA "descr" COLOR_OFF
- " - handle: 0x%04x, uuid: ",
- chrc->descs[i].handle);
- print_uuid(chrc->descs[i].uuid);
- }
- }
-
- printf("\n");
+ "uuid: ", handle, start, end);
+ print_uuid(&uuid);
}
-static void print_services(struct client *cli)
+static void print_desc(struct gatt_db_attribute *attr, void *user_data)
{
- struct bt_gatt_service_iter iter;
- const bt_gatt_service_t *service;
+ printf("\t\t " COLOR_MAGENTA "descr" COLOR_OFF
+ " - handle: 0x%04x, uuid: ",
+ gatt_db_attribute_get_handle(attr));
+ print_uuid(gatt_db_attribute_get_type(attr));
+}
- if (!bt_gatt_service_iter_init(&iter, cli->gatt)) {
- PRLOG("Failed to initialize service iterator\n");
+static void print_chrc(struct gatt_db_attribute *attr, void *user_data)
+{
+ uint16_t handle, value_handle;
+ uint8_t properties;
+ bt_uuid_t uuid;
+
+ if (!gatt_db_attribute_get_char_data(attr, &handle,
+ &value_handle,
+ &properties,
+ &uuid))
return;
- }
- printf("\n");
+ printf("\t " COLOR_YELLOW "charac" COLOR_OFF
+ " - start: 0x%04x, value: 0x%04x, "
+ "props: 0x%02x, uuid: ",
+ handle, value_handle, properties);
+ print_uuid(&uuid);
- while (bt_gatt_service_iter_next(&iter, &service))
- print_service(service);
+ gatt_db_service_foreach_desc(attr, print_desc, NULL);
}
-static void print_services_by_uuid(struct client *cli, const bt_uuid_t *uuid)
+static void print_service(struct gatt_db_attribute *attr, void *user_data)
{
- struct bt_gatt_service_iter iter;
- const bt_gatt_service_t *service;
+ struct client *cli = user_data;
+ uint16_t start, end;
+ bool primary;
+ bt_uuid_t uuid;
- if (!bt_gatt_service_iter_init(&iter, cli->gatt)) {
- PRLOG("Failed to initialize service iterator\n");
+ if (!gatt_db_attribute_get_service_data(attr, &start, &end, &primary,
+ &uuid))
return;
- }
+ printf(COLOR_RED "service" COLOR_OFF " - start: 0x%04x, "
+ "end: 0x%04x, type: %s, uuid: ",
+ start, end, primary ? "primary" : "secondary");
+ print_uuid(&uuid);
+
+ gatt_db_service_foreach_incl(attr, print_incl, cli);
+ gatt_db_service_foreach_char(attr, print_chrc, NULL);
+
+ printf("\n");
+}
+
+static void print_services(struct client *cli)
+{
printf("\n");
- while (bt_gatt_service_iter_next_by_uuid(&iter, uuid->value.u128.data,
- &service))
- print_service(service);
+ gatt_db_foreach_service(cli->db, print_service, cli);
}
-static void print_services_by_handle(struct client *cli, uint16_t handle)
+static void print_services_by_uuid(struct client *cli, const bt_uuid_t *uuid)
{
- struct bt_gatt_service_iter iter;
- const bt_gatt_service_t *service;
+ printf("\n");
- if (!bt_gatt_service_iter_init(&iter, cli->gatt)) {
- PRLOG("Failed to initialize service iterator\n");
- return;
- }
+ /* TODO: Filter by UUID */
+ gatt_db_foreach_service(cli->db, print_service, cli);
+}
+static void print_services_by_handle(struct client *cli, uint16_t handle)
+{
printf("\n");
- while (bt_gatt_service_iter_next_by_handle(&iter, handle, &service))
- print_service(service);
+ /* TODO: Filter by handle */
+ gatt_db_foreach_service(cli->db, print_service, cli);
}
static void ready_cb(bool success, uint8_t att_ecode, void *user_data)
@@ -304,32 +299,12 @@ static void service_changed_cb(uint16_t start_handle, uint16_t end_handle,
void *user_data)
{
struct client *cli = user_data;
- struct bt_gatt_service_iter iter;
- const bt_gatt_service_t *service;
-
- if (!bt_gatt_service_iter_init(&iter, cli->gatt)) {
- PRLOG("Failed to initialize service iterator\n");
- return;
- }
printf("\nService Changed handled - start: 0x%04x end: 0x%04x\n",
start_handle, end_handle);
- if (!bt_gatt_service_iter_next_by_handle(&iter, start_handle,
- &service)) {
- print_prompt();
- return;
- }
-
- print_service(service);
-
- while (bt_gatt_service_iter_next(&iter, &service)) {
- if (service->start_handle >= end_handle)
- break;
-
- print_service(service);
- }
-
+ gatt_db_foreach_service_in_range(cli->db, print_service, cli,
+ start_handle, end_handle);
print_prompt();
}
--
2.2.0.rc0.207.ga3a616c
Added TODO items for adding gatt-db service added/removed callbacks.
---
TODO | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/TODO b/TODO
index ca3779b..fbd3ad6 100644
--- a/TODO
+++ b/TODO
@@ -155,6 +155,12 @@ ATT/GATT (new shared stack)
Priority: Medium
Complexity: C4
+- Add service_added and service_removed callbacks to shared/gatt-db which should
+ get triggered via bt_gatt_service_set_active and when services get removed.
+
+ Priority: Medium
+ Complexity: C2
+
- Implement the client portion of doc/gatt-api.txt using shared/gatt-client once
plugin/profile code uses it.
--
2.2.0.rc0.207.ga3a616c
This patch rewrites parts of unit/test-gatt that verify the contents
of a shared/gatt-client to use the client's gatt-db instead of the
service iterators.
---
unit/test-gatt.c | 239 +++++++++++++++++++++++++++++++++++++------------------
1 file changed, 160 insertions(+), 79 deletions(-)
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 32bddac..12b1919 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -56,10 +56,29 @@ enum context_type {
SERVER
};
+struct gatt_desc {
+ uint16_t handle;
+ uint8_t uuid[16];
+};
+
+struct gatt_chrc {
+ uint16_t handle;
+ uint16_t value_handle;
+ uint8_t properties;
+ uint8_t uuid[16];
+
+ const struct gatt_desc *descs;
+ size_t num_descs;
+};
+
struct gatt_service {
- const bt_gatt_service_t *service;
- int num_chars;
- const bt_gatt_characteristic_t **chars;
+ bool primary;
+ uint16_t start_handle;
+ uint16_t end_handle;
+ uint8_t uuid[16];
+
+ const struct gatt_chrc **chars;
+ size_t num_chars;
};
struct test_data {
@@ -67,7 +86,7 @@ struct test_data {
struct test_pdu *pdu_list;
enum context_type context_type;
bt_uuid_t *uuid;
- int num_services;
+ size_t num_services;
const struct gatt_service **services;
const void *step;
};
@@ -177,23 +196,14 @@ static bt_uuid_t uuid_char_128 = {
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}
};
-const bt_gatt_service_t service_1 = {
- .primary = true,
- .start_handle = 0x0001,
- .end_handle = 0x0004,
- .uuid = {0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x10, 0x00,
- 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}
-};
-
-const bt_gatt_descriptor_t descriptor_1 = {
+const struct gatt_desc descriptor_1 = {
.handle = 0x0004,
.uuid = {0x00, 0x00, 0x29, 0x01, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}
};
-const bt_gatt_characteristic_t characteristic_1 = {
- .start_handle = 0x0002,
- .end_handle = 0x0004,
+const struct gatt_chrc characteristic_1 = {
+ .handle = 0x0002,
.value_handle = 0x0003,
.properties = 0x02,
.uuid = {0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x10, 0x00,
@@ -202,23 +212,14 @@ const bt_gatt_characteristic_t characteristic_1 = {
.num_descs = 1
};
-const bt_gatt_service_t service_2 = {
- .primary = true,
- .start_handle = 0x0005,
- .end_handle = 0x0008,
- .uuid = {0x00, 0x00, 0x18, 0x0d, 0x00, 0x00, 0x10, 0x00,
- 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}
-};
-
-const bt_gatt_descriptor_t descriptor_2 = {
+const struct gatt_desc descriptor_2 = {
.handle = 0x0008,
.uuid = {0x00, 0x00, 0x29, 0x01, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}
};
-const bt_gatt_characteristic_t characteristic_2 = {
- .start_handle = 0x0006,
- .end_handle = 0x0008,
+const struct gatt_chrc characteristic_2 = {
+ .handle = 0x0006,
.value_handle = 0x0007,
.properties = 0x02,
.uuid = {0x00, 0x00, 0x2a, 0x29, 0x00, 0x00, 0x10, 0x00,
@@ -227,19 +228,27 @@ const bt_gatt_characteristic_t characteristic_2 = {
.num_descs = 1
};
-const bt_gatt_characteristic_t *characteristics_1[] = {&characteristic_1};
-const bt_gatt_characteristic_t *characteristics_2[] = {&characteristic_2};
+const struct gatt_chrc *characteristics_1[] = {&characteristic_1};
+const struct gatt_chrc *characteristics_2[] = {&characteristic_2};
const struct gatt_service gatt_service_1 = {
- .service = &service_1,
- .num_chars = sizeof(characteristics_1) / sizeof(characteristics_1[0]),
- .chars = characteristics_1
+ .primary = true,
+ .start_handle = 0x0001,
+ .end_handle = 0x0004,
+ .uuid = {0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb},
+ .chars = characteristics_1,
+ .num_chars = sizeof(characteristics_1) / sizeof(characteristics_1[0])
};
const struct gatt_service gatt_service_2 = {
- .service = &service_2,
- .num_chars = sizeof(characteristics_2) / sizeof(characteristics_2[0]),
- .chars = characteristics_2
+ .primary = true,
+ .start_handle = 0x0005,
+ .end_handle = 0x0008,
+ .uuid = {0x00, 0x00, 0x18, 0x0d, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb},
+ .chars = characteristics_2,
+ .num_chars = sizeof(characteristics_2) / sizeof(characteristics_2[0])
};
const struct gatt_service *service_data_1[] = {&gatt_service_1,
@@ -344,36 +353,60 @@ static void print_debug(const char *str, void *user_data)
g_print("%s%s\n", prefix, str);
}
-static void compare_service(const bt_gatt_service_t *a,
- const bt_gatt_service_t *b)
+static void assert_service(const struct gatt_service *a,
+ struct gatt_db_attribute *attr)
{
- g_assert(a->primary && b->primary);
- g_assert(a->start_handle == b->start_handle);
- g_assert(a->end_handle == b->end_handle);
- g_assert(memcmp(a->uuid, b->uuid, sizeof(a->uuid)) == 0);
+ uint16_t start_handle, end_handle;
+ bool primary;
+ bt_uuid_t uuid;
+ uint128_t u128;
+
+ g_assert(gatt_db_attribute_get_service_data(attr, &start_handle,
+ &end_handle,
+ &primary, &uuid));
+
+ u128 = uuid.value.u128;
+
+ g_assert(a->primary && primary);
+ g_assert(a->start_handle == start_handle);
+ g_assert(a->end_handle == end_handle);
+ g_assert(memcmp(a->uuid, u128.data, sizeof(u128.data)) == 0);
}
-static void compare_descs(const bt_gatt_descriptor_t *a,
- const bt_gatt_descriptor_t *b)
+static void assert_chrc(const struct gatt_chrc *a,
+ struct gatt_db_attribute *attr)
{
- g_assert(a->handle == b->handle);
- g_assert(memcmp(a->uuid, b->uuid, sizeof(a->uuid)) == 0);
+ uint16_t handle, value_handle;
+ uint8_t properties;
+ bt_uuid_t uuid;
+ uint128_t u128;
+
+ g_assert(gatt_db_attribute_get_char_data(attr, &handle,
+ &value_handle,
+ &properties, &uuid));
+
+ u128 = uuid.value.u128;
+
+ g_assert(a->handle == handle);
+ g_assert(a->value_handle == value_handle);
+ g_assert(a->properties == properties);
+ g_assert(memcmp(a->uuid, u128.data, sizeof(u128.data)) == 0);
}
-static void compare_chars(const bt_gatt_characteristic_t *a,
- const bt_gatt_characteristic_t *b)
+static void assert_desc(const struct gatt_desc *a,
+ struct gatt_db_attribute *attr)
{
- unsigned int i;
+ uint16_t handle;
+ const bt_uuid_t *uuid;
+ uint128_t u128;
+
+ handle = gatt_db_attribute_get_handle(attr);
+ uuid = gatt_db_attribute_get_type(attr);
- g_assert(a->start_handle == b->start_handle);
- g_assert(a->end_handle == b->end_handle);
- g_assert(a->properties == b->properties);
- g_assert(a->value_handle == b->value_handle);
- g_assert(a->num_descs == b->num_descs);
- g_assert(memcmp(a->uuid, b->uuid, sizeof(a->uuid)) == 0);
+ u128 = uuid->value.u128;
- for (i = 0; i < a->num_descs; i++)
- compare_descs(&a->descs[i], &b->descs[i]);
+ g_assert(a->handle == handle);
+ g_assert(memcmp(a->uuid, u128.data, sizeof(u128.data)) == 0);
}
typedef void (*test_step_t)(struct context *context);
@@ -388,39 +421,87 @@ struct test_step {
uint16_t length;
};
+struct service_test_data {
+ const struct test_data *data;
+ size_t svc_index;
+ size_t chrc_index;
+ size_t desc_index;
+};
+
+static void compare_desc(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct service_test_data *test_data = user_data;
+ const struct gatt_service *svc;
+ const struct gatt_chrc *chrc;
+
+ svc = test_data->data->services[test_data->svc_index];
+ chrc = svc->chars[test_data->chrc_index];
+ g_assert(test_data->desc_index < chrc->num_descs);
+
+ assert_desc(&chrc->descs[test_data->desc_index], attr);
+
+ test_data->desc_index++;
+}
+
+static void compare_chrc(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct service_test_data *test_data = user_data;
+ const struct gatt_service *svc;
+ const struct gatt_chrc *chrc;
+
+ svc = test_data->data->services[test_data->svc_index];
+ g_assert(test_data->chrc_index < svc->num_chars);
+ chrc = svc->chars[test_data->chrc_index];
+
+ assert_chrc(chrc, attr);
+
+ test_data->desc_index = 0;
+
+ gatt_db_service_foreach_desc(attr, compare_desc, test_data);
+ g_assert(test_data->desc_index == chrc->num_descs);
+
+ test_data->chrc_index++;
+}
+
+static void compare_service(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct service_test_data *test_data = user_data;
+ const struct gatt_service *svc;
+
+ g_assert(test_data->svc_index < test_data->data->num_services);
+ svc = test_data->data->services[test_data->svc_index];
+
+ assert_service(test_data->data->services[test_data->svc_index], attr);
+
+ test_data->chrc_index = 0;
+
+ gatt_db_service_foreach_char(attr, compare_chrc, test_data);
+ g_assert(test_data->chrc_index == svc->num_chars);
+
+ test_data->svc_index++;
+}
+
static void client_ready_cb(bool success, uint8_t att_ecode, void *user_data)
{
struct context *context = user_data;
- const struct test_data *data = context->data;
- struct bt_gatt_characteristic_iter char_iter;
- const bt_gatt_characteristic_t *charac;
- const bt_gatt_service_t *service;
- struct bt_gatt_service_iter iter;
- int i, j;
+ struct service_test_data test_data;
g_assert(success);
- if (!data->services) {
+ test_data.data = context->data;
+ test_data.svc_index = 0;
+
+ if (!test_data.data->services) {
context_quit(context);
return;
}
- g_assert(bt_gatt_service_iter_init(&iter, context->client));
- for (i = 0; i < data->num_services; i++) {
- g_assert(bt_gatt_service_iter_next(&iter, &service));
- compare_service(service, data->services[i]->service);
- g_assert(bt_gatt_characteristic_iter_init(&char_iter, service));
-
- for (j = 0; j < data->services[i]->num_chars; j++) {
- g_assert(bt_gatt_characteristic_iter_next(&char_iter,
- &charac));
- compare_chars(charac, data->services[i]->chars[j]);
- }
- g_assert(!bt_gatt_characteristic_iter_next(&char_iter,
- &charac));
- }
+ g_assert(context->client);
+ g_assert(context->client_db);
- g_assert(!bt_gatt_service_iter_next(&iter, &service));
+ gatt_db_foreach_service(context->client_db, compare_service,
+ &test_data);
+ g_assert(test_data.svc_index == test_data.data->num_services);
if (context->data->step) {
const struct test_step *step = context->data->step;
--
2.2.0.rc0.207.ga3a616c
This patch rewrites the notification/indication logic in gatt-client to
use the internal gatt-db.
---
src/shared/gatt-client.c | 203 ++++++++++++++++++++++++++++++++++-------------
1 file changed, 147 insertions(+), 56 deletions(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index c891516..4cd8770 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -45,27 +45,6 @@
#define GATT_SVC_UUID 0x1801
#define SVC_CHNGD_UUID 0x2a05
-struct chrc_data {
- /* The public characteristic entry. */
- bt_gatt_characteristic_t chrc_external;
-
- /* The private entries. */
- uint16_t ccc_handle;
- int notify_count; /* Reference count of registered notify callbacks */
-
- /* Internal non-const pointer to the descriptor array. We use this
- * internally to modify/free the array, while we expose it externally
- * using the const pointer "descs" field in bt_gatt_characteristic_t.
- */
- bt_gatt_descriptor_t *descs;
-
- /* Pending calls to register_notify are queued here so that they can be
- * processed after a write that modifies the CCC descriptor.
- */
- struct queue *reg_notify_queue;
- unsigned int ccc_write_id;
-};
-
struct bt_gatt_client {
struct bt_att *att;
int ref_count;
@@ -97,6 +76,7 @@ struct bt_gatt_client {
/* List of registered disconnect/notification/indication callbacks */
struct queue *notify_list;
+ struct queue *notify_chrcs;
int next_reg_id;
unsigned int disc_id, notify_id, ind_id;
bool in_notify;
@@ -111,13 +91,26 @@ struct bt_gatt_client {
bool in_svc_chngd;
};
+struct notify_chrc {
+ uint16_t value_handle;
+ uint16_t ccc_handle;
+ uint16_t properties;
+ int notify_count; /* Reference count of registered notify callbacks */
+
+ /* Pending calls to register_notify are queued here so that they can be
+ * processed after a write that modifies the CCC descriptor.
+ */
+ struct queue *reg_notify_queue;
+ unsigned int ccc_write_id;
+};
+
struct notify_data {
struct bt_gatt_client *client;
bool removed;
bool invalid;
unsigned int id;
int ref_count;
- struct chrc_data *chrc;
+ struct notify_chrc *chrc;
bt_gatt_client_notify_id_callback_t callback;
bt_gatt_client_notify_callback_t notify;
void *user_data;
@@ -144,6 +137,76 @@ static void notify_data_unref(void *data)
free(notify_data);
}
+static void find_ccc(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct gatt_db_attribute **ccc_ptr = user_data;
+ bt_uuid_t uuid;
+
+ if (*ccc_ptr)
+ return;
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+ if (bt_uuid_cmp(&uuid, gatt_db_attribute_get_type(attr)))
+ return;
+
+ *ccc_ptr = attr;
+}
+
+static struct notify_chrc *notify_chrc_create(struct bt_gatt_client *client,
+ uint16_t value_handle)
+{
+ struct gatt_db_attribute *attr, *ccc;
+ struct notify_chrc *chrc;
+ bt_uuid_t uuid;
+ uint8_t properties;
+
+ /* Check that chrc_value_handle belongs to a known characteristic */
+ attr = gatt_db_get_attribute(client->db, value_handle - 1);
+ if (!attr)
+ return NULL;
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ if (bt_uuid_cmp(&uuid, gatt_db_attribute_get_type(attr)))
+ return NULL;
+
+ if (!gatt_db_attribute_get_char_data(attr, NULL, NULL,
+ &properties, NULL))
+ return NULL;
+
+ /* Find the CCC characteristic */
+ ccc = NULL;
+ gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
+ if (!ccc)
+ return NULL;
+
+ chrc = new0(struct notify_chrc, 1);
+ if (!chrc)
+ return NULL;
+
+ chrc->reg_notify_queue = queue_new();
+ if (!chrc->reg_notify_queue) {
+ free(chrc);
+ return NULL;
+ }
+
+ chrc->value_handle = value_handle;
+ chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
+ chrc->properties = properties;
+
+ queue_push_tail(client->notify_chrcs, chrc);
+
+ return chrc;
+}
+
+static void notify_chrc_free(void *data)
+{
+ struct notify_chrc *chrc = data;
+
+ queue_destroy(chrc->reg_notify_queue, notify_data_unref);
+ free(chrc);
+}
+
static bool match_notify_data_id(const void *a, const void *b)
{
const struct notify_data *notify_data = a;
@@ -174,7 +237,7 @@ struct handle_range {
static bool match_notify_data_handle_range(const void *a, const void *b)
{
const struct notify_data *notify_data = a;
- bt_gatt_characteristic_t *chrc = ¬ify_data->chrc->chrc_external;
+ struct notify_chrc *chrc = notify_data->chrc;
const struct handle_range *range = b;
return chrc->value_handle >= range->start &&
@@ -184,7 +247,7 @@ static bool match_notify_data_handle_range(const void *a, const void *b)
static void mark_notify_data_invalid_if_in_range(void *data, void *user_data)
{
struct notify_data *notify_data = data;
- bt_gatt_characteristic_t *chrc = ¬ify_data->chrc->chrc_external;
+ struct notify_chrc *chrc = notify_data->chrc;
struct handle_range *range = user_data;
if (chrc->value_handle >= range->start &&
@@ -192,6 +255,15 @@ static void mark_notify_data_invalid_if_in_range(void *data, void *user_data)
notify_data->invalid = true;
}
+static bool match_notify_chrc_handle_range(const void *a, const void *b)
+{
+ const struct notify_chrc *chrc = a;
+ const struct handle_range *range = b;
+
+ return chrc->value_handle >= range->start &&
+ chrc->value_handle <= range->end;
+}
+
static void gatt_client_remove_all_notify_in_range(
struct bt_gatt_client *client,
uint16_t start_handle, uint16_t end_handle)
@@ -213,6 +285,19 @@ static void gatt_client_remove_all_notify_in_range(
&range, notify_data_unref);
}
+static void gatt_client_remove_notify_chrcs_in_range(
+ struct bt_gatt_client *client,
+ uint16_t start_handle, uint16_t end_handle)
+{
+ struct handle_range range;
+
+ range.start = start_handle;
+ range.end = end_handle;
+
+ queue_remove_all(client->notify_chrcs, match_notify_chrc_handle_range,
+ &range, notify_chrc_free);
+}
+
struct discovery_op;
typedef void (*discovery_op_complete_func_t)(struct discovery_op *op,
@@ -993,6 +1078,8 @@ static void process_service_changed(struct bt_gatt_client *client,
/* Invalidate and remove all effected notify callbacks */
gatt_client_remove_all_notify_in_range(client, start_handle,
end_handle);
+ gatt_client_remove_notify_chrcs_in_range(client, start_handle,
+ end_handle);
/* Remove all services that overlap the modified range since we'll
* rediscover them
@@ -1211,12 +1298,10 @@ static bool notify_data_write_ccc(struct notify_data *notify_data, bool enable,
/* Try to enable notifications and/or indications based on
* whatever the characteristic supports.
*/
- if (notify_data->chrc->chrc_external.properties &
- BT_GATT_CHRC_PROP_NOTIFY)
+ if (notify_data->chrc->properties & BT_GATT_CHRC_PROP_NOTIFY)
pdu[2] = 0x01;
- if (notify_data->chrc->chrc_external.properties &
- BT_GATT_CHRC_PROP_INDICATE)
+ if (notify_data->chrc->properties & BT_GATT_CHRC_PROP_INDICATE)
pdu[2] |= 0x02;
if (!pdu[2])
@@ -1327,7 +1412,7 @@ static void notify_handler(void *data, void *user_data)
value_handle = get_le16(pdu_data->pdu);
- if (notify_data->chrc->chrc_external.value_handle != value_handle)
+ if (notify_data->chrc->value_handle != value_handle)
return;
if (pdu_data->length > 2)
@@ -1393,6 +1478,7 @@ static void bt_gatt_client_free(struct bt_gatt_client *client)
queue_destroy(client->svc_chngd_queue, free);
queue_destroy(client->long_write_queue, long_write_op_unref);
queue_destroy(client->notify_list, notify_data_unref);
+ queue_destroy(client->notify_chrcs, notify_chrc_free);
free(client);
}
@@ -1444,6 +1530,10 @@ struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db,
if (!client->notify_list)
goto fail;
+ client->notify_chrcs = queue_new();
+ if (!client->notify_chrcs)
+ goto fail;
+
client->notify_id = bt_att_register(att, BT_ATT_OP_HANDLE_VAL_NOT,
notify_cb, client, NULL);
if (!client->notify_id)
@@ -2374,6 +2464,14 @@ bool bt_gatt_client_write_long_value(struct bt_gatt_client *client,
return true;
}
+static bool match_notify_chrc_value_handle(const void *a, const void *b)
+{
+ const struct notify_chrc *chrc = a;
+ uint16_t value_handle = PTR_TO_UINT(b);
+
+ return chrc->value_handle == value_handle;
+}
+
bool bt_gatt_client_register_notify(struct bt_gatt_client *client,
uint16_t chrc_value_handle,
bt_gatt_client_notify_id_callback_t callback,
@@ -2382,40 +2480,31 @@ bool bt_gatt_client_register_notify(struct bt_gatt_client *client,
bt_gatt_client_destroy_func_t destroy)
{
struct notify_data *notify_data;
- struct service_list *svc_data = NULL;
- struct chrc_data *chrc = NULL;
- struct bt_gatt_service_iter iter;
- const bt_gatt_service_t *service;
+ struct notify_chrc *chrc = NULL;
- if (!client || !chrc_value_handle || !callback)
+ if (!client || !client->db || !chrc_value_handle || !callback)
return false;
if (!bt_gatt_client_is_ready(client) || client->in_svc_chngd)
return false;
- /* Check that chrc_value_handle belongs to a known characteristic */
- if (!bt_gatt_service_iter_init(&iter, client))
- return false;
+ /* Check if a characteristic ref count has been started already */
+ chrc = queue_find(client->notify_chrcs, match_notify_chrc_value_handle,
+ UINT_TO_PTR(chrc_value_handle));
- while (bt_gatt_service_iter_next(&iter, &service)) {
- if (chrc_value_handle >= service->start_handle &&
- chrc_value_handle <= service->end_handle) {
- svc_data = (void *) service;
- break;
- }
- }
-
- if (!svc_data)
- return false;
+ if (!chrc) {
+ /*
+ * Create an entry if the characteristic is known and has a CCC
+ * descriptor.
+ */
+ chrc = notify_chrc_create(client, chrc_value_handle);
+ if (!chrc)
+ return false;
- /*
- * TODO: Lookup characteristic and CCC in database. Add entries for each
- * characteristic to a list on demand.
- */
- return false;
+ }
- /* Check that the characteristic supports notifications/indications */
- if (!chrc || !chrc->ccc_handle || chrc->notify_count == INT_MAX)
+ /* Fail if we've hit the maximum allowed notify sessions */
+ if (chrc->notify_count == INT_MAX)
return false;
notify_data = new0(struct notify_data, 1);
@@ -2430,7 +2519,8 @@ bool bt_gatt_client_register_notify(struct bt_gatt_client *client,
notify_data->user_data = user_data;
notify_data->destroy = destroy;
- /* If a write to the CCC descriptor is in progress, then queue this
+ /*
+ * If a write to the CCC descriptor is in progress, then queue this
* request.
*/
if (chrc->ccc_write_id) {
@@ -2438,7 +2528,8 @@ bool bt_gatt_client_register_notify(struct bt_gatt_client *client,
return true;
}
- /* If the ref count is not zero, then notifications are already enabled.
+ /*
+ * If the ref count is not zero, then notifications are already enabled.
*/
if (chrc->notify_count > 0) {
complete_notify_request(notify_data);
--
2.2.0.rc0.207.ga3a616c
This patch rewrites the service discovery logic inside
shared/gatt-client. The internal service_list structure has been
entirely removed and services are stored in a gatt_db instance.
Initially, gatt-client creates and owns the life-time of the gatt_db.
---
src/shared/gatt-client.c | 941 +++++++++++++++++++++--------------------------
src/shared/gatt-client.h | 4 +-
tools/btgatt-client.c | 13 +-
unit/test-gatt.c | 23 +-
4 files changed, 455 insertions(+), 526 deletions(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 033cba1..c891516 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -22,11 +22,12 @@
*/
#include "src/shared/att.h"
-#include "src/shared/gatt-client.h"
#include "lib/uuid.h"
#include "src/shared/gatt-helpers.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-client.h"
#include <assert.h>
#include <limits.h>
@@ -65,15 +66,6 @@ struct chrc_data {
unsigned int ccc_write_id;
};
-struct service_list {
- bt_gatt_service_t service;
- struct chrc_data *chrcs;
- size_t num_chrcs;
- bt_gatt_included_service_t *includes;
- size_t num_includes;
- struct service_list *next;
-};
-
struct bt_gatt_client {
struct bt_att *att;
int ref_count;
@@ -90,7 +82,7 @@ struct bt_gatt_client {
bt_gatt_client_destroy_func_t debug_destroy;
void *debug_data;
- struct service_list *svc_head, *svc_tail;
+ struct gatt_db *db;
bool in_init;
bool ready;
@@ -114,9 +106,6 @@ struct bt_gatt_client {
* value handle. These will have the value 0 if they are not present on
* the remote peripheral.
*/
- uint16_t gatt_svc_handle;
- uint16_t svc_chngd_val_handle;
- uint16_t svc_chngd_ccc_handle;
unsigned int svc_chngd_ind_id;
struct queue *svc_chngd_queue; /* Queued service changed events */
bool in_svc_chngd;
@@ -203,161 +192,6 @@ static void mark_notify_data_invalid_if_in_range(void *data, void *user_data)
notify_data->invalid = true;
}
-static struct service_list *new_service_list(uint16_t start, uint16_t end,
- bool primary,
- uint8_t uuid[BT_GATT_UUID_SIZE])
-{
- struct service_list *list;
-
- list = new0(struct service_list, 1);
- if (!list)
- return NULL;
-
- list->service.primary = primary;
- list->service.start_handle = start;
- list->service.end_handle = end;
- memcpy(list->service.uuid, uuid, UUID_BYTES);
-
- return list;
-}
-
-static bool service_list_add_service(struct service_list **head,
- struct service_list **tail,
- bool primary, uint16_t start,
- uint16_t end,
- uint8_t uuid[BT_GATT_UUID_SIZE])
-{
- struct service_list *list;
-
- list = new_service_list(start, end, primary, uuid);
- if (!list)
- return false;
-
- if (!(*head))
- *head = *tail = list;
- else {
- (*tail)->next = list;
- *tail = list;
- }
-
- return true;
-}
-
-static void service_destroy_characteristics(struct service_list *service)
-{
- unsigned int i;
-
- for (i = 0; i < service->num_chrcs; i++) {
- free(service->chrcs[i].descs);
- queue_destroy(service->chrcs[i].reg_notify_queue,
- notify_data_unref);
- }
-
- free(service->chrcs);
-}
-
-static void service_destroy_includes(struct service_list *service)
-{
- free(service->includes);
-
- service->includes = NULL;
- service->num_includes = 0;
-}
-
-static void service_list_clear(struct service_list **head,
- struct service_list **tail)
-{
- struct service_list *l, *tmp;
-
- if (!(*head) || !(*tail))
- return;
-
- l = *head;
-
- while (l) {
- service_destroy_characteristics(l);
- service_destroy_includes(l);
- tmp = l;
- l = tmp->next;
- free(tmp);
- }
-
- *head = *tail = NULL;
-}
-
-static void service_list_clear_range(struct service_list **head,
- struct service_list **tail,
- uint16_t start, uint16_t end)
-{
- struct service_list *cur, *prev, *tmp;
-
- if (!(*head) || !(*tail))
- return;
-
- prev = NULL;
- cur = *head;
- while (cur) {
- if (cur->service.end_handle < start ||
- cur->service.start_handle > end) {
- prev = cur;
- cur = cur->next;
- continue;
- }
-
- service_destroy_characteristics(cur);
- service_destroy_includes(cur);
-
- if (!prev)
- *head = cur->next;
- else
- prev->next = cur->next;
-
- if (*tail == cur)
- *tail = prev;
-
- tmp = cur;
- cur = cur->next;
- free(tmp);
- }
-}
-
-static void service_list_insert_services(struct service_list **head,
- struct service_list **tail,
- struct service_list *svc_head,
- struct service_list *svc_tail)
-{
- struct service_list *cur, *prev;
-
- if (!(*head) || !(*tail)) {
- *head = svc_head;
- *tail = svc_tail;
- return;
- }
-
- prev = NULL;
- cur = *head;
- while (cur) {
- if (svc_tail->service.end_handle < cur->service.start_handle) {
- if (!prev)
- *head = svc_head;
- else
- prev->next = svc_head;
-
- svc_tail->next = cur;
- return;
- }
-
- prev = cur;
- cur = cur->next;
- }
-
- if (prev != *tail)
- return;
-
- prev->next = svc_head;
- *tail = svc_tail;
-}
-
static void gatt_client_remove_all_notify_in_range(
struct bt_gatt_client *client,
uint16_t start_handle, uint16_t end_handle)
@@ -379,25 +213,71 @@ static void gatt_client_remove_all_notify_in_range(
&range, notify_data_unref);
}
-static void gatt_client_clear_services(struct bt_gatt_client *client)
-{
+struct discovery_op;
- gatt_client_remove_all_notify_in_range(client, 0x0001, 0xffff);
- service_list_clear(&client->svc_head, &client->svc_tail);
-}
+typedef void (*discovery_op_complete_func_t)(struct discovery_op *op,
+ bool success,
+ uint8_t att_ecode);
+typedef void (*discovery_op_fail_func_t)(struct discovery_op *op);
struct discovery_op {
struct bt_gatt_client *client;
- struct service_list *result_head, *result_tail, *cur_service;
- struct chrc_data *cur_chrc;
+ struct queue *pending_svcs;
+ struct queue *pending_chrcs;
+ struct queue *tmp_queue;
+ struct gatt_db_attribute *cur_svc;
+ bool success;
uint16_t start;
uint16_t end;
- int cur_chrc_index;
int ref_count;
- void (*complete_func)(struct discovery_op *op, bool success,
- uint8_t att_ecode);
+ discovery_op_complete_func_t complete_func;
+ discovery_op_fail_func_t failure_func;
};
+static void discovery_op_free(struct discovery_op *op)
+{
+ queue_destroy(op->pending_svcs, NULL);
+ queue_destroy(op->pending_chrcs, free);
+ queue_destroy(op->tmp_queue, NULL);
+ free(op);
+}
+
+static struct discovery_op *discovery_op_create(struct bt_gatt_client *client,
+ uint16_t start, uint16_t end,
+ discovery_op_complete_func_t complete_func,
+ discovery_op_fail_func_t failure_func)
+{
+ struct discovery_op *op;
+
+ op = new0(struct discovery_op, 1);
+ if (!op)
+ return NULL;
+
+ op->pending_svcs = queue_new();
+ if (!op->pending_svcs)
+ goto fail;
+
+ op->pending_chrcs = queue_new();
+ if (!op->pending_chrcs)
+ goto fail;
+
+ op->tmp_queue = queue_new();
+ if (!op->tmp_queue)
+ goto fail;
+
+ op->client = client;
+ op->complete_func = complete_func;
+ op->failure_func = failure_func;
+ op->start = start;
+ op->end = end;
+
+ return op;
+
+fail:
+ discovery_op_free(op);
+ return NULL;
+}
+
static struct discovery_op *discovery_op_ref(struct discovery_op *op)
{
__sync_fetch_and_add(&op->ref_count, 1);
@@ -412,45 +292,27 @@ static void discovery_op_unref(void *data)
if (__sync_sub_and_fetch(&op->ref_count, 1))
return;
- service_list_clear(&op->result_head, &op->result_tail);
-
- free(data);
-}
-
-static void uuid_to_string(const uint8_t uuid[BT_GATT_UUID_SIZE],
- char str[MAX_LEN_UUID_STR])
-{
- bt_uuid_t tmp;
+ if (!op->success)
+ op->failure_func(op);
- tmp.type = BT_UUID128;
- memcpy(tmp.value.u128.data, uuid, UUID_BYTES);
- bt_uuid_to_string(&tmp, str, MAX_LEN_UUID_STR * sizeof(char));
+ discovery_op_free(op);
}
static void discover_chrcs_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data);
-static int uuid_cmp(const uint8_t uuid128[16], uint16_t uuid16)
-{
- uint8_t rhs_uuid[16] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
- 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
- };
-
- put_be16(uuid16, rhs_uuid + 2);
-
- return memcmp(uuid128, rhs_uuid, sizeof(rhs_uuid));
-}
-
static void discover_incl_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result, void *user_data)
{
struct discovery_op *op = user_data;
struct bt_gatt_client *client = op->client;
struct bt_gatt_iter iter;
+ struct gatt_db_attribute *attr, *tmp;
+ uint16_t handle, start, end;
+ uint128_t u128;
+ bt_uuid_t uuid;
char uuid_str[MAX_LEN_UUID_STR];
- bt_gatt_included_service_t *includes;
unsigned int includes_count, i;
if (!success) {
@@ -460,6 +322,11 @@ static void discover_incl_cb(bool success, uint8_t att_ecode,
goto failed;
}
+ /* Get the currently processed service */
+ attr = op->cur_svc;
+ if (!attr)
+ goto failed;
+
if (!result || !bt_gatt_iter_init(&iter, result))
goto failed;
@@ -467,42 +334,68 @@ static void discover_incl_cb(bool success, uint8_t att_ecode,
if (includes_count == 0)
goto failed;
- includes = new0(bt_gatt_included_service_t, includes_count);
- if (!includes)
- goto failed;
-
util_debug(client->debug_callback, client->debug_data,
"Included services found: %u",
includes_count);
for (i = 0; i < includes_count; i++) {
- if (!bt_gatt_iter_next_included_service(&iter,
- &includes[i].handle,
- &includes[i].start_handle,
- &includes[i].end_handle,
- includes[i].uuid))
+ if (!bt_gatt_iter_next_included_service(&iter, &handle, &start,
+ &end, u128.data))
break;
- uuid_to_string(includes[i].uuid, uuid_str);
+ bt_uuid128_create(&uuid, u128);
+
+ /* Log debug message */
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
util_debug(client->debug_callback, client->debug_data,
"handle: 0x%04x, start: 0x%04x, end: 0x%04x,"
- "uuid: %s", includes[i].handle,
- includes[i].start_handle,
- includes[i].end_handle, uuid_str);
- }
+ "uuid: %s", handle, start, end, uuid_str);
+
+ tmp = gatt_db_get_attribute(client->db, start);
+ if (!tmp)
+ goto failed;
- op->cur_service->includes = includes;
- op->cur_service->num_includes = includes_count;
+ tmp = gatt_db_service_add_included(attr, tmp);
+ if (!tmp)
+ goto failed;
+
+ /*
+ * GATT requires that all include definitions precede
+ * characteristic declarations. Based on the order we're adding
+ * these entries, the correct handle must be assigned to the new
+ * attribute.
+ */
+ if (gatt_db_attribute_get_handle(tmp) != handle)
+ goto failed;
+ }
next:
- if (!op->cur_service->next) {
- op->cur_service = op->result_head;
+ /* Move on to the next service */
+ attr = queue_pop_head(op->pending_svcs);
+ if (!attr) {
+ struct queue *tmp_queue;
+
+ tmp_queue = op->pending_svcs;
+ op->pending_svcs = op->tmp_queue;
+ op->tmp_queue = tmp_queue;
+
+ /*
+ * We have processed all include definitions. Move on to
+ * characteristics.
+ */
+ attr = queue_pop_head(op->pending_svcs);
+ if (!attr)
+ goto failed;
+
+ if (!gatt_db_attribute_get_service_handles(attr, &start, &end))
+ goto failed;
+
+ op->cur_svc = attr;
if (bt_gatt_discover_characteristics(client->att,
- op->cur_service->service.start_handle,
- op->cur_service->service.end_handle,
- discover_chrcs_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ start, end,
+ discover_chrcs_cb,
+ discovery_op_ref(op),
+ discovery_op_unref))
return;
util_debug(client->debug_callback, client->debug_data,
@@ -511,13 +404,15 @@ next:
goto failed;
}
- op->cur_service = op->cur_service->next;
- if (bt_gatt_discover_included_services(client->att,
- op->cur_service->service.start_handle,
- op->cur_service->service.end_handle,
- discover_incl_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ queue_push_tail(op->tmp_queue, attr);
+ op->cur_svc = attr;
+ if (!gatt_db_attribute_get_service_handles(attr, &start, &end))
+ goto failed;
+
+ if (bt_gatt_discover_included_services(client->att, start, end,
+ discover_incl_cb,
+ discovery_op_ref(op),
+ discovery_op_unref))
return;
util_debug(client->debug_callback, client->debug_data,
@@ -525,9 +420,74 @@ next:
discovery_op_unref(op);
failed:
+ op->success = false;
op->complete_func(op, false, att_ecode);
}
+struct chrc {
+ uint16_t start_handle;
+ uint16_t end_handle;
+ uint16_t value_handle;
+ uint8_t properties;
+ bt_uuid_t uuid;
+};
+
+static void discover_descs_cb(bool success, uint8_t att_ecode,
+ struct bt_gatt_result *result,
+ void *user_data);
+
+static bool discover_descs(struct discovery_op *op, bool *discovering)
+{
+ struct bt_gatt_client *client = op->client;
+ struct gatt_db_attribute *attr;
+ struct chrc *chrc_data;
+ uint16_t desc_start;
+
+ *discovering = false;
+
+ while ((chrc_data = queue_pop_head(op->pending_chrcs))) {
+ attr = gatt_db_service_add_characteristic(op->cur_svc,
+ &chrc_data->uuid, 0,
+ chrc_data->properties,
+ NULL, NULL, NULL);
+
+ if (!attr)
+ goto failed;
+
+ if (gatt_db_attribute_get_handle(attr) !=
+ chrc_data->value_handle)
+ goto failed;
+
+ desc_start = chrc_data->value_handle + 1;
+
+ if (desc_start > chrc_data->end_handle)
+ continue;
+
+ if (bt_gatt_discover_descriptors(client->att, desc_start,
+ chrc_data->end_handle,
+ discover_descs_cb,
+ discovery_op_ref(op),
+ discovery_op_unref)) {
+ *discovering = true;
+ goto done;
+ }
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Failed to start descriptor discovery");
+ discovery_op_unref(op);
+
+ goto failed;
+ }
+
+done:
+ free(chrc_data);
+ return true;
+
+failed:
+ free(chrc_data);
+ return false;
+}
+
static void discover_descs_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data)
@@ -535,11 +495,13 @@ static void discover_descs_cb(bool success, uint8_t att_ecode,
struct discovery_op *op = user_data;
struct bt_gatt_client *client = op->client;
struct bt_gatt_iter iter;
+ struct gatt_db_attribute *attr;
+ uint16_t handle, start, end;
+ uint128_t u128;
+ bt_uuid_t uuid;
char uuid_str[MAX_LEN_UUID_STR];
unsigned int desc_count;
- uint16_t desc_start;
- unsigned int i;
- bt_gatt_descriptor_t *descs;
+ bool discovering;
if (!success) {
if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
@@ -550,94 +512,71 @@ static void discover_descs_cb(bool success, uint8_t att_ecode,
goto done;
}
- if (!result || !bt_gatt_iter_init(&iter, result)) {
- success = false;
- goto done;
- }
+ if (!result || !bt_gatt_iter_init(&iter, result))
+ goto failed;
desc_count = bt_gatt_result_descriptor_count(result);
- if (desc_count == 0) {
- success = false;
- goto done;
- }
+ if (desc_count == 0)
+ goto failed;
util_debug(client->debug_callback, client->debug_data,
"Descriptors found: %u", desc_count);
- descs = new0(bt_gatt_descriptor_t, desc_count);
- if (!descs) {
- success = false;
- goto done;
- }
+ while (bt_gatt_iter_next_descriptor(&iter, &handle, u128.data)) {
+ bt_uuid128_create(&uuid, u128);
- i = 0;
- while (bt_gatt_iter_next_descriptor(&iter, &descs[i].handle,
- descs[i].uuid)) {
- uuid_to_string(descs[i].uuid, uuid_str);
+ /* Log debug message */
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
util_debug(client->debug_callback, client->debug_data,
"handle: 0x%04x, uuid: %s",
- descs[i].handle, uuid_str);
+ handle, uuid_str);
- if (uuid_cmp(descs[i].uuid, GATT_CLIENT_CHARAC_CFG_UUID) == 0) {
- op->cur_chrc->ccc_handle = descs[i].handle;
-
- if (uuid_cmp(op->cur_chrc->chrc_external.uuid,
- SVC_CHNGD_UUID) == 0)
- client->svc_chngd_ccc_handle = descs[i].handle;
- }
+ attr = gatt_db_service_add_descriptor(op->cur_svc, &uuid, 0,
+ NULL, NULL, NULL);
+ if (!attr)
+ goto failed;
- i++;
+ if (gatt_db_attribute_get_handle(attr) != handle)
+ goto failed;
}
- op->cur_chrc->chrc_external.num_descs = desc_count;
- op->cur_chrc->descs = descs;
- op->cur_chrc->chrc_external.descs = descs;
-
- for (i = op->cur_chrc_index + 1; i < op->cur_service->num_chrcs; i++) {
- op->cur_chrc_index = i;
- op->cur_chrc++;
- desc_start = op->cur_chrc->chrc_external.value_handle + 1;
- if (desc_start > op->cur_chrc->chrc_external.end_handle)
- continue;
+ if (!discover_descs(op, &discovering))
+ goto failed;
- if (bt_gatt_discover_descriptors(client->att, desc_start,
- op->cur_chrc->chrc_external.end_handle,
- discover_descs_cb, discovery_op_ref(op),
- discovery_op_unref))
- return;
+ if (discovering)
+ return;
- util_debug(client->debug_callback, client->debug_data,
- "Failed to start descriptor discovery");
- discovery_op_unref(op);
- success = false;
+next:
+ /* Done with the current service */
+ gatt_db_service_set_active(op->cur_svc, true);
+ attr = queue_pop_head(op->pending_svcs);
+ if (!attr)
goto done;
- }
-next:
- if (!op->cur_service->next)
- goto done;
+ if (!gatt_db_attribute_get_service_handles(attr, &start, &end))
+ goto failed;
/* Move on to the next service */
- op->cur_service = op->cur_service->next;
- if (bt_gatt_discover_characteristics(client->att,
- op->cur_service->service.start_handle,
- op->cur_service->service.end_handle,
- discover_chrcs_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ op->cur_svc = attr;
+ if (bt_gatt_discover_characteristics(client->att, start, end,
+ discover_chrcs_cb,
+ discovery_op_ref(op),
+ discovery_op_unref))
return;
util_debug(client->debug_callback, client->debug_data,
"Failed to start characteristic discovery");
discovery_op_unref(op);
+
+failed:
success = false;
done:
+ op->success = success;
op->complete_func(op, success, att_ecode);
}
-
static void discover_chrcs_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data)
@@ -645,11 +584,15 @@ static void discover_chrcs_cb(bool success, uint8_t att_ecode,
struct discovery_op *op = user_data;
struct bt_gatt_client *client = op->client;
struct bt_gatt_iter iter;
+ struct gatt_db_attribute *attr;
+ struct chrc *chrc_data;
+ uint16_t start, end, value;
+ uint8_t properties;
+ uint128_t u128;
+ bt_uuid_t uuid;
char uuid_str[MAX_LEN_UUID_STR];
unsigned int chrc_count;
- unsigned int i;
- uint16_t desc_start;
- struct chrc_data *chrcs;
+ bool discovering;
if (!success) {
if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
@@ -660,98 +603,78 @@ static void discover_chrcs_cb(bool success, uint8_t att_ecode,
goto done;
}
- if (!result || !bt_gatt_iter_init(&iter, result)) {
- success = false;
- goto done;
- }
+ if (!op->cur_svc || !result || !bt_gatt_iter_init(&iter, result))
+ goto failed;
chrc_count = bt_gatt_result_characteristic_count(result);
util_debug(client->debug_callback, client->debug_data,
"Characteristics found: %u", chrc_count);
if (chrc_count == 0)
- goto next;
+ goto failed;
- chrcs = new0(struct chrc_data, chrc_count);
- if (!chrcs) {
- success = false;
- goto done;
- }
+ while (bt_gatt_iter_next_characteristic(&iter, &start, &end, &value,
+ &properties, u128.data)) {
+ bt_uuid128_create(&uuid, u128);
- i = 0;
- while (bt_gatt_iter_next_characteristic(&iter,
- &chrcs[i].chrc_external.start_handle,
- &chrcs[i].chrc_external.end_handle,
- &chrcs[i].chrc_external.value_handle,
- &chrcs[i].chrc_external.properties,
- chrcs[i].chrc_external.uuid)) {
- uuid_to_string(chrcs[i].chrc_external.uuid, uuid_str);
+ /* Log debug message */
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
util_debug(client->debug_callback, client->debug_data,
"start: 0x%04x, end: 0x%04x, value: 0x%04x, "
"props: 0x%02x, uuid: %s",
- chrcs[i].chrc_external.start_handle,
- chrcs[i].chrc_external.end_handle,
- chrcs[i].chrc_external.value_handle,
- chrcs[i].chrc_external.properties,
- uuid_str);
-
- chrcs[i].reg_notify_queue = queue_new();
- if (!chrcs[i].reg_notify_queue) {
- success = false;
- goto done;
- }
+ start, end, value, properties, uuid_str);
- if (uuid_cmp(chrcs[i].chrc_external.uuid, SVC_CHNGD_UUID) == 0)
- client->svc_chngd_val_handle =
- chrcs[i].chrc_external.value_handle;
+ chrc_data = new0(struct chrc, 1);
+ if (!chrc_data)
+ goto failed;
- i++;
- }
+ chrc_data->start_handle = start;
+ chrc_data->end_handle = end;
+ chrc_data->value_handle = value;
+ chrc_data->properties = properties;
+ chrc_data->uuid = uuid;
- op->cur_service->chrcs = chrcs;
- op->cur_service->num_chrcs = chrc_count;
+ queue_push_tail(op->pending_chrcs, chrc_data);
+ }
- for (i = 0; i < chrc_count; i++) {
- op->cur_chrc_index = i;
- op->cur_chrc = chrcs + i;
- desc_start = chrcs[i].chrc_external.value_handle;
- if (desc_start++ == chrcs[i].chrc_external.end_handle)
- continue;
+ /*
+ * Sequentially discover descriptors for each characteristic and insert
+ * the characteristics into the database as we proceed.
+ */
+ if (!discover_descs(op, &discovering))
+ goto failed;
- if (bt_gatt_discover_descriptors(client->att, desc_start,
- chrcs[i].chrc_external.end_handle,
- discover_descs_cb, discovery_op_ref(op),
- discovery_op_unref))
- return;
+ if (discovering)
+ return;
- util_debug(client->debug_callback, client->debug_data,
- "Failed to start descriptor discovery");
- discovery_op_unref(op);
- success = false;
+next:
+ /* Done with the current service */
+ gatt_db_service_set_active(op->cur_svc, true);
+ attr = queue_pop_head(op->pending_svcs);
+ if (!attr)
goto done;
- }
-next:
- if (!op->cur_service->next)
- goto done;
+ if (!gatt_db_attribute_get_service_handles(attr, &start, &end))
+ goto failed;
/* Move on to the next service */
- op->cur_service = op->cur_service->next;
- if (bt_gatt_discover_characteristics(client->att,
- op->cur_service->service.start_handle,
- op->cur_service->service.end_handle,
- discover_chrcs_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ op->cur_svc = attr;
+ if (bt_gatt_discover_characteristics(client->att, start, end,
+ discover_chrcs_cb,
+ discovery_op_ref(op),
+ discovery_op_unref))
return;
util_debug(client->debug_callback, client->debug_data,
"Failed to start characteristic discovery");
discovery_op_unref(op);
+
+failed:
success = false;
done:
+ op->success = success;
op->complete_func(op, success, att_ecode);
}
@@ -762,10 +685,11 @@ static void discover_secondary_cb(bool success, uint8_t att_ecode,
struct discovery_op *op = user_data;
struct bt_gatt_client *client = op->client;
struct bt_gatt_iter iter;
+ struct gatt_db_attribute *attr;
uint16_t start, end;
- uint8_t uuid[BT_GATT_UUID_SIZE];
+ uint128_t u128;
+ bt_uuid_t uuid;
char uuid_str[MAX_LEN_UUID_STR];
- struct service_list *service;
if (!success) {
util_debug(client->debug_callback, client->debug_data,
@@ -780,40 +704,61 @@ static void discover_secondary_cb(bool success, uint8_t att_ecode,
}
}
- if (!result || !bt_gatt_iter_init(&iter, result))
+ if (!result || !bt_gatt_iter_init(&iter, result)) {
+ success = false;
goto done;
+ }
util_debug(client->debug_callback, client->debug_data,
"Secondary services found: %u",
bt_gatt_result_service_count(result));
- while (bt_gatt_iter_next_service(&iter, &start, &end, uuid)) {
- uuid_to_string(uuid, uuid_str);
+ while (bt_gatt_iter_next_service(&iter, &start, &end, u128.data)) {
+ bt_uuid128_create(&uuid, u128);
+
+ /* Log debug message */
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
util_debug(client->debug_callback, client->debug_data,
"start: 0x%04x, end: 0x%04x, uuid: %s",
start, end, uuid_str);
/* Store the service */
- service = new_service_list(start, end, false, uuid);
- if (!service) {
+ attr = gatt_db_insert_service(client->db, start, &uuid, false,
+ end - start + 1);
+ if (!attr) {
util_debug(client->debug_callback, client->debug_data,
"Failed to create service");
+ success = false;
goto done;
}
- service_list_insert_services(&op->result_head, &op->result_tail,
- service, service);
+ queue_push_tail(op->pending_svcs, attr);
}
next:
/* Sequentially discover included services */
- op->cur_service = op->result_head;
- if (bt_gatt_discover_included_services(client->att,
- op->cur_service->service.start_handle,
- op->cur_service->service.end_handle,
- discover_incl_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ attr = queue_pop_head(op->pending_svcs);
+
+ /* Complete with success if queue is empty */
+ if (!attr)
+ goto done;
+
+ /*
+ * Store the service in the tmp queue to be reused during
+ * characteristics discovery later.
+ */
+ queue_push_tail(op->tmp_queue, attr);
+ op->cur_svc = attr;
+
+ if (!gatt_db_attribute_get_service_handles(attr, &start, &end)) {
+ success = false;
+ goto done;
+ }
+
+ if (bt_gatt_discover_included_services(client->att, start, end,
+ discover_incl_cb,
+ discovery_op_ref(op),
+ discovery_op_unref))
return;
util_debug(client->debug_callback, client->debug_data,
@@ -821,7 +766,8 @@ next:
discovery_op_unref(op);
done:
- op->complete_func(op, false, att_ecode);
+ op->success = success;
+ op->complete_func(op, success, att_ecode);
}
static void discover_primary_cb(bool success, uint8_t att_ecode,
@@ -831,8 +777,10 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
struct discovery_op *op = user_data;
struct bt_gatt_client *client = op->client;
struct bt_gatt_iter iter;
+ struct gatt_db_attribute *attr;
uint16_t start, end;
- uint8_t uuid[BT_GATT_UUID_SIZE];
+ uint128_t u128;
+ bt_uuid_t uuid;
char uuid_str[MAX_LEN_UUID_STR];
if (!success) {
@@ -851,33 +799,28 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
"Primary services found: %u",
bt_gatt_result_service_count(result));
- while (bt_gatt_iter_next_service(&iter, &start, &end, uuid)) {
+ while (bt_gatt_iter_next_service(&iter, &start, &end, u128.data)) {
+ bt_uuid128_create(&uuid, u128);
+
/* Log debug message. */
- uuid_to_string(uuid, uuid_str);
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
util_debug(client->debug_callback, client->debug_data,
"start: 0x%04x, end: 0x%04x, uuid: %s",
start, end, uuid_str);
- /* Store the service */
- if (!service_list_add_service(&op->result_head,
- &op->result_tail, true, start, end,
- uuid)) {
+ attr = gatt_db_insert_service(client->db, start, &uuid, true,
+ end - start + 1);
+ if (!attr) {
util_debug(client->debug_callback, client->debug_data,
"Failed to store service");
success = false;
goto done;
}
- if (uuid_cmp(uuid, GATT_SVC_UUID) == 0)
- client->gatt_svc_handle = start;
+ queue_push_tail(op->pending_svcs, attr);
}
- /* Complete the process if the service list is empty */
- if (!op->result_head)
- goto done;
-
/* Discover secondary services */
- op->cur_service = op->result_head;
if (bt_gatt_discover_secondary_services(client->att, NULL,
op->start, op->end,
discover_secondary_cb,
@@ -891,6 +834,7 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
success = false;
done:
+ op->success = success;
op->complete_func(op, success, att_ecode);
}
@@ -917,6 +861,12 @@ static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
"MTU exchange complete, with MTU: %u",
bt_att_get_mtu(client->att));
+ /* Don't do discovery if the database was pre-populated */
+ if (!gatt_db_isempty(client->db)) {
+ op->complete_func(op, true, 0);
+ return;
+ }
+
if (bt_gatt_discover_all_primary_services(client->att, NULL,
discover_primary_cb,
discovery_op_ref(op),
@@ -929,7 +879,7 @@ static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
client->in_init = false;
if (client->ready_callback)
- client->ready_callback(success, att_ecode, client->ready_data);
+ client->ready_callback(false, att_ecode, client->ready_data);
discovery_op_unref(op);
}
@@ -971,7 +921,9 @@ static void service_changed_complete(struct discovery_op *op, bool success,
struct service_changed_op *next_sc_op;
uint16_t start_handle = op->start;
uint16_t end_handle = op->end;
- bool services_found = false;
+ struct gatt_db_attribute *attr;
+ bt_uuid_t uuid;
+ struct queue *q;
client->in_svc_chngd = false;
@@ -979,25 +931,10 @@ static void service_changed_complete(struct discovery_op *op, bool success,
util_debug(client->debug_callback, client->debug_data,
"Failed to discover services within changed range - "
"error: 0x%02x", att_ecode);
- goto next;
- }
- /* No new services in the modified range */
- if (!op->result_head || !op->result_tail)
- goto next;
-
- services_found = true;
-
- /* Insert all newly discovered services in their correct place as a
- * contiguous chunk */
- service_list_insert_services(&client->svc_head, &client->svc_tail,
- op->result_head, op->result_tail);
-
- /* Relinquish ownership of services, as the client now owns them */
- op->result_head = NULL;
- op->result_tail = NULL;
+ gatt_db_clear_range(client->db, start_handle, end_handle);
+ }
-next:
/* Notify the upper layer of changed services */
if (client->svc_chngd_callback)
client->svc_chngd_callback(start_handle, end_handle,
@@ -1012,26 +949,41 @@ next:
return;
}
- /* Check if the GATT service is not present or has remained unchanged */
- if (!services_found || !client->svc_chngd_val_handle ||
- client->svc_chngd_val_handle < start_handle ||
- client->svc_chngd_val_handle > end_handle)
+ /* Check if the GATT service was among the changed services */
+ q = queue_new();
+ if (!q)
return;
+ bt_uuid16_create(&uuid, SVC_CHNGD_UUID);
+
+ gatt_db_find_by_type(client->db, start_handle, end_handle, &uuid, q);
+ if (queue_isempty(q)) {
+ queue_destroy(q, NULL);
+ return;
+ }
+
+ attr = queue_pop_head(q);
+ queue_destroy(q, NULL);
+
/* The GATT service was modified. Re-register the handler for
* indications from the "Service Changed" characteristic.
*/
if (bt_gatt_client_register_notify(client,
- client->svc_chngd_val_handle,
- service_changed_reregister_cb,
- service_changed_cb,
- client, NULL))
+ gatt_db_attribute_get_handle(attr),
+ service_changed_reregister_cb,
+ service_changed_cb,
+ client, NULL))
return;
util_debug(client->debug_callback, client->debug_data,
"Failed to re-register handler for \"Service Changed\"");
}
+static void service_changed_failure(struct discovery_op *op)
+{
+ gatt_db_clear_range(op->client->db, op->start, op->end);
+}
+
static void process_service_changed(struct bt_gatt_client *client,
uint16_t start_handle,
uint16_t end_handle)
@@ -1045,42 +997,28 @@ static void process_service_changed(struct bt_gatt_client *client,
/* Remove all services that overlap the modified range since we'll
* rediscover them
*/
- service_list_clear_range(&client->svc_head, &client->svc_tail,
- start_handle, end_handle);
+ gatt_db_clear_range(client->db, start_handle, end_handle);
- op = new0(struct discovery_op, 1);
- if (!op) {
- util_debug(client->debug_callback, client->debug_data,
- "Failed to initiate primary service discovery"
- " after Service Changed");
- return;
- }
-
- if (client->gatt_svc_handle >= start_handle &&
- client->gatt_svc_handle <= end_handle) {
- client->gatt_svc_handle = 0;
- client->svc_chngd_val_handle = 0;
- client->svc_chngd_ind_id = 0;
- }
-
- op->client = client;
- op->complete_func = service_changed_complete;
- op->start = start_handle;
- op->end = end_handle;
+ op = discovery_op_create(client, start_handle, end_handle,
+ service_changed_complete,
+ service_changed_failure);
+ if (!op)
+ goto fail;
- if (!bt_gatt_discover_primary_services(client->att, NULL,
+ if (bt_gatt_discover_primary_services(client->att, NULL,
start_handle, end_handle,
discover_primary_cb,
discovery_op_ref(op),
discovery_op_unref)) {
- util_debug(client->debug_callback, client->debug_data,
- "Failed to initiate primary service discovery"
- " after Service Changed");
- free(op);
+ client->in_svc_chngd = true;
return;
}
- client->in_svc_chngd = true;
+fail:
+ util_debug(client->debug_callback, client->debug_data,
+ "Failed to initiate service discovery"
+ " after Service Changed");
+ discovery_op_free(op);
}
static void service_changed_cb(uint16_t value_handle, const uint8_t *value,
@@ -1090,7 +1028,7 @@ static void service_changed_cb(uint16_t value_handle, const uint8_t *value,
struct service_changed_op *op;
uint16_t start, end;
- if (value_handle != client->svc_chngd_val_handle || length != 4)
+ if (length != 4)
return;
start = get_le16(value);
@@ -1150,24 +1088,31 @@ static void init_complete(struct discovery_op *op, bool success,
{
struct bt_gatt_client *client = op->client;
bool registered;
+ struct gatt_db_attribute *attr;
+ bt_uuid_t uuid;
+ struct queue *q;
client->in_init = false;
if (!success)
goto fail;
- client->svc_head = op->result_head;
- client->svc_tail = op->result_tail;
+ q = queue_new();
+ if (!q)
+ goto fail;
- /* Relinquish ownership of services, as the client now owns them */
- op->result_head = NULL;
- op->result_tail = NULL;
+ bt_uuid16_create(&uuid, SVC_CHNGD_UUID);
- if (!client->svc_chngd_val_handle || !client->svc_chngd_ccc_handle) {
+ gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid, q);
+ if (queue_isempty(q)) {
+ queue_destroy(q, NULL);
client->ready = true;
goto done;
}
+ attr = queue_pop_head(q);
+ queue_destroy(q, NULL);
+
/* Register an indication handler for the "Service Changed"
* characteristic and report ready only if the handler is registered
* successfully. Temporarily set "ready" to true so that we can register
@@ -1175,10 +1120,10 @@ static void init_complete(struct discovery_op *op, bool success,
*/
client->ready = true;
registered = bt_gatt_client_register_notify(client,
- client->svc_chngd_val_handle,
- service_changed_register_cb,
- service_changed_cb,
- client, NULL);
+ gatt_db_attribute_get_handle(attr),
+ service_changed_register_cb,
+ service_changed_cb,
+ client, NULL);
client->ready = false;
if (registered)
@@ -1190,13 +1135,17 @@ static void init_complete(struct discovery_op *op, bool success,
fail:
util_debug(client->debug_callback, client->debug_data,
"Failed to initialize gatt-client");
- service_list_clear(&client->svc_head, &client->svc_head);
done:
if (client->ready_callback)
client->ready_callback(success, att_ecode, client->ready_data);
}
+static void init_fail(struct discovery_op *op)
+{
+ gatt_db_clear(op->client->db);
+}
+
static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
{
struct discovery_op *op;
@@ -1204,21 +1153,17 @@ static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
if (client->in_init || client->ready)
return false;
- op = new0(struct discovery_op, 1);
+ op = discovery_op_create(client, 0x0001, 0xffff, init_complete,
+ init_fail);
if (!op)
return false;
- op->client = client;
- op->complete_func = init_complete;
- op->start = 0x0001;
- op->end = 0xffff;
-
/* Configure the MTU */
if (!bt_gatt_exchange_mtu(client->att, MAX(BT_ATT_DEFAULT_LE_MTU, mtu),
exchange_mtu_cb,
discovery_op_ref(op),
discovery_op_unref)) {
- free(op);
+ discovery_op_free(op);
return false;
}
@@ -1443,7 +1388,7 @@ static void bt_gatt_client_free(struct bt_gatt_client *client)
bt_att_unref(client->att);
}
- gatt_client_clear_services(client);
+ gatt_db_unref(client->db);
queue_destroy(client->svc_chngd_queue, free);
queue_destroy(client->long_write_queue, long_write_op_unref);
@@ -1469,11 +1414,13 @@ static void att_disconnect_cb(void *user_data)
client->ready_callback(false, 0, client->ready_data);
}
-struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu)
+struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db,
+ struct bt_att *att,
+ uint16_t mtu)
{
struct bt_gatt_client *client;
- if (!att)
+ if (!att || !db)
return NULL;
client = new0(struct bt_gatt_client, 1);
@@ -1508,6 +1455,7 @@ struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu)
goto fail;
client->att = bt_att_ref(att);
+ client->db = gatt_db_ref(db);
if (!gatt_client_init(client, mtu))
goto fail;
@@ -1617,25 +1565,9 @@ bool bt_gatt_service_iter_init(struct bt_gatt_service_iter *iter,
bool bt_gatt_service_iter_next(struct bt_gatt_service_iter *iter,
const bt_gatt_service_t **service)
{
- struct service_list *l;
+ /* TODO: Remove iterator functions */
- if (!iter || !service)
- return false;
-
- l = iter->ptr;
-
- if (!l)
- l = iter->client->svc_head;
- else
- l = l->next;
-
- if (!l)
- return false;
-
- *service = &l->service;
- iter->ptr = l;
-
- return true;
+ return false;
}
bool bt_gatt_service_iter_next_by_handle(struct bt_gatt_service_iter *iter,
@@ -1677,19 +1609,9 @@ bool bt_gatt_characteristic_iter_init(struct bt_gatt_characteristic_iter *iter,
bool bt_gatt_characteristic_iter_next(struct bt_gatt_characteristic_iter *iter,
const bt_gatt_characteristic_t **chrc)
{
- struct service_list *service;
+ /* TODO: Remove iterator functions */
- if (!iter || !chrc)
- return false;
-
- service = iter->service;
-
- if (iter->pos >= service->num_chrcs)
- return false;
-
- *chrc = &service->chrcs[iter->pos++].chrc_external;
-
- return true;
+ return false;
}
bool bt_gatt_include_service_iter_init(struct bt_gatt_incl_service_iter *iter,
@@ -1707,19 +1629,9 @@ bool bt_gatt_include_service_iter_init(struct bt_gatt_incl_service_iter *iter,
bool bt_gatt_include_service_iter_next(struct bt_gatt_incl_service_iter *iter,
const bt_gatt_included_service_t **incl)
{
- struct service_list *service;
-
- if (!iter || !incl)
- return false;
-
- service = iter->service;
+ /* TODO: Remove iterator functions */
- if (iter->pos >= service->num_includes)
- return false;
-
- *incl = &service->includes[iter->pos++];
-
- return true;
+ return false;
}
struct read_op {
@@ -2474,7 +2386,6 @@ bool bt_gatt_client_register_notify(struct bt_gatt_client *client,
struct chrc_data *chrc = NULL;
struct bt_gatt_service_iter iter;
const bt_gatt_service_t *service;
- size_t i;
if (!client || !chrc_value_handle || !callback)
return false;
@@ -2497,13 +2408,11 @@ bool bt_gatt_client_register_notify(struct bt_gatt_client *client,
if (!svc_data)
return false;
- for (i = 0; i < svc_data->num_chrcs; i++) {
- if (svc_data->chrcs[i].chrc_external.value_handle ==
- chrc_value_handle) {
- chrc = svc_data->chrcs + i;
- break;
- }
- }
+ /*
+ * TODO: Lookup characteristic and CCC in database. Add entries for each
+ * characteristic to a list on demand.
+ */
+ return false;
/* Check that the characteristic supports notifications/indications */
if (!chrc || !chrc->ccc_handle || chrc->notify_count == INT_MAX)
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index 11b1f37..a09c3b8 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -29,7 +29,9 @@
struct bt_gatt_client;
-struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu);
+struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db,
+ struct bt_att *att,
+ uint16_t mtu);
struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client);
void bt_gatt_client_unref(struct bt_gatt_client *client);
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index dadfa37..6d1b5cb 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -62,6 +62,7 @@ static bool verbose = false;
struct client {
int fd;
+ struct gatt_db *db;
struct bt_gatt_client *gatt;
};
@@ -130,9 +131,18 @@ static struct client *client_create(int fd, uint16_t mtu)
}
cli->fd = fd;
- cli->gatt = bt_gatt_client_new(att, mtu);
+ cli->db = gatt_db_new();
+ if (!cli->db) {
+ fprintf(stderr, "Failed to create GATT database\n");
+ bt_att_unref(att);
+ free(cli);
+ return NULL;
+ }
+
+ cli->gatt = bt_gatt_client_new(cli->db, att, mtu);
if (!cli->gatt) {
fprintf(stderr, "Failed to create GATT client\n");
+ gatt_db_unref(cli->db);
bt_att_unref(att);
free(cli);
return NULL;
@@ -150,6 +160,7 @@ static struct client *client_create(int fd, uint16_t mtu)
/* bt_gatt_client already holds a reference */
bt_att_unref(att);
+ gatt_db_unref(cli->db);
return cli;
}
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 03a66b9..32bddac 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -39,10 +39,10 @@
#include "src/shared/util.h"
#include "src/shared/att.h"
#include "src/shared/gatt-helpers.h"
-#include "src/shared/gatt-client.h"
#include "src/shared/queue.h"
#include "src/shared/gatt-db.h"
#include "src/shared/gatt-server.h"
+#include "src/shared/gatt-client.h"
struct test_pdu {
bool valid;
@@ -77,7 +77,8 @@ struct context {
struct bt_gatt_client *client;
struct bt_gatt_server *server;
struct bt_att *att;
- struct gatt_db *db;
+ struct gatt_db *client_db;
+ struct gatt_db *server_db;
guint source;
guint process;
int fd;
@@ -433,7 +434,7 @@ static void client_ready_cb(bool success, uint8_t att_ecode, void *user_data)
static void populate_db(struct context *context)
{
- struct gatt_db *db = context->db;
+ struct gatt_db *db = context->server_db;
struct gatt_db_attribute *attr;
bt_uuid_t uuid;
uint128_t u128 = {
@@ -495,10 +496,11 @@ static struct context *create_context(uint16_t mtu, gconstpointer data)
bt_gatt_exchange_mtu(context->att, mtu, NULL, NULL, NULL);
break;
case SERVER:
- context->db = gatt_db_new();
- g_assert(context->db);
+ context->server_db = gatt_db_new();
+ g_assert(context->server_db);
- context->server = bt_gatt_server_new(context->db, att, mtu);
+ context->server = bt_gatt_server_new(context->server_db, att,
+ mtu);
g_assert(context->server);
populate_db(context);
@@ -509,7 +511,11 @@ static struct context *create_context(uint16_t mtu, gconstpointer data)
bt_att_unref(att);
break;
case CLIENT:
- context->client = bt_gatt_client_new(att, mtu);
+ context->client_db = gatt_db_new();
+ g_assert(context->client_db);
+
+ context->client = bt_gatt_client_new(context->client_db, att,
+ mtu);
g_assert(context->client);
if (g_test_verbose())
@@ -562,7 +568,8 @@ static void destroy_context(struct context *context)
bt_gatt_client_unref(context->client);
bt_gatt_server_unref(context->server);
- gatt_db_unref(context->db);
+ gatt_db_unref(context->client_db);
+ gatt_db_unref(context->server_db);
if (context->att)
bt_att_unref(context->att);
--
2.2.0.rc0.207.ga3a616c
This patch adds the "services" command which displayes the currently
stored services in the server database.
---
tools/btgatt-server.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 90 insertions(+)
diff --git a/tools/btgatt-server.c b/tools/btgatt-server.c
index 87b8067..a2a0ec9 100644
--- a/tools/btgatt-server.c
+++ b/tools/btgatt-server.c
@@ -883,6 +883,95 @@ static void cmd_heart_rate(struct server *server, char *cmd_str)
pdu, 4, conf_cb, NULL, NULL);
}
+static void print_uuid(const bt_uuid_t *uuid)
+{
+ char uuid_str[MAX_LEN_UUID_STR];
+ bt_uuid_t uuid128;
+
+ bt_uuid_to_uuid128(uuid, &uuid128);
+ bt_uuid_to_string(&uuid128, uuid_str, sizeof(uuid_str));
+
+ printf("%s\n", uuid_str);
+}
+
+static void print_incl(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct server *server = user_data;
+ uint16_t handle, start, end;
+ struct gatt_db_attribute *service;
+ bt_uuid_t uuid;
+
+ if (!gatt_db_attribute_get_incl_data(attr, &handle, &start, &end))
+ return;
+
+ service = gatt_db_get_attribute(server->db, start);
+ if (!service)
+ return;
+
+ gatt_db_attribute_get_service_uuid(service, &uuid);
+
+ printf("\t " COLOR_GREEN "include" COLOR_OFF " - handle: "
+ "0x%04x, - start: 0x%04x, end: 0x%04x,"
+ "uuid: ", handle, start, end);
+ print_uuid(&uuid);
+}
+
+static void print_desc(struct gatt_db_attribute *attr, void *user_data)
+{
+ printf("\t\t " COLOR_MAGENTA "descr" COLOR_OFF
+ " - handle: 0x%04x, uuid: ",
+ gatt_db_attribute_get_handle(attr));
+ print_uuid(gatt_db_attribute_get_type(attr));
+}
+
+static void print_chrc(struct gatt_db_attribute *attr, void *user_data)
+{
+ uint16_t handle, value_handle;
+ uint8_t properties;
+ bt_uuid_t uuid;
+
+ if (!gatt_db_attribute_get_char_data(attr, &handle,
+ &value_handle,
+ &properties,
+ &uuid))
+ return;
+
+ printf("\t " COLOR_YELLOW "charac" COLOR_OFF
+ " - start: 0x%04x, value: 0x%04x, "
+ "props: 0x%02x, uuid: ",
+ handle, value_handle, properties);
+ print_uuid(&uuid);
+
+ gatt_db_service_foreach_desc(attr, print_desc, NULL);
+}
+
+static void print_service(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct server *server = user_data;
+ uint16_t start, end;
+ bool primary;
+ bt_uuid_t uuid;
+
+ if (!gatt_db_attribute_get_service_data(attr, &start, &end, &primary,
+ &uuid))
+ return;
+
+ printf(COLOR_RED "service" COLOR_OFF " - start: 0x%04x, "
+ "end: 0x%04x, type: %s, uuid: ",
+ start, end, primary ? "primary" : "secondary");
+ print_uuid(&uuid);
+
+ gatt_db_service_foreach_incl(attr, print_incl, server);
+ gatt_db_service_foreach_char(attr, print_chrc, NULL);
+
+ printf("\n");
+}
+
+static void cmd_services(struct server *server, char *cmd_str)
+{
+ gatt_db_foreach_service(server->db, print_service, server);
+}
+
static void cmd_help(struct server *server, char *cmd_str);
typedef void (*command_func_t)(struct server *server, char *cmd_str);
@@ -895,6 +984,7 @@ static struct {
{ "help", cmd_help, "\tDisplay help message" },
{ "notify", cmd_notify, "\tSend handle-value notification" },
{ "heart-rate", cmd_heart_rate, "\tHide/Unhide Heart Rate Service" },
+ { "services", cmd_services, "\tEnumerate all services" },
{ }
};
--
2.2.0.rc0.207.ga3a616c
Added the gatt_db_isempty function which returns true if the database
is not populated with any services.
---
src/shared/gatt-db.c | 8 ++++++++
src/shared/gatt-db.h | 2 ++
2 files changed, 10 insertions(+)
diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index b10db4a..238872c 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -205,6 +205,14 @@ void gatt_db_unref(struct gatt_db *db)
gatt_db_destroy(db);
}
+bool gatt_db_isempty(struct gatt_db *db)
+{
+ if (!db)
+ return true;
+
+ return queue_isempty(db->services);
+}
+
static int uuid_to_le(const bt_uuid_t *uuid, uint8_t *dst)
{
bt_uuid_t uuid128;
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 7018b14..5db9f9b 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -29,6 +29,8 @@ struct gatt_db *gatt_db_new(void);
struct gatt_db *gatt_db_ref(struct gatt_db *db);
void gatt_db_unref(struct gatt_db *db);
+bool gatt_db_isempty(struct gatt_db *db);
+
struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db,
const bt_uuid_t *uuid,
bool primary,
--
2.2.0.rc0.207.ga3a616c