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: Fix bug in maximum handle check.
shared/gatt-db: Add helper to get service handles.
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
src/shared/gatt-client.c | 1189 +++++++++++++++++++++-------------------------
src/shared/gatt-client.h | 73 +--
src/shared/gatt-db.c | 41 +-
tools/btgatt-client.c | 175 +++----
tools/btgatt-server.c | 90 ++++
unit/test-gatt.c | 241 +++++++---
6 files changed, 911 insertions(+), 898 deletions(-)
--
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 | 241 +++++++++++++++++++++++++++++++++++++------------------
1 file changed, 162 insertions(+), 79 deletions(-)
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 703430c..7c61083 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;
};
@@ -176,23 +195,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,
@@ -201,23 +211,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,
@@ -226,19 +227,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,
@@ -343,36 +352,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;
- 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);
+ handle = gatt_db_attribute_get_handle(attr);
+ uuid = gatt_db_attribute_get_type(attr);
- for (i = 0; i < a->num_descs; i++)
- compare_descs(&a->descs[i], &b->descs[i]);
+ u128 = uuid->value.u128;
+
+ g_assert(a->handle == handle);
+ g_assert(memcmp(a->uuid, u128.data, sizeof(u128.data)) == 0);
}
typedef void (*test_step_t)(struct context *context);
@@ -387,39 +420,89 @@ 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;
+ struct gatt_db *db;
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);
+
+ db = bt_gatt_client_get_db(context->client);
+ g_assert(db);
- g_assert(!bt_gatt_service_iter_next(&iter, &service));
+ gatt_db_foreach_service(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 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 | 73 +++------------------------------------
2 files changed, 5 insertions(+), 156 deletions(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index a26f43a..4b1b90e 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -1641,94 +1641,6 @@ struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client)
return client->db;
}
-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 0309e5e..235b9cb 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -38,6 +38,10 @@ 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);
@@ -51,6 +55,7 @@ typedef void (*bt_gatt_client_service_changed_callback_t)(uint16_t start_handle,
uint16_t end_handle,
void *user_data);
+
bool bt_gatt_client_is_ready(struct bt_gatt_client *client);
bool bt_gatt_client_set_ready_handler(struct bt_gatt_client *client,
bt_gatt_client_callback_t callback,
@@ -67,74 +72,6 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client);
-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,
--
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 | 175 +++++++++++++++++++++++---------------------------
1 file changed, 79 insertions(+), 96 deletions(-)
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index dadfa37..4a2b013 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
@@ -159,118 +161,118 @@ 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(bt_gatt_client_get_db(cli->gatt),
+ 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 client *cli = 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: ",
- service->start_handle, service->end_handle,
- service->primary ? "primary" : "secondary");
- print_uuid(service->uuid);
+ start, end, primary ? "primary" : "secondary");
+ print_uuid(&uuid);
- while (bt_gatt_include_service_iter_next(&include_iter, &incl)) {
- 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);
- }
- }
+ 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)
{
- 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;
- }
+ struct gatt_db *db = bt_gatt_client_get_db(cli->gatt);
printf("\n");
- while (bt_gatt_service_iter_next(&iter, &service))
- print_service(service);
+ gatt_db_foreach_service(db, print_service, cli);
}
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;
-
- if (!bt_gatt_service_iter_init(&iter, cli->gatt)) {
- PRLOG("Failed to initialize service iterator\n");
- return;
- }
+ struct gatt_db *db = bt_gatt_client_get_db(cli->gatt);
printf("\n");
- while (bt_gatt_service_iter_next_by_uuid(&iter, uuid->value.u128.data,
- &service))
- print_service(service);
+ /* TODO: Filter by UUID */
+ gatt_db_foreach_service(db, print_service, cli);
}
static void print_services_by_handle(struct client *cli, uint16_t handle)
{
- 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;
- }
+ struct gatt_db *db = bt_gatt_client_get_db(cli->gatt);
printf("\n");
- while (bt_gatt_service_iter_next_by_handle(&iter, handle, &service))
- print_service(service);
+ /* TODO: Filter by handle */
+ gatt_db_foreach_service(db, print_service, cli);
}
static void ready_cb(bool success, uint8_t att_ecode, void *user_data)
@@ -293,32 +295,13 @@ 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(bt_gatt_client_get_db(cli->gatt),
+ print_service, cli,
+ start_handle, end_handle);
print_prompt();
}
--
2.2.0.rc0.207.ga3a616c
This patch adds gatt_db_service_get_handles which is an internal helper
to obtain service handles from a gatt_db_service directly.
---
src/shared/gatt-db.c | 35 ++++++++++++++++++-----------------
1 file changed, 18 insertions(+), 17 deletions(-)
diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index 30fa4df..b210376 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -317,6 +317,18 @@ bool gatt_db_clear(struct gatt_db *db)
return true;
}
+static void gatt_db_service_get_handles(const struct gatt_db_service *service,
+ uint16_t *start_handle,
+ uint16_t *end_handle)
+{
+ if (start_handle)
+ *start_handle = service->attributes[0]->handle;
+
+ if (end_handle)
+ *end_handle = service->attributes[0]->handle +
+ service->num_handles - 1;
+}
+
struct clear_range {
uint16_t start, end;
};
@@ -327,8 +339,7 @@ static bool match_range(const void *a, const void *b)
const struct clear_range *range = b;
uint16_t svc_start, svc_end;
- svc_start = service->attributes[0]->handle;
- svc_end = service->attributes[0]->handle + service->num_handles - 1;
+ gatt_db_service_get_handles(service, &svc_start, &svc_end);
return svc_start <= range->end && svc_end >= range->start;
}
@@ -366,8 +377,7 @@ static void search_for_insert_loc(void *data, void *user_data)
if (loc_data->done)
return;
- cur_start = service->attributes[0]->handle;
- cur_end = service->attributes[0]->handle + service->num_handles - 1;
+ gatt_db_service_get_handles(service, &cur_start, &cur_end);
/* Abort if the requested range overlaps with an existing service. */
if ((loc_data->start >= cur_start && loc_data->start <= cur_end) ||
@@ -1038,8 +1048,8 @@ bool gatt_db_attribute_get_service_uuid(struct gatt_db_attribute *attrib,
}
bool gatt_db_attribute_get_service_handles(struct gatt_db_attribute *attrib,
- uint16_t *start_handle,
- uint16_t *end_handle)
+ uint16_t *start_handle,
+ uint16_t *end_handle)
{
struct gatt_db_service *service;
@@ -1048,12 +1058,7 @@ bool gatt_db_attribute_get_service_handles(struct gatt_db_attribute *attrib,
service = attrib->service;
- if (start_handle)
- *start_handle = service->attributes[0]->handle;
-
- if (end_handle)
- *end_handle = service->attributes[0]->handle +
- service->num_handles - 1;
+ gatt_db_service_get_handles(service, start_handle, end_handle);
return true;
}
@@ -1073,11 +1078,7 @@ bool gatt_db_attribute_get_service_data(struct gatt_db_attribute *attrib,
service = attrib->service;
decl = service->attributes[0];
- if (start_handle)
- *start_handle = decl->handle;
-
- if (end_handle)
- *end_handle = decl->handle + service->num_handles - 1;
+ gatt_db_service_get_handles(service, start_handle, end_handle);
if (primary)
*primary = bt_uuid_cmp(&decl->uuid, &secondary_service_uuid);
--
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 2dc6735..a26f43a 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,
@@ -988,6 +1073,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
@@ -1206,12 +1293,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])
@@ -1322,7 +1407,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)
@@ -1389,6 +1474,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);
}
@@ -1438,6 +1524,10 @@ struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu)
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)
@@ -2379,6 +2469,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,
@@ -2387,40 +2485,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);
@@ -2435,7 +2524,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) {
@@ -2443,7 +2533,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 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 c603b30..13038d1 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
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 | 942 +++++++++++++++++++++--------------------------
src/shared/gatt-client.h | 2 +
2 files changed, 430 insertions(+), 514 deletions(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 033cba1..2dc6735 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -27,6 +27,7 @@
#include "src/shared/gatt-helpers.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"
+#include "src/shared/gatt-db.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);
- op->cur_service->includes = includes;
- op->cur_service->num_includes = includes_count;
+ tmp = gatt_db_get_attribute(client->db, start);
+ if (!tmp)
+ goto failed;
+
+ 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,78 @@ 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 int discover_descs(struct discovery_op *op)
+{
+ struct bt_gatt_client *client = op->client;
+ struct gatt_db_attribute *attr;
+ struct chrc *chrc_data;
+ uint16_t desc_start;
+
+ /*
+ * This method returns the following three values:
+ * -1: Failure
+ * 0: No discovery started
+ * 1: Discovery started
+ */
+
+ 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)) {
+ free(chrc_data);
+ return 1;
+ }
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Failed to start descriptor discovery");
+ discovery_op_unref(op);
+
+ goto failed;
+ }
+
+ free(chrc_data);
+ return 0;
+
+failed:
+ free(chrc_data);
+ return -1;
+}
+
static void discover_descs_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data)
@@ -535,11 +499,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;
+ int status;
if (!success) {
if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
@@ -550,94 +516,69 @@ 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 (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;
-
- util_debug(client->debug_callback, client->debug_data,
- "Failed to start descriptor discovery");
- discovery_op_unref(op);
- success = false;
+ status = discover_descs(op);
+ if (status < 0)
+ goto failed;
- goto done;
- }
+ if (status > 0)
+ return;
next:
- if (!op->cur_service->next)
+ attr = queue_pop_head(op->pending_svcs);
+ if (!attr)
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 +586,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;
+ int status;
if (!success) {
if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
@@ -660,98 +605,76 @@ 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;
- }
-
- if (uuid_cmp(chrcs[i].chrc_external.uuid, SVC_CHNGD_UUID) == 0)
- client->svc_chngd_val_handle =
- chrcs[i].chrc_external.value_handle;
+ start, end, value, properties, uuid_str);
- i++;
- }
+ chrc_data = new0(struct chrc, 1);
+ if (!chrc_data)
+ goto failed;
- op->cur_service->chrcs = chrcs;
- op->cur_service->num_chrcs = chrc_count;
+ chrc_data->start_handle = start;
+ chrc_data->end_handle = end;
+ chrc_data->value_handle = value;
+ chrc_data->properties = properties;
+ chrc_data->uuid = uuid;
- 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;
-
- 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;
+ queue_push_tail(op->pending_chrcs, chrc_data);
+ }
- util_debug(client->debug_callback, client->debug_data,
- "Failed to start descriptor discovery");
- discovery_op_unref(op);
- success = false;
+ /*
+ * Sequentially discover descriptors for each characteristic and insert
+ * the characteristics into the database as we proceed.
+ */
+ status = discover_descs(op);
+ if (status < 0)
+ goto failed;
- goto done;
- }
+ if (status > 0)
+ return;
next:
- if (!op->cur_service->next)
+ attr = queue_pop_head(op->pending_svcs);
+ if (!attr)
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);
+ gatt_db_service_set_active(attr, true);
+ 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 current 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,29 @@ 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;
+ gatt_db_service_set_active(attr, true);
+ 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 +835,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);
}
@@ -971,7 +916,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 +926,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 +944,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 +992,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);
-
- 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;
- }
+ gatt_db_clear_range(client->db, start_handle, end_handle);
- 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 +1023,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 +1083,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 +1115,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 +1130,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 +1148,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 +1383,8 @@ static void bt_gatt_client_free(struct bt_gatt_client *client)
bt_att_unref(client->att);
}
- gatt_client_clear_services(client);
+ if (client->db)
+ gatt_db_destroy(client->db);
queue_destroy(client->svc_chngd_queue, free);
queue_destroy(client->long_write_queue, long_write_op_unref);
@@ -1507,6 +1448,10 @@ struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu)
if (!client->ind_id)
goto fail;
+ client->db = gatt_db_new();
+ if (!client->db)
+ goto fail;
+
client->att = bt_att_ref(att);
if (!gatt_client_init(client, mtu))
@@ -1598,6 +1543,14 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
return true;
}
+struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client)
+{
+ if (!client || !client->ready || client->in_svc_chngd)
+ return NULL;
+
+ return client->db;
+}
+
bool bt_gatt_service_iter_init(struct bt_gatt_service_iter *iter,
struct bt_gatt_client *client)
{
@@ -1617,25 +1570,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 +1614,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 +1634,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 +2391,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 +2413,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..0309e5e 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -65,6 +65,8 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
+struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client);
+
typedef struct {
bool primary;
uint16_t start_handle;
--
2.2.0.rc0.207.ga3a616c
0xffff is a valid handle and many devices return it as the end group
of their last service. This patch fixes a bug that prevents adding or
inserting such a service into the database.
---
src/shared/gatt-db.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index 2515e5a..30fa4df 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -265,7 +265,7 @@ struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db,
{
struct gatt_db_service *service;
- if (!db || (num_handles + db->next_handle) > UINT16_MAX)
+ if (!db || (num_handles + db->next_handle - 1) > UINT16_MAX)
return NULL;
service = gatt_db_service_create(uuid, primary, num_handles);
@@ -395,7 +395,7 @@ struct gatt_db_attribute *gatt_db_insert_service(struct gatt_db *db,
struct insert_loc_data data;
struct gatt_db_service *service;
- if (!db || num_handles < 1 || handle + num_handles > UINT16_MAX)
+ if (!db || num_handles < 1 || (handle + num_handles - 1) > UINT16_MAX)
return NULL;
memset(&data, 0, sizeof(data));
@@ -423,7 +423,7 @@ struct gatt_db_attribute *gatt_db_insert_service(struct gatt_db *db,
service->num_handles = num_handles;
/* Fast-forward next_handle if the new service was added to the end */
- db->next_handle = MAX(handle + num_handles + 1, db->next_handle);
+ db->next_handle = MAX(handle + num_handles, db->next_handle);
return service->attributes[0];
--
2.2.0.rc0.207.ga3a616c
Hi Luiz,
> On Tue, Dec 2, 2014 at 11:35 AM, Arman Uguray <[email protected]> wr=
ote:
> Hi Luiz,
>
>> On Tue, Dec 2, 2014 at 7:12 AM, Luiz Augusto von Dentz <luiz.dentz@gmail=
.com> wrote:
>> Hi Arman,
>>
>> On Tue, Dec 2, 2014 at 12:46 PM, Luiz Augusto von Dentz
>> <[email protected]> wrote:
>>> Hi Arman,
>>>
>>> On Mon, Dec 1, 2014 at 7:57 PM, Arman Uguray <[email protected]> w=
rote:
>>>> Hi Marcel & Luiz,
>>>>
>>>>> On Mon, Dec 1, 2014 at 9:19 AM, Marcel Holtmann <[email protected]>=
wrote:
>>>>> Hi Arman,
>>>>>
>>>>>>>>>> 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 gat=
t_db.
>>>>>>>>>
>>>>>>>>> Im trying to figure out the reason why you want to start with you=
r own
>>>>>>>>> gatt_db, is it because it lacks reference counting, if that is th=
e
>>>>>>>>> case it should be trivial to add it.
>>>>>>>>>
>>>>>>>>
>>>>>>>> Initially, yes, the lack of reference counting is one reason, whic=
h I
>>>>>>>> was thinking of adding to gatt-db eventually. Though, what I had i=
n
>>>>>>>> mind was that, the gatt-db would be created by gatt-client if you =
want
>>>>>>>> it to perform discovery, otherwise if you construct it with gatt-d=
b
>>>>>>>> then it wouldn't do discovery, which would address the permanent c=
ache
>>>>>>>> case. So, we would have two "new" functions:
>>>>>>>>
>>>>>>>> bt_gatt_client_new
>>>>>>>> bt_gatt_client_new_from_db
>>>>>>>>
>>>>>>>> In the first case, if the upper layer wants to make the gatt-db
>>>>>>>> outlive the gatt-client, in the future they can just add a referen=
ce
>>>>>>>> to it and own it and in the next connection they can create the cl=
ient
>>>>>>>> using that same db instance. This is kind of a rough idea right no=
w
>>>>>>>> but I think it makes sense.
>>>>>>>>
>>>>>>>> We can also keep both functions but have both accept a gatt-db as =
a
>>>>>>>> parameter. Not sure what's best here really.
>>>>>>>
>>>>>>> For unit test maybe we can have bt_gatt_client_new_default, iirc
>>>>>>> Marcel suggested something like this for naming, but for the core
>>>>>>> daemon it can create a empty db when pairing so even the initial
>>>>>>> discovery would use it. This comes back to the idea of having the
>>>>>>> callbacks in the db to attribute added/removed, with this logic we
>>>>>>> could start creating D-Bus objects by the time a services is
>>>>>>> discovered (provided we discover everything necessary) and not only
>>>>>>> when bt_gatt_client signals it is done discovering, actually with t=
he
>>>>>>> current concept we could be spamming the bus a little too much sinc=
e
>>>>>>> the objects would be created all at same time this would cause seve=
ral
>>>>>>> InterfacesAdded signals in a row at the end of pairing.
>>>>>>>
>>>>>>
>>>>>> I don't know about "_default" naming but are we talking about someth=
ing like:
>>>>>>
>>>>>> bt_gatt_client_new(att, mtu, db) /* Does discovery */
>>>>>> bt_gatt_client_new_from_cache(att, mtu, db) /* No discovery */
>>>>>>
>>>>>> So both functions get passed a db either way?
>>>>>
>>>>> actually we might just better only use bt_gatt_client_new() and provi=
de functionality to flush certain devices from the cache. In case we do wan=
t to trigger a discovery or re-discovery of devices.
>>>>>
>>>>> The cache should just work and do the right thing. We should be able =
set max cacheable service, devices etc. So lets just give the GATT client a=
cache to work with and that is it. Everything else is an implementation de=
tail between the client and the cache.
>>>>>
>>>>> I prefer that every client has a cache attached to it (or allow NULL =
in case we really want to run without a cache). If the cache is independent=
, it can easily do the right thing for us.
>>>>>
>>>>
>>>> After our brief chat on IRC, please confirm that this is more or less
>>>> what we want:
>>>>
>>>> bt_gatt_client_new(att, mtu, db) -> adds ref to db.
>>>> bt_gatt_client_new(att, mtu, NULL) -> creates its own db
>>>>
>>>> Either function might cause discovery based on whether or not the db
>>>> is empty. If it's empty, client always starts discovery, otherwise it
>>>> skips it.
>>>
>>> Sounds good. Sorry for not responding on IRC yesterday, I was away and
>>> only noticed today that you have been there.
>
> No worries. I haven't uploaded the next round yet until we have a good
> idea of what we want.
>
>>
>> I went ahead and implemented the reference counting for gatt_db, I
>> will try to be online later today in case you want some to discuss
>> more about this.
>>
>
> Cool, I'll rebase and make the code work with ref counts.
>
>>>> bt_gatt_client_get_db -> returns db. This would be necessary if we
>>>> want to allow passing NULL to bt_gatt_client_new.
>>>
>>> Hmm, but if we pass NULL, which I suppose is to enable things like
>>> unit tests, why would we want to pull the db out of it? As I mention
>>> from the core we would probably create the db when pairing so we get
>>> notification regarding services changes from discovery, with this mind
>>> I guess if the client pass NULL this means the db would be internal.
>>>
>
> I guess the idea with passing NULL is that you're basically saying
> "there is no existing database around for this device, create a new
> one and I'll get it from you later". So, if we allow passing NULL and
> having gatt-client create the db, then we would want to pull the db
> out of it, since we can't browse any of the handles or get events
> otherwise. I don't have a particular opinion on this; I think it's
> equally fine to disallow passing NULL and always have the upper layer
> instantiate the db. Then we wouldn't need the getter I guess.
>
>>> Btw, I guess we should associate a valid db if service changed CCC has
>>> been written or perhaps we clear the db if discovery is interrupted
>>> before that happens but I guess in some cases we may have a db without
>>> a service changed, in case we don't pair for example, in that case we
>>> need to rediscover the handles before letting any profile connect
>>> otherwise they may end up using invalid handles.
>>>
>
> Yeah, I think if we always require that a db is passed in to
> bt_gatt_client_new, then we can have the client make the correct
> decision. If there was no service changed characteristic for instance,
> then the upper layer may want to skip discovery and reuse the same db
> (with the assumption that the services won't change) or pass an empty
> db to have client discover everything.
>
>>>> I think all of these are minor details that can be revised later, but
>>>> I still need something as a starting point.
>>>>
>>>> In addition, I'm thinking that gatt-db should have service added and
>>>> removed events that can be triggered by set_active and the clear
>>>> functions. Something like
>>>>
>>>> gatt_db_register_service_callbacks(db, added_cb, removed_cb);
>>>>
>>>> In the client case, added_cb would only be called once all attributes
>>>> belonging to the service have been discovered and inserted into the
>>>> db. Client would also communicate services that are affected by a
>>>> "Service Changed" indication via these callbacks. Either way, I
>>>> suggest adding the service callback mechanism in a later patch set.
>>>
>>> Sounds good, but we might have to introduce this before we start doing
>>> changing the core since this will probably be the preferable way the
>>> discovery interact with core.
>>>
>
> OK, sounds good to me. I might actually just add this into v1 of this set=
.
>
I just sent out v1. Added a TODO for the service added/removed events
which I'll send to the list in a separate patch set once this one is
in.
>>>> Let me know of this makes sense to everyone.
>>>>
>>>>> Regards
>>>>>
>>>>> Marcel
>>>>>
>>>>
>>>> Arman
>>>
>>>
>>>
>>> --
>>> Luiz Augusto von Dentz
>>
>>
>>
>> --
>> Luiz Augusto von Dentz
>
> Thanks,
> Arman
Thanks,
Arman
Hi Luiz,
> On Tue, Dec 2, 2014 at 7:12 AM, Luiz Augusto von Dentz <luiz.dentz@gmail.=
com> wrote:
> Hi Arman,
>
> On Tue, Dec 2, 2014 at 12:46 PM, Luiz Augusto von Dentz
> <[email protected]> wrote:
>> Hi Arman,
>>
>> On Mon, Dec 1, 2014 at 7:57 PM, Arman Uguray <[email protected]> wr=
ote:
>>> Hi Marcel & Luiz,
>>>
>>>> On Mon, Dec 1, 2014 at 9:19 AM, Marcel Holtmann <[email protected]> =
wrote:
>>>> Hi Arman,
>>>>
>>>>>>>>> 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.
>>>>>>>>
>>>>>>>> Im trying to figure out the reason why you want to start with your=
own
>>>>>>>> gatt_db, is it because it lacks reference counting, if that is the
>>>>>>>> case it should be trivial to add it.
>>>>>>>>
>>>>>>>
>>>>>>> Initially, yes, the lack of reference counting is one reason, which=
I
>>>>>>> was thinking of adding to gatt-db eventually. Though, what I had in
>>>>>>> mind was that, the gatt-db would be created by gatt-client if you w=
ant
>>>>>>> it to perform discovery, otherwise if you construct it with gatt-db
>>>>>>> then it wouldn't do discovery, which would address the permanent ca=
che
>>>>>>> case. So, we would have two "new" functions:
>>>>>>>
>>>>>>> bt_gatt_client_new
>>>>>>> bt_gatt_client_new_from_db
>>>>>>>
>>>>>>> In the first case, if the upper layer wants to make the gatt-db
>>>>>>> outlive the gatt-client, in the future they can just add a referenc=
e
>>>>>>> to it and own it and in the next connection they can create the cli=
ent
>>>>>>> using that same db instance. This is kind of a rough idea right now
>>>>>>> but I think it makes sense.
>>>>>>>
>>>>>>> We can also keep both functions but have both accept a gatt-db as a
>>>>>>> parameter. Not sure what's best here really.
>>>>>>
>>>>>> For unit test maybe we can have bt_gatt_client_new_default, iirc
>>>>>> Marcel suggested something like this for naming, but for the core
>>>>>> daemon it can create a empty db when pairing so even the initial
>>>>>> discovery would use it. This comes back to the idea of having the
>>>>>> callbacks in the db to attribute added/removed, with this logic we
>>>>>> could start creating D-Bus objects by the time a services is
>>>>>> discovered (provided we discover everything necessary) and not only
>>>>>> when bt_gatt_client signals it is done discovering, actually with th=
e
>>>>>> current concept we could be spamming the bus a little too much since
>>>>>> the objects would be created all at same time this would cause sever=
al
>>>>>> InterfacesAdded signals in a row at the end of pairing.
>>>>>>
>>>>>
>>>>> I don't know about "_default" naming but are we talking about somethi=
ng like:
>>>>>
>>>>> bt_gatt_client_new(att, mtu, db) /* Does discovery */
>>>>> bt_gatt_client_new_from_cache(att, mtu, db) /* No discovery */
>>>>>
>>>>> So both functions get passed a db either way?
>>>>
>>>> actually we might just better only use bt_gatt_client_new() and provid=
e functionality to flush certain devices from the cache. In case we do want=
to trigger a discovery or re-discovery of devices.
>>>>
>>>> The cache should just work and do the right thing. We should be able s=
et max cacheable service, devices etc. So lets just give the GATT client a =
cache to work with and that is it. Everything else is an implementation det=
ail between the client and the cache.
>>>>
>>>> I prefer that every client has a cache attached to it (or allow NULL i=
n case we really want to run without a cache). If the cache is independent,=
it can easily do the right thing for us.
>>>>
>>>
>>> After our brief chat on IRC, please confirm that this is more or less
>>> what we want:
>>>
>>> bt_gatt_client_new(att, mtu, db) -> adds ref to db.
>>> bt_gatt_client_new(att, mtu, NULL) -> creates its own db
>>>
>>> Either function might cause discovery based on whether or not the db
>>> is empty. If it's empty, client always starts discovery, otherwise it
>>> skips it.
>>
>> Sounds good. Sorry for not responding on IRC yesterday, I was away and
>> only noticed today that you have been there.
No worries. I haven't uploaded the next round yet until we have a good
idea of what we want.
>
> I went ahead and implemented the reference counting for gatt_db, I
> will try to be online later today in case you want some to discuss
> more about this.
>
Cool, I'll rebase and make the code work with ref counts.
>>> bt_gatt_client_get_db -> returns db. This would be necessary if we
>>> want to allow passing NULL to bt_gatt_client_new.
>>
>> Hmm, but if we pass NULL, which I suppose is to enable things like
>> unit tests, why would we want to pull the db out of it? As I mention
>> from the core we would probably create the db when pairing so we get
>> notification regarding services changes from discovery, with this mind
>> I guess if the client pass NULL this means the db would be internal.
>>
I guess the idea with passing NULL is that you're basically saying
"there is no existing database around for this device, create a new
one and I'll get it from you later". So, if we allow passing NULL and
having gatt-client create the db, then we would want to pull the db
out of it, since we can't browse any of the handles or get events
otherwise. I don't have a particular opinion on this; I think it's
equally fine to disallow passing NULL and always have the upper layer
instantiate the db. Then we wouldn't need the getter I guess.
>> Btw, I guess we should associate a valid db if service changed CCC has
>> been written or perhaps we clear the db if discovery is interrupted
>> before that happens but I guess in some cases we may have a db without
>> a service changed, in case we don't pair for example, in that case we
>> need to rediscover the handles before letting any profile connect
>> otherwise they may end up using invalid handles.
>>
Yeah, I think if we always require that a db is passed in to
bt_gatt_client_new, then we can have the client make the correct
decision. If there was no service changed characteristic for instance,
then the upper layer may want to skip discovery and reuse the same db
(with the assumption that the services won't change) or pass an empty
db to have client discover everything.
>>> I think all of these are minor details that can be revised later, but
>>> I still need something as a starting point.
>>>
>>> In addition, I'm thinking that gatt-db should have service added and
>>> removed events that can be triggered by set_active and the clear
>>> functions. Something like
>>>
>>> gatt_db_register_service_callbacks(db, added_cb, removed_cb);
>>>
>>> In the client case, added_cb would only be called once all attributes
>>> belonging to the service have been discovered and inserted into the
>>> db. Client would also communicate services that are affected by a
>>> "Service Changed" indication via these callbacks. Either way, I
>>> suggest adding the service callback mechanism in a later patch set.
>>
>> Sounds good, but we might have to introduce this before we start doing
>> changing the core since this will probably be the preferable way the
>> discovery interact with core.
>>
OK, sounds good to me. I might actually just add this into v1 of this set.
>>> Let me know of this makes sense to everyone.
>>>
>>>> Regards
>>>>
>>>> Marcel
>>>>
>>>
>>> Arman
>>
>>
>>
>> --
>> Luiz Augusto von Dentz
>
>
>
> --
> Luiz Augusto von Dentz
Thanks,
Arman
Hi Arman,
On Tue, Dec 2, 2014 at 12:46 PM, Luiz Augusto von Dentz
<[email protected]> wrote:
> Hi Arman,
>
> On Mon, Dec 1, 2014 at 7:57 PM, Arman Uguray <[email protected]> wro=
te:
>> Hi Marcel & Luiz,
>>
>>> On Mon, Dec 1, 2014 at 9:19 AM, Marcel Holtmann <[email protected]> w=
rote:
>>> Hi Arman,
>>>
>>>>>>>> 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.
>>>>>>>
>>>>>>> Im trying to figure out the reason why you want to start with your =
own
>>>>>>> gatt_db, is it because it lacks reference counting, if that is the
>>>>>>> case it should be trivial to add it.
>>>>>>>
>>>>>>
>>>>>> Initially, yes, the lack of reference counting is one reason, which =
I
>>>>>> was thinking of adding to gatt-db eventually. Though, what I had in
>>>>>> mind was that, the gatt-db would be created by gatt-client if you wa=
nt
>>>>>> it to perform discovery, otherwise if you construct it with gatt-db
>>>>>> then it wouldn't do discovery, which would address the permanent cac=
he
>>>>>> case. So, we would have two "new" functions:
>>>>>>
>>>>>> bt_gatt_client_new
>>>>>> bt_gatt_client_new_from_db
>>>>>>
>>>>>> In the first case, if the upper layer wants to make the gatt-db
>>>>>> outlive the gatt-client, in the future they can just add a reference
>>>>>> to it and own it and in the next connection they can create the clie=
nt
>>>>>> using that same db instance. This is kind of a rough idea right now
>>>>>> but I think it makes sense.
>>>>>>
>>>>>> We can also keep both functions but have both accept a gatt-db as a
>>>>>> parameter. Not sure what's best here really.
>>>>>
>>>>> For unit test maybe we can have bt_gatt_client_new_default, iirc
>>>>> Marcel suggested something like this for naming, but for the core
>>>>> daemon it can create a empty db when pairing so even the initial
>>>>> discovery would use it. This comes back to the idea of having the
>>>>> callbacks in the db to attribute added/removed, with this logic we
>>>>> could start creating D-Bus objects by the time a services is
>>>>> discovered (provided we discover everything necessary) and not only
>>>>> when bt_gatt_client signals it is done discovering, actually with the
>>>>> current concept we could be spamming the bus a little too much since
>>>>> the objects would be created all at same time this would cause severa=
l
>>>>> InterfacesAdded signals in a row at the end of pairing.
>>>>>
>>>>
>>>> I don't know about "_default" naming but are we talking about somethin=
g like:
>>>>
>>>> bt_gatt_client_new(att, mtu, db) /* Does discovery */
>>>> bt_gatt_client_new_from_cache(att, mtu, db) /* No discovery */
>>>>
>>>> So both functions get passed a db either way?
>>>
>>> actually we might just better only use bt_gatt_client_new() and provide=
functionality to flush certain devices from the cache. In case we do want =
to trigger a discovery or re-discovery of devices.
>>>
>>> The cache should just work and do the right thing. We should be able se=
t max cacheable service, devices etc. So lets just give the GATT client a c=
ache to work with and that is it. Everything else is an implementation deta=
il between the client and the cache.
>>>
>>> I prefer that every client has a cache attached to it (or allow NULL in=
case we really want to run without a cache). If the cache is independent, =
it can easily do the right thing for us.
>>>
>>
>> After our brief chat on IRC, please confirm that this is more or less
>> what we want:
>>
>> bt_gatt_client_new(att, mtu, db) -> adds ref to db.
>> bt_gatt_client_new(att, mtu, NULL) -> creates its own db
>>
>> Either function might cause discovery based on whether or not the db
>> is empty. If it's empty, client always starts discovery, otherwise it
>> skips it.
>
> Sounds good. Sorry for not responding on IRC yesterday, I was away and
> only noticed today that you have been there.
I went ahead and implemented the reference counting for gatt_db, I
will try to be online later today in case you want some to discuss
more about this.
>> bt_gatt_client_get_db -> returns db. This would be necessary if we
>> want to allow passing NULL to bt_gatt_client_new.
>
> Hmm, but if we pass NULL, which I suppose is to enable things like
> unit tests, why would we want to pull the db out of it? As I mention
> from the core we would probably create the db when pairing so we get
> notification regarding services changes from discovery, with this mind
> I guess if the client pass NULL this means the db would be internal.
>
> Btw, I guess we should associate a valid db if service changed CCC has
> been written or perhaps we clear the db if discovery is interrupted
> before that happens but I guess in some cases we may have a db without
> a service changed, in case we don't pair for example, in that case we
> need to rediscover the handles before letting any profile connect
> otherwise they may end up using invalid handles.
>
>> I think all of these are minor details that can be revised later, but
>> I still need something as a starting point.
>>
>> In addition, I'm thinking that gatt-db should have service added and
>> removed events that can be triggered by set_active and the clear
>> functions. Something like
>>
>> gatt_db_register_service_callbacks(db, added_cb, removed_cb);
>>
>> In the client case, added_cb would only be called once all attributes
>> belonging to the service have been discovered and inserted into the
>> db. Client would also communicate services that are affected by a
>> "Service Changed" indication via these callbacks. Either way, I
>> suggest adding the service callback mechanism in a later patch set.
>
> Sounds good, but we might have to introduce this before we start doing
> changing the core since this will probably be the preferable way the
> discovery interact with core.
>
>> Let me know of this makes sense to everyone.
>>
>>> Regards
>>>
>>> Marcel
>>>
>>
>> Arman
>
>
>
> --
> Luiz Augusto von Dentz
--=20
Luiz Augusto von Dentz
Hi Arman,
On Mon, Dec 1, 2014 at 7:57 PM, Arman Uguray <[email protected]> wrote=
:
> Hi Marcel & Luiz,
>
>> On Mon, Dec 1, 2014 at 9:19 AM, Marcel Holtmann <[email protected]> wr=
ote:
>> Hi Arman,
>>
>>>>>>> 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_d=
b.
>>>>>>
>>>>>> Im trying to figure out the reason why you want to start with your o=
wn
>>>>>> gatt_db, is it because it lacks reference counting, if that is the
>>>>>> case it should be trivial to add it.
>>>>>>
>>>>>
>>>>> Initially, yes, the lack of reference counting is one reason, which I
>>>>> was thinking of adding to gatt-db eventually. Though, what I had in
>>>>> mind was that, the gatt-db would be created by gatt-client if you wan=
t
>>>>> it to perform discovery, otherwise if you construct it with gatt-db
>>>>> then it wouldn't do discovery, which would address the permanent cach=
e
>>>>> case. So, we would have two "new" functions:
>>>>>
>>>>> bt_gatt_client_new
>>>>> bt_gatt_client_new_from_db
>>>>>
>>>>> In the first case, if the upper layer wants to make the gatt-db
>>>>> outlive the gatt-client, in the future they can just add a reference
>>>>> to it and own it and in the next connection they can create the clien=
t
>>>>> using that same db instance. This is kind of a rough idea right now
>>>>> but I think it makes sense.
>>>>>
>>>>> We can also keep both functions but have both accept a gatt-db as a
>>>>> parameter. Not sure what's best here really.
>>>>
>>>> For unit test maybe we can have bt_gatt_client_new_default, iirc
>>>> Marcel suggested something like this for naming, but for the core
>>>> daemon it can create a empty db when pairing so even the initial
>>>> discovery would use it. This comes back to the idea of having the
>>>> callbacks in the db to attribute added/removed, with this logic we
>>>> could start creating D-Bus objects by the time a services is
>>>> discovered (provided we discover everything necessary) and not only
>>>> when bt_gatt_client signals it is done discovering, actually with the
>>>> current concept we could be spamming the bus a little too much since
>>>> the objects would be created all at same time this would cause several
>>>> InterfacesAdded signals in a row at the end of pairing.
>>>>
>>>
>>> I don't know about "_default" naming but are we talking about something=
like:
>>>
>>> bt_gatt_client_new(att, mtu, db) /* Does discovery */
>>> bt_gatt_client_new_from_cache(att, mtu, db) /* No discovery */
>>>
>>> So both functions get passed a db either way?
>>
>> actually we might just better only use bt_gatt_client_new() and provide =
functionality to flush certain devices from the cache. In case we do want t=
o trigger a discovery or re-discovery of devices.
>>
>> The cache should just work and do the right thing. We should be able set=
max cacheable service, devices etc. So lets just give the GATT client a ca=
che to work with and that is it. Everything else is an implementation detai=
l between the client and the cache.
>>
>> I prefer that every client has a cache attached to it (or allow NULL in =
case we really want to run without a cache). If the cache is independent, i=
t can easily do the right thing for us.
>>
>
> After our brief chat on IRC, please confirm that this is more or less
> what we want:
>
> bt_gatt_client_new(att, mtu, db) -> adds ref to db.
> bt_gatt_client_new(att, mtu, NULL) -> creates its own db
>
> Either function might cause discovery based on whether or not the db
> is empty. If it's empty, client always starts discovery, otherwise it
> skips it.
Sounds good. Sorry for not responding on IRC yesterday, I was away and
only noticed today that you have been there.
> bt_gatt_client_get_db -> returns db. This would be necessary if we
> want to allow passing NULL to bt_gatt_client_new.
Hmm, but if we pass NULL, which I suppose is to enable things like
unit tests, why would we want to pull the db out of it? As I mention
from the core we would probably create the db when pairing so we get
notification regarding services changes from discovery, with this mind
I guess if the client pass NULL this means the db would be internal.
Btw, I guess we should associate a valid db if service changed CCC has
been written or perhaps we clear the db if discovery is interrupted
before that happens but I guess in some cases we may have a db without
a service changed, in case we don't pair for example, in that case we
need to rediscover the handles before letting any profile connect
otherwise they may end up using invalid handles.
> I think all of these are minor details that can be revised later, but
> I still need something as a starting point.
>
> In addition, I'm thinking that gatt-db should have service added and
> removed events that can be triggered by set_active and the clear
> functions. Something like
>
> gatt_db_register_service_callbacks(db, added_cb, removed_cb);
>
> In the client case, added_cb would only be called once all attributes
> belonging to the service have been discovered and inserted into the
> db. Client would also communicate services that are affected by a
> "Service Changed" indication via these callbacks. Either way, I
> suggest adding the service callback mechanism in a later patch set.
Sounds good, but we might have to introduce this before we start doing
changing the core since this will probably be the preferable way the
discovery interact with core.
> Let me know of this makes sense to everyone.
>
>> Regards
>>
>> Marcel
>>
>
> Arman
--=20
Luiz Augusto von Dentz
Hi Marcel & Luiz,
> On Mon, Dec 1, 2014 at 9:19 AM, Marcel Holtmann <[email protected]> wro=
te:
> Hi Arman,
>
>>>>>> 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=
.
>>>>>
>>>>> Im trying to figure out the reason why you want to start with your ow=
n
>>>>> gatt_db, is it because it lacks reference counting, if that is the
>>>>> case it should be trivial to add it.
>>>>>
>>>>
>>>> Initially, yes, the lack of reference counting is one reason, which I
>>>> was thinking of adding to gatt-db eventually. Though, what I had in
>>>> mind was that, the gatt-db would be created by gatt-client if you want
>>>> it to perform discovery, otherwise if you construct it with gatt-db
>>>> then it wouldn't do discovery, which would address the permanent cache
>>>> case. So, we would have two "new" functions:
>>>>
>>>> bt_gatt_client_new
>>>> bt_gatt_client_new_from_db
>>>>
>>>> In the first case, if the upper layer wants to make the gatt-db
>>>> outlive the gatt-client, in the future they can just add a reference
>>>> to it and own it and in the next connection they can create the client
>>>> using that same db instance. This is kind of a rough idea right now
>>>> but I think it makes sense.
>>>>
>>>> We can also keep both functions but have both accept a gatt-db as a
>>>> parameter. Not sure what's best here really.
>>>
>>> For unit test maybe we can have bt_gatt_client_new_default, iirc
>>> Marcel suggested something like this for naming, but for the core
>>> daemon it can create a empty db when pairing so even the initial
>>> discovery would use it. This comes back to the idea of having the
>>> callbacks in the db to attribute added/removed, with this logic we
>>> could start creating D-Bus objects by the time a services is
>>> discovered (provided we discover everything necessary) and not only
>>> when bt_gatt_client signals it is done discovering, actually with the
>>> current concept we could be spamming the bus a little too much since
>>> the objects would be created all at same time this would cause several
>>> InterfacesAdded signals in a row at the end of pairing.
>>>
>>
>> I don't know about "_default" naming but are we talking about something =
like:
>>
>> bt_gatt_client_new(att, mtu, db) /* Does discovery */
>> bt_gatt_client_new_from_cache(att, mtu, db) /* No discovery */
>>
>> So both functions get passed a db either way?
>
> actually we might just better only use bt_gatt_client_new() and provide f=
unctionality to flush certain devices from the cache. In case we do want to=
trigger a discovery or re-discovery of devices.
>
> The cache should just work and do the right thing. We should be able set =
max cacheable service, devices etc. So lets just give the GATT client a cac=
he to work with and that is it. Everything else is an implementation detail=
between the client and the cache.
>
> I prefer that every client has a cache attached to it (or allow NULL in c=
ase we really want to run without a cache). If the cache is independent, it=
can easily do the right thing for us.
>
After our brief chat on IRC, please confirm that this is more or less
what we want:
bt_gatt_client_new(att, mtu, db) -> adds ref to db.
bt_gatt_client_new(att, mtu, NULL) -> creates its own db
Either function might cause discovery based on whether or not the db
is empty. If it's empty, client always starts discovery, otherwise it
skips it.
bt_gatt_client_get_db -> returns db. This would be necessary if we
want to allow passing NULL to bt_gatt_client_new.
I think all of these are minor details that can be revised later, but
I still need something as a starting point.
In addition, I'm thinking that gatt-db should have service added and
removed events that can be triggered by set_active and the clear
functions. Something like
gatt_db_register_service_callbacks(db, added_cb, removed_cb);
In the client case, added_cb would only be called once all attributes
belonging to the service have been discovered and inserted into the
db. Client would also communicate services that are affected by a
"Service Changed" indication via these callbacks. Either way, I
suggest adding the service callback mechanism in a later patch set.
Let me know of this makes sense to everyone.
> Regards
>
> Marcel
>
Arman
Hi Arman,
>>>>> 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.
>>>>
>>>> Im trying to figure out the reason why you want to start with your own
>>>> gatt_db, is it because it lacks reference counting, if that is the
>>>> case it should be trivial to add it.
>>>>
>>>
>>> Initially, yes, the lack of reference counting is one reason, which I
>>> was thinking of adding to gatt-db eventually. Though, what I had in
>>> mind was that, the gatt-db would be created by gatt-client if you want
>>> it to perform discovery, otherwise if you construct it with gatt-db
>>> then it wouldn't do discovery, which would address the permanent cache
>>> case. So, we would have two "new" functions:
>>>
>>> bt_gatt_client_new
>>> bt_gatt_client_new_from_db
>>>
>>> In the first case, if the upper layer wants to make the gatt-db
>>> outlive the gatt-client, in the future they can just add a reference
>>> to it and own it and in the next connection they can create the client
>>> using that same db instance. This is kind of a rough idea right now
>>> but I think it makes sense.
>>>
>>> We can also keep both functions but have both accept a gatt-db as a
>>> parameter. Not sure what's best here really.
>>
>> For unit test maybe we can have bt_gatt_client_new_default, iirc
>> Marcel suggested something like this for naming, but for the core
>> daemon it can create a empty db when pairing so even the initial
>> discovery would use it. This comes back to the idea of having the
>> callbacks in the db to attribute added/removed, with this logic we
>> could start creating D-Bus objects by the time a services is
>> discovered (provided we discover everything necessary) and not only
>> when bt_gatt_client signals it is done discovering, actually with the
>> current concept we could be spamming the bus a little too much since
>> the objects would be created all at same time this would cause several
>> InterfacesAdded signals in a row at the end of pairing.
>>
>
> I don't know about "_default" naming but are we talking about something like:
>
> bt_gatt_client_new(att, mtu, db) /* Does discovery */
> bt_gatt_client_new_from_cache(att, mtu, db) /* No discovery */
>
> So both functions get passed a db either way?
actually we might just better only use bt_gatt_client_new() and provide functionality to flush certain devices from the cache. In case we do want to trigger a discovery or re-discovery of devices.
The cache should just work and do the right thing. We should be able set max cacheable service, devices etc. So lets just give the GATT client a cache to work with and that is it. Everything else is an implementation detail between the client and the cache.
I prefer that every client has a cache attached to it (or allow NULL in case we really want to run without a cache). If the cache is independent, it can easily do the right thing for us.
Regards
Marcel
Hi Luiz,
> On Mon Dec 01 2014 at 8:02:47 AM Luiz Augusto von Dentz <[email protected]> wrote:
>
> Hi Arman,
>
> On Mon, Dec 1, 2014 at 4:50 PM, Arman Uguray <[email protected]> wrote:
> > Hi Luiz,
> >
> >> On Mon, Dec 1, 2014 at 1:45 AM, Luiz Augusto von Dentz <[email protected]> wrote:
> >> Hi Arman,
> >>
> >> On Fri, Nov 28, 2014 at 7:49 PM, Arman Uguray <[email protected]> wrote:
> >>> 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.
> >>
> >> Im trying to figure out the reason why you want to start with your own
> >> gatt_db, is it because it lacks reference counting, if that is the
> >> case it should be trivial to add it.
> >>
> >
> > Initially, yes, the lack of reference counting is one reason, which I
> > was thinking of adding to gatt-db eventually. Though, what I had in
> > mind was that, the gatt-db would be created by gatt-client if you want
> > it to perform discovery, otherwise if you construct it with gatt-db
> > then it wouldn't do discovery, which would address the permanent cache
> > case. So, we would have two "new" functions:
> >
> > bt_gatt_client_new
> > bt_gatt_client_new_from_db
> >
> > In the first case, if the upper layer wants to make the gatt-db
> > outlive the gatt-client, in the future they can just add a reference
> > to it and own it and in the next connection they can create the client
> > using that same db instance. This is kind of a rough idea right now
> > but I think it makes sense.
> >
> > We can also keep both functions but have both accept a gatt-db as a
> > parameter. Not sure what's best here really.
>
> For unit test maybe we can have bt_gatt_client_new_default, iirc
> Marcel suggested something like this for naming, but for the core
> daemon it can create a empty db when pairing so even the initial
> discovery would use it. This comes back to the idea of having the
> callbacks in the db to attribute added/removed, with this logic we
> could start creating D-Bus objects by the time a services is
> discovered (provided we discover everything necessary) and not only
> when bt_gatt_client signals it is done discovering, actually with the
> current concept we could be spamming the bus a little too much since
> the objects would be created all at same time this would cause several
> InterfacesAdded signals in a row at the end of pairing.
>
I don't know about "_default" naming but are we talking about something like:
bt_gatt_client_new(att, mtu, db) /* Does discovery */
bt_gatt_client_new_from_cache(att, mtu, db) /* No discovery */
So both functions get passed a db either way?
As for the signals, I think there will be several InterfacesAdded
signals in a row regardless, as objects for characteristics and
descriptors get exported, though either way is fine with me.
I think having these callbacks is OK but to me it makes more sense to
send a service_added signal with all of its attributes already
available in the database. Then the core can just act on that signal
by going through all characteristics and exporting objects for them.
If we're talking about an attribute_added signal for each attribute
then I don't think that will be easy to work with.
> Actually to be more clear, what Im thinking is that we could register
> org.bluez.GattService1 with empty Characteristics and Includes, same
> thing for org.bluez.GattCharacteristic1, or if we want to really do
> per service could register when a service becomes active, meaning the
> bt_gatt_client would control when a service can be registered by
> calling gatt_db_service_set_active thus causing a notification back to
> the core.
>
We can do this in an event driven way, though it's probably simpler to
have a service_added event, where the client can add all the
characteristics, includes, etc and then call set_active to trigger it.
> >>> ---
> >>> src/shared/gatt-client.c | 942 +++++++++++++++++++++--------------------------
> >>> src/shared/gatt-client.h | 2 +
> >>> 2 files changed, 430 insertions(+), 514 deletions(-)
> >>>
> >>> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
> >>> index 033cba1..2dc6735 100644
> >>> --- a/src/shared/gatt-client.c
> >>> +++ b/src/shared/gatt-client.c
> >>> @@ -27,6 +27,7 @@
> >>> #include "src/shared/gatt-helpers.h"
> >>> #include "src/shared/util.h"
> >>> #include "src/shared/queue.h"
> >>> +#include "src/shared/gatt-db.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);
> >>>
> >>> - op->cur_service->includes = includes;
> >>> - op->cur_service->num_includes = includes_count;
> >>> + tmp = gatt_db_get_attribute(client->db, start);
> >>> + if (!tmp)
> >>> + goto failed;
> >>> +
> >>> + 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,78 @@ 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 int discover_descs(struct discovery_op *op)
> >>> +{
> >>> + struct bt_gatt_client *client = op->client;
> >>> + struct gatt_db_attribute *attr;
> >>> + struct chrc *chrc_data;
> >>> + uint16_t desc_start;
> >>> +
> >>> + /*
> >>> + * This method returns the following three values:
> >>> + * -1: Failure
> >>> + * 0: No discovery started
> >>> + * 1: Discovery started
> >>> + */
> >>
> >> This is a bad sign if you have to explain what the return are, Id say
> >> it would probably be better if you return errno such as -EINVAL for
> >> errors and 0 for success and the caller can check if pending_chrcs is
> >> empty before calling this one.
> >>
> >
> > Hmm, pending_chrcs can be empty and discovery may have been initiated.
> > I'll just add a boolean parameter to indicate whether descriptor
> > discovery was started.
>
> Btw is there a reason we are not requesting every descriptor at once?
> Would it be a problem if we fail to discover some descriptors but
> proceed to discover the others? Because it looks like that this could
> be queued by bt_att itself but perhaps we need some way to
> cancel/abort if something goes wrong.
>
The bt_gatt_discover_descriptors function will call the callback once
all the descriptors within the given range have been discovered. In
other words, we are discovering all descriptors per characteristic
following the GATT spec.
If you're asking why we aren't just calling
bt_gatt_discover_descriptors for each characteristic in one loop
instead of having this step function, that's because attributes need
to be inserted into gatt-db in a certain order. Doing things in
sequence this way just makes keeping track of insertion order
straightforward. Otherwise, since each discovery procedure might
return at a different time, we would probably have to maintain a
separate list of descriptors per characteristic, keep track of when
all procedures have been completed, and then insert everything in
order then. This way seemed simpler to me.
> >>> + 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)) {
> >>> + free(chrc_data);
> >>> + return 1;
> >>> + }
> >>> +
> >>> + util_debug(client->debug_callback, client->debug_data,
> >>> + "Failed to start descriptor discovery");
> >>> + discovery_op_unref(op);
> >>> +
> >>> + goto failed;
> >>> + }
> >>> +
> >>> + free(chrc_data);
> >>> + return 0;
> >>> +
> >>> +failed:
> >>> + free(chrc_data);
> >>> + return -1;
> >>> +}
> >>> +
> >>> static void discover_descs_cb(bool success, uint8_t att_ecode,
> >>> struct bt_gatt_result *result,
> >>> void *user_data)
> >>> @@ -535,11 +499,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;
> >>> + int status;
> >>>
> >>> if (!success) {
> >>> if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
> >>> @@ -550,94 +516,69 @@ 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 (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;
> >>> -
> >>> - util_debug(client->debug_callback, client->debug_data,
> >>> - "Failed to start descriptor discovery");
> >>> - discovery_op_unref(op);
> >>> - success = false;
> >>> + status = discover_descs(op);
> >>> + if (status < 0)
> >>> + goto failed;
> >>>
> >>> - goto done;
> >>> - }
> >>> + if (status > 0)
> >>> + return;
> >>>
> >>> next:
> >>> - if (!op->cur_service->next)
> >>> + attr = queue_pop_head(op->pending_svcs);
> >>> + if (!attr)
> >>> 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 +586,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;
> >>> + int status;
> >>>
> >>> if (!success) {
> >>> if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
> >>> @@ -660,98 +605,76 @@ 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;
> >>> - }
> >>> -
> >>> - if (uuid_cmp(chrcs[i].chrc_external.uuid, SVC_CHNGD_UUID) == 0)
> >>> - client->svc_chngd_val_handle =
> >>> - chrcs[i].chrc_external.value_handle;
> >>> + start, end, value, properties, uuid_str);
> >>>
> >>> - i++;
> >>> - }
> >>> + chrc_data = new0(struct chrc, 1);
> >>> + if (!chrc_data)
> >>> + goto failed;
> >>>
> >>> - op->cur_service->chrcs = chrcs;
> >>> - op->cur_service->num_chrcs = chrc_count;
> >>> + chrc_data->start_handle = start;
> >>> + chrc_data->end_handle = end;
> >>> + chrc_data->value_handle = value;
> >>> + chrc_data->properties = properties;
> >>> + chrc_data->uuid = uuid;
> >>>
> >>> - 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;
> >>> -
> >>> - 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;
> >>> + queue_push_tail(op->pending_chrcs, chrc_data);
> >>> + }
> >>>
> >>> - util_debug(client->debug_callback, client->debug_data,
> >>> - "Failed to start descriptor discovery");
> >>> - discovery_op_unref(op);
> >>> - success = false;
> >>> + /*
> >>> + * Sequentially discover descriptors for each characteristic and insert
> >>> + * the characteristics into the database as we proceed.
> >>> + */
> >>> + status = discover_descs(op);
> >>> + if (status < 0)
> >>> + goto failed;
> >>>
> >>> - goto done;
> >>> - }
> >>> + if (status > 0)
> >>> + return;
> >>>
> >>> next:
> >>> - if (!op->cur_service->next)
> >>> + attr = queue_pop_head(op->pending_svcs);
> >>> + if (!attr)
> >>> 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);
> >>> + gatt_db_service_set_active(attr, true);
> >>> + 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 current 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,29 @@ 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;
> >>> + gatt_db_service_set_active(attr, true);
> >>> + 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 +835,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);
> >>> }
> >>>
> >>> @@ -971,7 +916,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 +926,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 +944,41 @@ next:
> >>> return;
-Arman
Hi Arman,
On Mon, Dec 1, 2014 at 4:50 PM, Arman Uguray <[email protected]> wrote:
> Hi Luiz,
>
>> On Mon, Dec 1, 2014 at 1:45 AM, Luiz Augusto von Dentz <[email protected]> wrote:
>> Hi Arman,
>>
>> On Fri, Nov 28, 2014 at 7:49 PM, Arman Uguray <[email protected]> wrote:
>>> 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.
>>
>> Im trying to figure out the reason why you want to start with your own
>> gatt_db, is it because it lacks reference counting, if that is the
>> case it should be trivial to add it.
>>
>
> Initially, yes, the lack of reference counting is one reason, which I
> was thinking of adding to gatt-db eventually. Though, what I had in
> mind was that, the gatt-db would be created by gatt-client if you want
> it to perform discovery, otherwise if you construct it with gatt-db
> then it wouldn't do discovery, which would address the permanent cache
> case. So, we would have two "new" functions:
>
> bt_gatt_client_new
> bt_gatt_client_new_from_db
>
> In the first case, if the upper layer wants to make the gatt-db
> outlive the gatt-client, in the future they can just add a reference
> to it and own it and in the next connection they can create the client
> using that same db instance. This is kind of a rough idea right now
> but I think it makes sense.
>
> We can also keep both functions but have both accept a gatt-db as a
> parameter. Not sure what's best here really.
For unit test maybe we can have bt_gatt_client_new_default, iirc
Marcel suggested something like this for naming, but for the core
daemon it can create a empty db when pairing so even the initial
discovery would use it. This comes back to the idea of having the
callbacks in the db to attribute added/removed, with this logic we
could start creating D-Bus objects by the time a services is
discovered (provided we discover everything necessary) and not only
when bt_gatt_client signals it is done discovering, actually with the
current concept we could be spamming the bus a little too much since
the objects would be created all at same time this would cause several
InterfacesAdded signals in a row at the end of pairing.
Actually to be more clear, what Im thinking is that we could register
org.bluez.GattService1 with empty Characteristics and Includes, same
thing for org.bluez.GattCharacteristic1, or if we want to really do
per service could register when a service becomes active, meaning the
bt_gatt_client would control when a service can be registered by
calling gatt_db_service_set_active thus causing a notification back to
the core.
>>> ---
>>> src/shared/gatt-client.c | 942 +++++++++++++++++++++--------------------------
>>> src/shared/gatt-client.h | 2 +
>>> 2 files changed, 430 insertions(+), 514 deletions(-)
>>>
>>> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
>>> index 033cba1..2dc6735 100644
>>> --- a/src/shared/gatt-client.c
>>> +++ b/src/shared/gatt-client.c
>>> @@ -27,6 +27,7 @@
>>> #include "src/shared/gatt-helpers.h"
>>> #include "src/shared/util.h"
>>> #include "src/shared/queue.h"
>>> +#include "src/shared/gatt-db.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);
>>>
>>> - op->cur_service->includes = includes;
>>> - op->cur_service->num_includes = includes_count;
>>> + tmp = gatt_db_get_attribute(client->db, start);
>>> + if (!tmp)
>>> + goto failed;
>>> +
>>> + 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,78 @@ 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 int discover_descs(struct discovery_op *op)
>>> +{
>>> + struct bt_gatt_client *client = op->client;
>>> + struct gatt_db_attribute *attr;
>>> + struct chrc *chrc_data;
>>> + uint16_t desc_start;
>>> +
>>> + /*
>>> + * This method returns the following three values:
>>> + * -1: Failure
>>> + * 0: No discovery started
>>> + * 1: Discovery started
>>> + */
>>
>> This is a bad sign if you have to explain what the return are, Id say
>> it would probably be better if you return errno such as -EINVAL for
>> errors and 0 for success and the caller can check if pending_chrcs is
>> empty before calling this one.
>>
>
> Hmm, pending_chrcs can be empty and discovery may have been initiated.
> I'll just add a boolean parameter to indicate whether descriptor
> discovery was started.
Btw is there a reason we are not requesting every descriptor at once?
Would it be a problem if we fail to discover some descriptors but
proceed to discover the others? Because it looks like that this could
be queued by bt_att itself but perhaps we need some way to
cancel/abort if something goes wrong.
>>> + 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)) {
>>> + free(chrc_data);
>>> + return 1;
>>> + }
>>> +
>>> + util_debug(client->debug_callback, client->debug_data,
>>> + "Failed to start descriptor discovery");
>>> + discovery_op_unref(op);
>>> +
>>> + goto failed;
>>> + }
>>> +
>>> + free(chrc_data);
>>> + return 0;
>>> +
>>> +failed:
>>> + free(chrc_data);
>>> + return -1;
>>> +}
>>> +
>>> static void discover_descs_cb(bool success, uint8_t att_ecode,
>>> struct bt_gatt_result *result,
>>> void *user_data)
>>> @@ -535,11 +499,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;
>>> + int status;
>>>
>>> if (!success) {
>>> if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
>>> @@ -550,94 +516,69 @@ 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 (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;
>>> -
>>> - util_debug(client->debug_callback, client->debug_data,
>>> - "Failed to start descriptor discovery");
>>> - discovery_op_unref(op);
>>> - success = false;
>>> + status = discover_descs(op);
>>> + if (status < 0)
>>> + goto failed;
>>>
>>> - goto done;
>>> - }
>>> + if (status > 0)
>>> + return;
>>>
>>> next:
>>> - if (!op->cur_service->next)
>>> + attr = queue_pop_head(op->pending_svcs);
>>> + if (!attr)
>>> 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 +586,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;
>>> + int status;
>>>
>>> if (!success) {
>>> if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
>>> @@ -660,98 +605,76 @@ 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;
>>> - }
>>> -
>>> - if (uuid_cmp(chrcs[i].chrc_external.uuid, SVC_CHNGD_UUID) == 0)
>>> - client->svc_chngd_val_handle =
>>> - chrcs[i].chrc_external.value_handle;
>>> + start, end, value, properties, uuid_str);
>>>
>>> - i++;
>>> - }
>>> + chrc_data = new0(struct chrc, 1);
>>> + if (!chrc_data)
>>> + goto failed;
>>>
>>> - op->cur_service->chrcs = chrcs;
>>> - op->cur_service->num_chrcs = chrc_count;
>>> + chrc_data->start_handle = start;
>>> + chrc_data->end_handle = end;
>>> + chrc_data->value_handle = value;
>>> + chrc_data->properties = properties;
>>> + chrc_data->uuid = uuid;
>>>
>>> - 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;
>>> -
>>> - 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;
>>> + queue_push_tail(op->pending_chrcs, chrc_data);
>>> + }
>>>
>>> - util_debug(client->debug_callback, client->debug_data,
>>> - "Failed to start descriptor discovery");
>>> - discovery_op_unref(op);
>>> - success = false;
>>> + /*
>>> + * Sequentially discover descriptors for each characteristic and insert
>>> + * the characteristics into the database as we proceed.
>>> + */
>>> + status = discover_descs(op);
>>> + if (status < 0)
>>> + goto failed;
>>>
>>> - goto done;
>>> - }
>>> + if (status > 0)
>>> + return;
>>>
>>> next:
>>> - if (!op->cur_service->next)
>>> + attr = queue_pop_head(op->pending_svcs);
>>> + if (!attr)
>>> 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);
>>> + gatt_db_service_set_active(attr, true);
>>> + 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 current 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,29 @@ 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;
>>> + gatt_db_service_set_active(attr, true);
>>> + 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 +835,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);
>>> }
>>>
>>> @@ -971,7 +916,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 +926,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 +944,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 +992,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);
>>> -
>>> - 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;
>>> - }
>>> + gatt_db_clear_range(client->db, start_handle, end_handle);
>>>
>>> - 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 +1023,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 +1083,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 +1115,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 +1130,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 +1148,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 +1383,8 @@ static void bt_gatt_client_free(struct bt_gatt_client *client)
>>> bt_att_unref(client->att);
>>> }
>>>
>>> - gatt_client_clear_services(client);
>>> + if (client->db)
>>> + gatt_db_destroy(client->db);
>>>
>>> queue_destroy(client->svc_chngd_queue, free);
>>> queue_destroy(client->long_write_queue, long_write_op_unref);
>>> @@ -1507,6 +1448,10 @@ struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu)
>>> if (!client->ind_id)
>>> goto fail;
>>>
>>> + client->db = gatt_db_new();
>>> + if (!client->db)
>>> + goto fail;
>>> +
>>> client->att = bt_att_ref(att);
>>>
>>> if (!gatt_client_init(client, mtu))
>>> @@ -1598,6 +1543,14 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
>>> return true;
>>> }
>>>
>>> +struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client)
>>> +{
>>> + if (!client || !client->ready || client->in_svc_chngd)
>>> + return NULL;
>>> +
>>> + return client->db;
>>> +}
>>> +
>>> bool bt_gatt_service_iter_init(struct bt_gatt_service_iter *iter,
>>> struct bt_gatt_client *client)
>>> {
>>> @@ -1617,25 +1570,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 +1614,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 +1634,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 +2391,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 +2413,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;
>>
>> It looks like these would be breaking the unit tests, which I guess
>> would be quite hard to avoid except if we do it in single patch.
>>
>
> Unfortunately yes, though the rest of the patch set fixes the tests so
> it makes sense to apply them all together if we can.
Fair enough.
>>> /* 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..0309e5e 100644
>>> --- a/src/shared/gatt-client.h
>>> +++ b/src/shared/gatt-client.h
>>> @@ -65,6 +65,8 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
>>> void *user_data,
>>> bt_gatt_client_destroy_func_t destroy);
>>>
>>> +struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client);
>>
>> This go along with my first comment, you would not need to have such
>> function if the db is passed on new.
>>
>
> See my response to your comment above. Also I think this is generally
> useful for upper layers to be able to obtain "the db assigned to a
> gatt-client", though it depends on what we decide with regards to how
> the client should be constructed.
>
>>> typedef struct {
>>> bool primary;
>>> uint16_t start_handle;
>>> --
>>> 2.2.0.rc0.207.ga3a616c
>>>
>>> --
>>> 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
>
> Thanks,
> Arman
--
Luiz Augusto von Dentz
Hi Luiz,
> On Mon, Dec 1, 2014 at 1:45 AM, Luiz Augusto von Dentz <[email protected]> wrote:
> Hi Arman,
>
> On Fri, Nov 28, 2014 at 7:49 PM, Arman Uguray <[email protected]> wrote:
>> 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.
>
> Im trying to figure out the reason why you want to start with your own
> gatt_db, is it because it lacks reference counting, if that is the
> case it should be trivial to add it.
>
Initially, yes, the lack of reference counting is one reason, which I
was thinking of adding to gatt-db eventually. Though, what I had in
mind was that, the gatt-db would be created by gatt-client if you want
it to perform discovery, otherwise if you construct it with gatt-db
then it wouldn't do discovery, which would address the permanent cache
case. So, we would have two "new" functions:
bt_gatt_client_new
bt_gatt_client_new_from_db
In the first case, if the upper layer wants to make the gatt-db
outlive the gatt-client, in the future they can just add a reference
to it and own it and in the next connection they can create the client
using that same db instance. This is kind of a rough idea right now
but I think it makes sense.
We can also keep both functions but have both accept a gatt-db as a
parameter. Not sure what's best here really.
>> ---
>> src/shared/gatt-client.c | 942 +++++++++++++++++++++--------------------------
>> src/shared/gatt-client.h | 2 +
>> 2 files changed, 430 insertions(+), 514 deletions(-)
>>
>> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
>> index 033cba1..2dc6735 100644
>> --- a/src/shared/gatt-client.c
>> +++ b/src/shared/gatt-client.c
>> @@ -27,6 +27,7 @@
>> #include "src/shared/gatt-helpers.h"
>> #include "src/shared/util.h"
>> #include "src/shared/queue.h"
>> +#include "src/shared/gatt-db.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);
>>
>> - op->cur_service->includes = includes;
>> - op->cur_service->num_includes = includes_count;
>> + tmp = gatt_db_get_attribute(client->db, start);
>> + if (!tmp)
>> + goto failed;
>> +
>> + 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,78 @@ 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 int discover_descs(struct discovery_op *op)
>> +{
>> + struct bt_gatt_client *client = op->client;
>> + struct gatt_db_attribute *attr;
>> + struct chrc *chrc_data;
>> + uint16_t desc_start;
>> +
>> + /*
>> + * This method returns the following three values:
>> + * -1: Failure
>> + * 0: No discovery started
>> + * 1: Discovery started
>> + */
>
> This is a bad sign if you have to explain what the return are, Id say
> it would probably be better if you return errno such as -EINVAL for
> errors and 0 for success and the caller can check if pending_chrcs is
> empty before calling this one.
>
Hmm, pending_chrcs can be empty and discovery may have been initiated.
I'll just add a boolean parameter to indicate whether descriptor
discovery was started.
>> + 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)) {
>> + free(chrc_data);
>> + return 1;
>> + }
>> +
>> + util_debug(client->debug_callback, client->debug_data,
>> + "Failed to start descriptor discovery");
>> + discovery_op_unref(op);
>> +
>> + goto failed;
>> + }
>> +
>> + free(chrc_data);
>> + return 0;
>> +
>> +failed:
>> + free(chrc_data);
>> + return -1;
>> +}
>> +
>> static void discover_descs_cb(bool success, uint8_t att_ecode,
>> struct bt_gatt_result *result,
>> void *user_data)
>> @@ -535,11 +499,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;
>> + int status;
>>
>> if (!success) {
>> if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
>> @@ -550,94 +516,69 @@ 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 (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;
>> -
>> - util_debug(client->debug_callback, client->debug_data,
>> - "Failed to start descriptor discovery");
>> - discovery_op_unref(op);
>> - success = false;
>> + status = discover_descs(op);
>> + if (status < 0)
>> + goto failed;
>>
>> - goto done;
>> - }
>> + if (status > 0)
>> + return;
>>
>> next:
>> - if (!op->cur_service->next)
>> + attr = queue_pop_head(op->pending_svcs);
>> + if (!attr)
>> 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 +586,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;
>> + int status;
>>
>> if (!success) {
>> if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
>> @@ -660,98 +605,76 @@ 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;
>> - }
>> -
>> - if (uuid_cmp(chrcs[i].chrc_external.uuid, SVC_CHNGD_UUID) == 0)
>> - client->svc_chngd_val_handle =
>> - chrcs[i].chrc_external.value_handle;
>> + start, end, value, properties, uuid_str);
>>
>> - i++;
>> - }
>> + chrc_data = new0(struct chrc, 1);
>> + if (!chrc_data)
>> + goto failed;
>>
>> - op->cur_service->chrcs = chrcs;
>> - op->cur_service->num_chrcs = chrc_count;
>> + chrc_data->start_handle = start;
>> + chrc_data->end_handle = end;
>> + chrc_data->value_handle = value;
>> + chrc_data->properties = properties;
>> + chrc_data->uuid = uuid;
>>
>> - 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;
>> -
>> - 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;
>> + queue_push_tail(op->pending_chrcs, chrc_data);
>> + }
>>
>> - util_debug(client->debug_callback, client->debug_data,
>> - "Failed to start descriptor discovery");
>> - discovery_op_unref(op);
>> - success = false;
>> + /*
>> + * Sequentially discover descriptors for each characteristic and insert
>> + * the characteristics into the database as we proceed.
>> + */
>> + status = discover_descs(op);
>> + if (status < 0)
>> + goto failed;
>>
>> - goto done;
>> - }
>> + if (status > 0)
>> + return;
>>
>> next:
>> - if (!op->cur_service->next)
>> + attr = queue_pop_head(op->pending_svcs);
>> + if (!attr)
>> 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);
>> + gatt_db_service_set_active(attr, true);
>> + 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 current 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,29 @@ 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;
>> + gatt_db_service_set_active(attr, true);
>> + 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 +835,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);
>> }
>>
>> @@ -971,7 +916,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 +926,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 +944,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 +992,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);
>> -
>> - 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;
>> - }
>> + gatt_db_clear_range(client->db, start_handle, end_handle);
>>
>> - 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 +1023,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 +1083,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 +1115,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 +1130,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 +1148,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 +1383,8 @@ static void bt_gatt_client_free(struct bt_gatt_client *client)
>> bt_att_unref(client->att);
>> }
>>
>> - gatt_client_clear_services(client);
>> + if (client->db)
>> + gatt_db_destroy(client->db);
>>
>> queue_destroy(client->svc_chngd_queue, free);
>> queue_destroy(client->long_write_queue, long_write_op_unref);
>> @@ -1507,6 +1448,10 @@ struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu)
>> if (!client->ind_id)
>> goto fail;
>>
>> + client->db = gatt_db_new();
>> + if (!client->db)
>> + goto fail;
>> +
>> client->att = bt_att_ref(att);
>>
>> if (!gatt_client_init(client, mtu))
>> @@ -1598,6 +1543,14 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
>> return true;
>> }
>>
>> +struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client)
>> +{
>> + if (!client || !client->ready || client->in_svc_chngd)
>> + return NULL;
>> +
>> + return client->db;
>> +}
>> +
>> bool bt_gatt_service_iter_init(struct bt_gatt_service_iter *iter,
>> struct bt_gatt_client *client)
>> {
>> @@ -1617,25 +1570,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 +1614,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 +1634,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 +2391,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 +2413,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;
>
> It looks like these would be breaking the unit tests, which I guess
> would be quite hard to avoid except if we do it in single patch.
>
Unfortunately yes, though the rest of the patch set fixes the tests so
it makes sense to apply them all together if we can.
>> /* 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..0309e5e 100644
>> --- a/src/shared/gatt-client.h
>> +++ b/src/shared/gatt-client.h
>> @@ -65,6 +65,8 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
>> void *user_data,
>> bt_gatt_client_destroy_func_t destroy);
>>
>> +struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client);
>
> This go along with my first comment, you would not need to have such
> function if the db is passed on new.
>
See my response to your comment above. Also I think this is generally
useful for upper layers to be able to obtain "the db assigned to a
gatt-client", though it depends on what we decide with regards to how
the client should be constructed.
>> typedef struct {
>> bool primary;
>> uint16_t start_handle;
>> --
>> 2.2.0.rc0.207.ga3a616c
>>
>> --
>> 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
Thanks,
Arman
Hi Arman,
On Fri, Nov 28, 2014 at 7:49 PM, Arman Uguray <[email protected]> wrote:
> 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: Fix bug in maximum handle check.
> shared/gatt-db: Add helper to get service handles.
> 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
>
> src/shared/gatt-client.c | 1189 +++++++++++++++++++++-------------------------
> src/shared/gatt-client.h | 73 +--
> src/shared/gatt-db.c | 41 +-
> tools/btgatt-client.c | 175 +++----
> tools/btgatt-server.c | 90 ++++
> unit/test-gatt.c | 241 +++++++---
> 6 files changed, 911 insertions(+), 898 deletions(-)
>
> --
> 2.2.0.rc0.207.ga3a616c
Ive applied the first 2, please check the comments about the third.
--
Luiz Augusto von Dentz
Hi Arman,
On Fri, Nov 28, 2014 at 7:49 PM, Arman Uguray <[email protected]> wrote:
> 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.
Im trying to figure out the reason why you want to start with your own
gatt_db, is it because it lacks reference counting, if that is the
case it should be trivial to add it.
> ---
> src/shared/gatt-client.c | 942 +++++++++++++++++++++--------------------------
> src/shared/gatt-client.h | 2 +
> 2 files changed, 430 insertions(+), 514 deletions(-)
>
> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
> index 033cba1..2dc6735 100644
> --- a/src/shared/gatt-client.c
> +++ b/src/shared/gatt-client.c
> @@ -27,6 +27,7 @@
> #include "src/shared/gatt-helpers.h"
> #include "src/shared/util.h"
> #include "src/shared/queue.h"
> +#include "src/shared/gatt-db.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);
>
> - op->cur_service->includes = includes;
> - op->cur_service->num_includes = includes_count;
> + tmp = gatt_db_get_attribute(client->db, start);
> + if (!tmp)
> + goto failed;
> +
> + 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,78 @@ 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 int discover_descs(struct discovery_op *op)
> +{
> + struct bt_gatt_client *client = op->client;
> + struct gatt_db_attribute *attr;
> + struct chrc *chrc_data;
> + uint16_t desc_start;
> +
> + /*
> + * This method returns the following three values:
> + * -1: Failure
> + * 0: No discovery started
> + * 1: Discovery started
> + */
This is a bad sign if you have to explain what the return are, Id say
it would probably be better if you return errno such as -EINVAL for
errors and 0 for success and the caller can check if pending_chrcs is
empty before calling this one.
> + 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)) {
> + free(chrc_data);
> + return 1;
> + }
> +
> + util_debug(client->debug_callback, client->debug_data,
> + "Failed to start descriptor discovery");
> + discovery_op_unref(op);
> +
> + goto failed;
> + }
> +
> + free(chrc_data);
> + return 0;
> +
> +failed:
> + free(chrc_data);
> + return -1;
> +}
> +
> static void discover_descs_cb(bool success, uint8_t att_ecode,
> struct bt_gatt_result *result,
> void *user_data)
> @@ -535,11 +499,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;
> + int status;
>
> if (!success) {
> if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
> @@ -550,94 +516,69 @@ 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 (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;
> -
> - util_debug(client->debug_callback, client->debug_data,
> - "Failed to start descriptor discovery");
> - discovery_op_unref(op);
> - success = false;
> + status = discover_descs(op);
> + if (status < 0)
> + goto failed;
>
> - goto done;
> - }
> + if (status > 0)
> + return;
>
> next:
> - if (!op->cur_service->next)
> + attr = queue_pop_head(op->pending_svcs);
> + if (!attr)
> 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 +586,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;
> + int status;
>
> if (!success) {
> if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
> @@ -660,98 +605,76 @@ 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;
> - }
> -
> - if (uuid_cmp(chrcs[i].chrc_external.uuid, SVC_CHNGD_UUID) == 0)
> - client->svc_chngd_val_handle =
> - chrcs[i].chrc_external.value_handle;
> + start, end, value, properties, uuid_str);
>
> - i++;
> - }
> + chrc_data = new0(struct chrc, 1);
> + if (!chrc_data)
> + goto failed;
>
> - op->cur_service->chrcs = chrcs;
> - op->cur_service->num_chrcs = chrc_count;
> + chrc_data->start_handle = start;
> + chrc_data->end_handle = end;
> + chrc_data->value_handle = value;
> + chrc_data->properties = properties;
> + chrc_data->uuid = uuid;
>
> - 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;
> -
> - 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;
> + queue_push_tail(op->pending_chrcs, chrc_data);
> + }
>
> - util_debug(client->debug_callback, client->debug_data,
> - "Failed to start descriptor discovery");
> - discovery_op_unref(op);
> - success = false;
> + /*
> + * Sequentially discover descriptors for each characteristic and insert
> + * the characteristics into the database as we proceed.
> + */
> + status = discover_descs(op);
> + if (status < 0)
> + goto failed;
>
> - goto done;
> - }
> + if (status > 0)
> + return;
>
> next:
> - if (!op->cur_service->next)
> + attr = queue_pop_head(op->pending_svcs);
> + if (!attr)
> 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);
> + gatt_db_service_set_active(attr, true);
> + 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 current 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,29 @@ 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;
> + gatt_db_service_set_active(attr, true);
> + 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 +835,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);
> }
>
> @@ -971,7 +916,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 +926,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 +944,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 +992,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);
> -
> - 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;
> - }
> + gatt_db_clear_range(client->db, start_handle, end_handle);
>
> - 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 +1023,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 +1083,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 +1115,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 +1130,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 +1148,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 +1383,8 @@ static void bt_gatt_client_free(struct bt_gatt_client *client)
> bt_att_unref(client->att);
> }
>
> - gatt_client_clear_services(client);
> + if (client->db)
> + gatt_db_destroy(client->db);
>
> queue_destroy(client->svc_chngd_queue, free);
> queue_destroy(client->long_write_queue, long_write_op_unref);
> @@ -1507,6 +1448,10 @@ struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu)
> if (!client->ind_id)
> goto fail;
>
> + client->db = gatt_db_new();
> + if (!client->db)
> + goto fail;
> +
> client->att = bt_att_ref(att);
>
> if (!gatt_client_init(client, mtu))
> @@ -1598,6 +1543,14 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
> return true;
> }
>
> +struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client)
> +{
> + if (!client || !client->ready || client->in_svc_chngd)
> + return NULL;
> +
> + return client->db;
> +}
> +
> bool bt_gatt_service_iter_init(struct bt_gatt_service_iter *iter,
> struct bt_gatt_client *client)
> {
> @@ -1617,25 +1570,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 +1614,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 +1634,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 +2391,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 +2413,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;
It looks like these would be breaking the unit tests, which I guess
would be quite hard to avoid except if we do it in single patch.
> /* 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..0309e5e 100644
> --- a/src/shared/gatt-client.h
> +++ b/src/shared/gatt-client.h
> @@ -65,6 +65,8 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
> void *user_data,
> bt_gatt_client_destroy_func_t destroy);
>
> +struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client);
This go along with my first comment, you would not need to have such
function if the db is passed on new.
> typedef struct {
> bool primary;
> uint16_t start_handle;
> --
> 2.2.0.rc0.207.ga3a616c
>
> --
> 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