* v1:
- Moved the TODOs to the top-level TODO file in a new ATT/GATT section.
- Removed bt_gatt_client_init. Instead, added a
bt_gatt_client_set_ready_handler to set up a handler which gets called when
discovery is over. Discovery is initiated in bt_gatt_client_new instead.
- Removed bt_gatt_client_get_att.
- Removed the (un)register_service_changed functions for now.
- Removed bt_gatt_client_destroy in favor of ref/unref functions.
- Dropped "_handler" from bt_att_(un)register_disconnect_handler functions.
Arman Uguray (13):
TODO: Add items for tasks involving the new shared ATT/GATT stack
shared/gatt-helpers: Remove service, characteristic, descriptor
structures.
shared/gatt-helpers: Added result count functions.
shared/gatt-client: Added initial skeleton and simple functions.
shared/att: Support multiple disconnect handlers.
shared/att: Add BT_ATT_DEFAULT_LE_MTU macro.
shared/gatt-helpers: Fixed typo in callback args.
shared/gatt-client: Add bt_gatt_client_set_debug.
shared/gatt-client: Implement initial service discovery.
shared/gatt-client: Add service iterator functions.
shared/gatt-client: Implement characteristic discovery.
shared/gatt-client: Implement descriptor discovery.
TODO: shared/gatt-client has been defined and discovery implemented
Makefile.am | 3 +-
TODO | 90 ++++++-
src/shared/att-types.h | 2 +
src/shared/att.c | 161 ++++++++++--
src/shared/att.h | 11 +-
src/shared/gatt-client.c | 652 ++++++++++++++++++++++++++++++++++++++++++++++
src/shared/gatt-client.h | 90 +++++++
src/shared/gatt-helpers.c | 100 +++++--
src/shared/gatt-helpers.h | 36 +--
9 files changed, 1064 insertions(+), 81 deletions(-)
create mode 100644 src/shared/gatt-client.c
create mode 100644 src/shared/gatt-client.h
--
2.1.0.rc2.206.gedb03e5
Hi Marcel,
> Can you just rebase your patches against latest git so that I can apply them.
>
Rebased and fixed the blank line. I just sent out v2 with the fixes.
Let me know if you run into any issues.
Thanks,
Arman
Hi Arman,
> * v1:
> - Moved the TODOs to the top-level TODO file in a new ATT/GATT section.
> - Removed bt_gatt_client_init. Instead, added a
> bt_gatt_client_set_ready_handler to set up a handler which gets called when
> discovery is over. Discovery is initiated in bt_gatt_client_new instead.
> - Removed bt_gatt_client_get_att.
> - Removed the (un)register_service_changed functions for now.
> - Removed bt_gatt_client_destroy in favor of ref/unref functions.
> - Dropped "_handler" from bt_att_(un)register_disconnect_handler functions.
>
> Arman Uguray (13):
> TODO: Add items for tasks involving the new shared ATT/GATT stack
> shared/gatt-helpers: Remove service, characteristic, descriptor
> structures.
> shared/gatt-helpers: Added result count functions.
> shared/gatt-client: Added initial skeleton and simple functions.
> shared/att: Support multiple disconnect handlers.
> shared/att: Add BT_ATT_DEFAULT_LE_MTU macro.
> shared/gatt-helpers: Fixed typo in callback args.
> shared/gatt-client: Add bt_gatt_client_set_debug.
> shared/gatt-client: Implement initial service discovery.
> shared/gatt-client: Add service iterator functions.
> shared/gatt-client: Implement characteristic discovery.
> shared/gatt-client: Implement descriptor discovery.
> TODO: shared/gatt-client has been defined and discovery implemented
>
> Makefile.am | 3 +-
> TODO | 90 ++++++-
> src/shared/att-types.h | 2 +
> src/shared/att.c | 161 ++++++++++--
> src/shared/att.h | 11 +-
> src/shared/gatt-client.c | 652 ++++++++++++++++++++++++++++++++++++++++++++++
> src/shared/gatt-client.h | 90 +++++++
> src/shared/gatt-helpers.c | 100 +++++--
> src/shared/gatt-helpers.h | 36 +--
> 9 files changed, 1064 insertions(+), 81 deletions(-)
> create mode 100644 src/shared/gatt-client.c
> create mode 100644 src/shared/gatt-client.h
sorry for the delay, I never fully got through the whole set in one go. I tried to apply it today, but I ran into some issues.
/data/devel/x/.git/rebase-apply/patch:155: new blank line at EOF.
+
fatal: 1 line adds whitespace errors.
Patch failed at 0004 shared/gatt-client: Added initial skeleton and simple functions.
We are using struct checks here and you need to make sure you do not have any extra empty lines at the end of your files.
The more problematic one is this.
error: patch failed: src/shared/gatt-client.h:34
error: src/shared/gatt-client.h: patch does not apply
Patch failed at 0008 shared/gatt-client: Add bt_gatt_client_set_debug.
Can you just rebase your patches against latest git so that I can apply them.
Regards
Marcel
This patch introduces high-level structures and functions for iterating through,
and storing, data about the discovered services.
---
src/shared/gatt-client.c | 148 +++++++++++++++++++++++++++++++++++++++++++++--
src/shared/gatt-client.h | 41 +++++++++++++
2 files changed, 185 insertions(+), 4 deletions(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 7a76234..17b2f56 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -31,6 +31,13 @@
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
+#define UUID_BYTES BT_GATT_UUID_SIZE * sizeof(uint8_t)
+
+struct service_list {
+ bt_gatt_service_t service;
+ struct service_list *next;
+};
+
struct bt_gatt_client {
struct bt_att *att;
int ref_count;
@@ -43,9 +50,61 @@ struct bt_gatt_client {
bt_gatt_client_destroy_func_t debug_destroy;
void *debug_data;
+ struct service_list *svc_head, *svc_tail;
bool in_init;
+ bool ready;
};
+static bool gatt_client_add_service(struct bt_gatt_client *client,
+ uint16_t start, uint16_t end,
+ uint8_t uuid[BT_GATT_UUID_SIZE])
+{
+ struct service_list *list;
+
+ list = new0(struct service_list, 1);
+ if (!list)
+ return false;
+
+ list->service.start_handle = start;
+ list->service.end_handle = end;
+ memcpy(list->service.uuid, uuid, UUID_BYTES);
+
+ if (!client->svc_head)
+ client->svc_head = client->svc_tail = list;
+ else {
+ client->svc_tail->next = list;
+ client->svc_tail = list;
+ }
+
+ return true;
+}
+
+static void service_destroy_characteristics(bt_gatt_service_t *service)
+{
+ unsigned int i;
+
+ for (i = 0; i < service->num_chrcs; i++)
+ free((bt_gatt_descriptor_t *) service->chrcs[i].descs);
+
+ free((bt_gatt_characteristic_t *) service->chrcs);
+}
+
+static void gatt_client_clear_services(struct bt_gatt_client *client)
+{
+ struct service_list *l, *tmp;
+
+ l = client->svc_head;
+
+ while (l) {
+ service_destroy_characteristics(&l->service);
+ tmp = l;
+ l = tmp->next;
+ free(tmp);
+ }
+
+ client->svc_head = client->svc_tail = NULL;
+}
+
struct async_op {
struct bt_gatt_client *client;
int ref_count;
@@ -76,7 +135,7 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
struct bt_gatt_client *client = op->client;
struct bt_gatt_iter iter;
uint16_t start, end;
- uint8_t uuid[16];
+ uint8_t uuid[BT_GATT_UUID_SIZE];
char uuid_str[MAX_LEN_UUID_STR];
bt_uuid_t tmp;
@@ -98,8 +157,6 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
bt_gatt_result_service_count(result));
while (bt_gatt_iter_next_service(&iter, &start, &end, uuid)) {
- /* TODO: discover characteristics and store the service. */
-
/* Log debug message. */
tmp.type = BT_UUID128;
memcpy(tmp.value.u128.data, uuid, sizeof(uuid));
@@ -107,11 +164,25 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
util_debug(client->debug_callback, client->debug_data,
"start: 0x%04x, end: 0x%04x, uuid: %s",
start, end, uuid_str);
+
+ /* Store the service */
+ if (!gatt_client_add_service(client, start, end, uuid)) {
+ util_debug(client->debug_callback, client->debug_data,
+ "Failed to store service");
+
+ gatt_client_clear_services(client);
+
+ success = false;
+ goto done;
+ }
}
done:
client->in_init = false;
+ if (success)
+ client->ready = true;
+
if (client->ready_callback)
client->ready_callback(success, att_ecode, client->ready_data);
}
@@ -160,7 +231,7 @@ static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
{
struct async_op *op;
- if (client->in_init)
+ if (client->in_init || client->ready)
return false;
op = new0(struct async_op, 1);
@@ -231,6 +302,11 @@ void bt_gatt_client_unref(struct bt_gatt_client *client)
free(client);
}
+bool bt_gatt_client_is_ready(struct bt_gatt_client *client)
+{
+ return (client && client->ready);
+}
+
bool bt_gatt_client_set_ready_handler(struct bt_gatt_client *client,
bt_gatt_client_callback_t callback,
void *user_data,
@@ -265,3 +341,67 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
return true;
}
+
+bool bt_gatt_service_iter_init(struct bt_gatt_service_iter *iter,
+ struct bt_gatt_client *client)
+{
+ if (!iter || !client)
+ return false;
+
+ if (client->in_init)
+ 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,
+ bt_gatt_service_t *service)
+{
+ struct service_list *l;
+
+ 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;
+}
+
+bool bt_gatt_service_iter_next_by_handle(struct bt_gatt_service_iter *iter,
+ uint16_t start_handle,
+ 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],
+ 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;
+}
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index 45c6010..f724c3c 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -23,6 +23,9 @@
#include <stdbool.h>
#include <stdint.h>
+#include <stddef.h>
+
+#define BT_GATT_UUID_SIZE 16
struct bt_gatt_client;
@@ -36,6 +39,7 @@ 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);
+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,
void *user_data,
@@ -46,3 +50,40 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
+typedef struct {
+ uint16_t handle;
+ uint8_t uuid[BT_GATT_UUID_SIZE];
+} bt_gatt_descriptor_t;
+
+typedef struct {
+ uint16_t 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 start_handle;
+ uint16_t end_handle;
+ uint8_t uuid[BT_GATT_UUID_SIZE];
+ const bt_gatt_characteristic_t *chrcs;
+ size_t num_chrcs;
+} bt_gatt_service_t;
+
+struct bt_gatt_service_iter {
+ struct bt_gatt_client *client;
+ void *ptr;
+};
+
+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,
+ bt_gatt_service_t *service);
+bool bt_gatt_service_iter_next_by_handle(struct bt_gatt_service_iter *iter,
+ uint16_t start_handle,
+ 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],
+ bt_gatt_service_t *service);
--
2.1.0.rc2.206.gedb03e5
This patch implements characteristic descriptor discovery as part of the client
initialization flow.
---
src/shared/gatt-client.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 131 insertions(+)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 6b18e91..ba27224 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -108,6 +108,8 @@ static void gatt_client_clear_services(struct bt_gatt_client *client)
struct async_op {
struct bt_gatt_client *client;
struct service_list *cur_service;
+ bt_gatt_characteristic_t *cur_chrc;
+ int cur_chrc_index;
int ref_count;
};
@@ -156,6 +158,112 @@ static void uuid_to_string(const uint8_t uuid[BT_GATT_UUID_SIZE],
static void discover_chrcs_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
+ void *user_data);
+
+static void discover_descs_cb(bool success, uint8_t att_ecode,
+ struct bt_gatt_result *result,
+ void *user_data)
+{
+ struct async_op *op = user_data;
+ struct bt_gatt_client *client = op->client;
+ struct bt_gatt_iter iter;
+ char uuid_str[MAX_LEN_UUID_STR];
+ unsigned int desc_count;
+ uint16_t desc_start;
+ unsigned int i;
+ bt_gatt_descriptor_t *descs;
+
+ if (!success) {
+ if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
+ success = true;
+ goto next;
+ }
+
+ goto done;
+ }
+
+ if (!result || !bt_gatt_iter_init(&iter, result)) {
+ success = false;
+ goto done;
+ }
+
+ desc_count = bt_gatt_result_descriptor_count(result);
+ if (desc_count == 0) {
+ success = false;
+ goto done;
+ }
+
+ 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;
+ }
+
+ i = 0;
+ while (bt_gatt_iter_next_descriptor(&iter, &descs[i].handle,
+ descs[i].uuid)) {
+ uuid_to_string(descs[i].uuid, uuid_str);
+ util_debug(client->debug_callback, client->debug_data,
+ "handle: 0x%04x, uuid: %s",
+ descs[i].handle, uuid_str);
+ i++;
+ }
+
+ op->cur_chrc->num_descs = desc_count;
+ op->cur_chrc->descs = descs;
+
+ for (i = op->cur_chrc_index;
+ i < op->cur_service->service.num_chrcs; i++) {
+ op->cur_chrc_index = i;
+ op->cur_chrc++;
+ desc_start = op->cur_chrc->value_handle + 1;
+ if (desc_start > op->cur_chrc->end_handle)
+ continue;
+
+ if (bt_gatt_discover_descriptors(client->att,
+ desc_start,
+ op->cur_chrc->end_handle,
+ discover_descs_cb,
+ async_op_ref(op),
+ async_op_unref))
+ return;
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Failed to start descriptor discovery");
+ async_op_unref(op);
+ success = false;
+
+ goto done;
+ }
+
+next:
+ if (!op->cur_service->next)
+ goto done;
+
+ /* 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,
+ async_op_ref(op),
+ async_op_unref))
+ return;
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Failed to start characteristic discovery");
+ async_op_unref(op);
+ success = false;
+
+done:
+ async_op_complete(op, success, att_ecode);
+}
+
+static void discover_chrcs_cb(bool success, uint8_t att_ecode,
+ struct bt_gatt_result *result,
void *user_data)
{
struct async_op *op = user_data;
@@ -164,6 +272,7 @@ static void discover_chrcs_cb(bool success, uint8_t att_ecode,
char uuid_str[MAX_LEN_UUID_STR];
unsigned int chrc_count;
unsigned int i;
+ uint16_t desc_start;
bt_gatt_characteristic_t *chrcs;
if (!success) {
@@ -212,6 +321,28 @@ static void discover_chrcs_cb(bool success, uint8_t att_ecode,
op->cur_service->service.chrcs = chrcs;
op->cur_service->service.num_chrcs = chrc_count;
+ for (i = 0; i < chrc_count; i++) {
+ op->cur_chrc_index = i;
+ op->cur_chrc = chrcs + i;
+ desc_start = chrcs[i].value_handle + 1;
+ if (desc_start > chrcs[i].end_handle)
+ continue;
+
+ if (bt_gatt_discover_descriptors(client->att,
+ desc_start, chrcs[i].end_handle,
+ discover_descs_cb,
+ async_op_ref(op),
+ async_op_unref))
+ return;
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Failed to start descriptor discovery");
+ async_op_unref(op);
+ success = false;
+
+ goto done;
+ }
+
next:
if (!op->cur_service->next)
goto done;
--
2.1.0.rc2.206.gedb03e5
---
TODO | 16 ----------------
1 file changed, 16 deletions(-)
diff --git a/TODO b/TODO
index 39a1773..be56f28 100644
--- a/TODO
+++ b/TODO
@@ -145,22 +145,6 @@ ATT/GATT (new shared stack)
Priority: Medium
Complexity: C1
-- Introduce shared/gatt-client as a standalone client implementation that can
- operate on a single instance of bt_att as the ATT protocol transport. The
- basic API should include functions for reference counting, setting a handler
- for util_debug, and a high-level abstraction for GATT services,
- characteristics, and descriptors.
-
- Priority: Medium
- Complexity: C1
-
-- Implement attribute discovery for shared/gatt-client. All services,
- characteristics, and descriptors should be discovered and cached during the
- set up process, which should also involve the initial MTU exchange.
-
- Priority: Medium
- Complexity: C1
-
- Merge functions from shared/gatt-helpers involving value reads and writes into
shared/gatt-client. These functions should accept an instance of
bt_gatt_client instead of taking a bt_att directly.
--
2.1.0.rc2.206.gedb03e5
This patch implements characteristic discovery as part of the client
initialization flow. The characteristics of each service are discovered in
order.
---
src/shared/gatt-client.c | 142 ++++++++++++++++++++++++++++++++++++++++++-----
src/shared/gatt-client.h | 3 +-
2 files changed, 130 insertions(+), 15 deletions(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 17b2f56..6b18e91 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -107,6 +107,7 @@ static void gatt_client_clear_services(struct bt_gatt_client *client)
struct async_op {
struct bt_gatt_client *client;
+ struct service_list *cur_service;
int ref_count;
};
@@ -127,6 +128,113 @@ static void async_op_unref(void *data)
free(data);
}
+static void async_op_complete(struct async_op *op, bool success,
+ uint8_t att_ecode)
+{
+ struct bt_gatt_client *client = op->client;
+
+ client->in_init = false;
+
+ if (success)
+ client->ready = true;
+ else
+ gatt_client_clear_services(client);
+
+ if (client->ready_callback)
+ client->ready_callback(success, att_ecode, client->ready_data);
+}
+
+static void uuid_to_string(const uint8_t uuid[BT_GATT_UUID_SIZE],
+ char str[MAX_LEN_UUID_STR])
+{
+ bt_uuid_t tmp;
+
+ tmp.type = BT_UUID128;
+ memcpy(tmp.value.u128.data, uuid, UUID_BYTES);
+ bt_uuid_to_string(&tmp, str, MAX_LEN_UUID_STR * sizeof(char));
+}
+
+static void discover_chrcs_cb(bool success, uint8_t att_ecode,
+ struct bt_gatt_result *result,
+ void *user_data)
+{
+ struct async_op *op = user_data;
+ struct bt_gatt_client *client = op->client;
+ struct bt_gatt_iter iter;
+ char uuid_str[MAX_LEN_UUID_STR];
+ unsigned int chrc_count;
+ unsigned int i;
+ bt_gatt_characteristic_t *chrcs;
+
+ if (!success) {
+ if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
+ success = true;
+ goto next;
+ }
+
+ goto done;
+ }
+
+ if (!result || !bt_gatt_iter_init(&iter, result)) {
+ success = false;
+ goto done;
+ }
+
+ 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;
+
+ chrcs = new0(bt_gatt_characteristic_t, chrc_count);
+ if (!chrcs) {
+ success = false;
+ goto done;
+ }
+
+ i = 0;
+ while (bt_gatt_iter_next_characteristic(&iter, &chrcs[i].start_handle,
+ &chrcs[i].end_handle,
+ &chrcs[i].value_handle,
+ &chrcs[i].properties,
+ chrcs[i].uuid)) {
+ uuid_to_string(chrcs[i].uuid, 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].start_handle, chrcs[i].end_handle,
+ chrcs[i].value_handle, chrcs[i].properties,
+ uuid_str);
+ i++;
+ }
+
+ op->cur_service->service.chrcs = chrcs;
+ op->cur_service->service.num_chrcs = chrc_count;
+
+next:
+ if (!op->cur_service->next)
+ goto done;
+
+ /* 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,
+ async_op_ref(op),
+ async_op_unref))
+ return;
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Failed to start characteristic discovery");
+ async_op_unref(op);
+ success = false;
+
+done:
+ async_op_complete(op, success, att_ecode);
+}
+
static void discover_primary_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data)
@@ -137,7 +245,6 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
uint16_t start, end;
uint8_t uuid[BT_GATT_UUID_SIZE];
char uuid_str[MAX_LEN_UUID_STR];
- bt_uuid_t tmp;
if (!success) {
util_debug(client->debug_callback, client->debug_data,
@@ -148,7 +255,6 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
if (!result || !bt_gatt_iter_init(&iter, result)) {
success = false;
- att_ecode = 0;
goto done;
}
@@ -158,9 +264,7 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
while (bt_gatt_iter_next_service(&iter, &start, &end, uuid)) {
/* Log debug message. */
- tmp.type = BT_UUID128;
- memcpy(tmp.value.u128.data, uuid, sizeof(uuid));
- bt_uuid_to_string(&tmp, uuid_str, sizeof(uuid_str));
+ uuid_to_string(uuid, uuid_str);
util_debug(client->debug_callback, client->debug_data,
"start: 0x%04x, end: 0x%04x, uuid: %s",
start, end, uuid_str);
@@ -169,22 +273,32 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
if (!gatt_client_add_service(client, start, end, uuid)) {
util_debug(client->debug_callback, client->debug_data,
"Failed to store service");
-
- gatt_client_clear_services(client);
-
success = false;
goto done;
}
}
-done:
- client->in_init = false;
+ /* Complete the process if the service list is empty */
+ if (!client->svc_head)
+ goto done;
- if (success)
- client->ready = true;
+ /* Sequentially discover the characteristics of all services */
+ op->cur_service = client->svc_head;
+ if (bt_gatt_discover_characteristics(client->att,
+ op->cur_service->service.start_handle,
+ op->cur_service->service.end_handle,
+ discover_chrcs_cb,
+ async_op_ref(op),
+ async_op_unref))
+ return;
- if (client->ready_callback)
- client->ready_callback(success, att_ecode, client->ready_data);
+ util_debug(client->debug_callback, client->debug_data,
+ "Failed to start characteristic discovery");
+ async_op_unref(op);
+ success = false;
+
+done:
+ async_op_complete(op, success, att_ecode);
}
static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index f724c3c..b93836d 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -56,7 +56,8 @@ typedef struct {
} bt_gatt_descriptor_t;
typedef struct {
- uint16_t handle;
+ uint16_t start_handle;
+ uint16_t end_handle;
uint16_t value_handle;
uint8_t properties;
uint8_t uuid[BT_GATT_UUID_SIZE];
--
2.1.0.rc2.206.gedb03e5
Moved the ATT_DEFAULT_LE_MTU to att-types.h to make it public.
---
src/shared/att-types.h | 2 ++
src/shared/att.c | 6 +++---
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/shared/att-types.h b/src/shared/att-types.h
index 1637f6f..b85c969 100644
--- a/src/shared/att-types.h
+++ b/src/shared/att-types.h
@@ -23,6 +23,8 @@
#include <stdint.h>
+#define BT_ATT_DEFAULT_LE_MTU 23
+
/* ATT protocol opcodes */
#define BT_ATT_OP_ERROR_RSP 0x01
#define BT_ATT_OP_MTU_REQ 0x02
diff --git a/src/shared/att.c b/src/shared/att.c
index 73f5552..a5cab45 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -35,8 +35,8 @@
#include "src/shared/timeout.h"
#include "lib/uuid.h"
#include "src/shared/att.h"
+#include "src/shared/att-types.h"
-#define ATT_DEFAULT_LE_MTU 23
#define ATT_MIN_PDU_LEN 1 /* At least 1 byte for the opcode. */
#define ATT_OP_CMD_MASK 0x40
#define ATT_OP_SIGNED_MASK 0x80
@@ -698,7 +698,7 @@ struct bt_att *bt_att_new(int fd)
att->fd = fd;
- att->mtu = ATT_DEFAULT_LE_MTU;
+ att->mtu = BT_ATT_DEFAULT_LE_MTU;
att->buf = malloc(att->mtu);
if (!att->buf)
goto fail;
@@ -833,7 +833,7 @@ bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu)
if (!att)
return false;
- if (mtu < ATT_DEFAULT_LE_MTU)
+ if (mtu < BT_ATT_DEFAULT_LE_MTU)
return false;
buf = malloc(mtu);
--
2.1.0.rc2.206.gedb03e5
Added the bt_gatt_client_set_debug function, which sets up a callback to be used
internally by util_debug.
---
src/shared/gatt-client.c | 24 ++++++++++++++++++++++++
src/shared/gatt-client.h | 6 ++++++
2 files changed, 30 insertions(+)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 8d5c126..eeca394 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -28,6 +28,10 @@
struct bt_gatt_client {
struct bt_att *att;
int ref_count;
+
+ bt_gatt_client_debug_func_t debug_callback;
+ bt_gatt_client_destroy_func_t debug_destroy;
+ void *debug_data;
};
struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu)
@@ -66,6 +70,9 @@ void bt_gatt_client_unref(struct bt_gatt_client *client)
if (__sync_sub_and_fetch(&client->ref_count, 1))
return;
+ if (client->debug_destroy)
+ client->debug_destroy(client->debug_data);
+
bt_att_unref(client->att);
free(client);
}
@@ -77,3 +84,20 @@ bool bt_gatt_client_set_ready_handler(struct bt_gatt_client *client,
{
// TODO
}
+
+bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
+ bt_gatt_client_debug_func_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy) {
+ if (!client)
+ return false;
+
+ if (client->debug_destroy)
+ client->debug_destroy(client->debug_data);
+
+ client->debug_callback = callback;
+ client->debug_destroy = destroy;
+ client->debug_data = user_data;
+
+ return true;
+}
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index 757db65..45c6010 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -34,9 +34,15 @@ void bt_gatt_client_unref(struct bt_gatt_client *client);
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);
bool bt_gatt_client_set_ready_handler(struct bt_gatt_client *client,
bt_gatt_client_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
+bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
+ bt_gatt_client_debug_func_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy);
+
--
2.1.0.rc2.206.gedb03e5
This code implements the following portion of attribute discovery:
- Initial MTU exchange.
- Primary service discovery.
Discovery results aren't stored yet.
---
src/shared/gatt-client.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 166 insertions(+), 2 deletions(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index eeca394..7a76234 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -23,17 +23,168 @@
#include "src/shared/att.h"
#include "src/shared/gatt-client.h"
+#include "lib/uuid.h"
+#include "src/shared/gatt-helpers.h"
#include "src/shared/util.h"
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
struct bt_gatt_client {
struct bt_att *att;
int ref_count;
+ bt_gatt_client_callback_t ready_callback;
+ bt_gatt_client_destroy_func_t ready_destroy;
+ void *ready_data;
+
bt_gatt_client_debug_func_t debug_callback;
bt_gatt_client_destroy_func_t debug_destroy;
void *debug_data;
+
+ bool in_init;
+};
+
+struct async_op {
+ struct bt_gatt_client *client;
+ int ref_count;
};
+static struct async_op *async_op_ref(struct async_op *op)
+{
+ __sync_fetch_and_add(&op->ref_count, 1);
+
+ return op;
+}
+
+static void async_op_unref(void *data)
+{
+ struct async_op *op = data;
+
+ if (__sync_sub_and_fetch(&op->ref_count, 1))
+ return;
+
+ free(data);
+}
+
+static void discover_primary_cb(bool success, uint8_t att_ecode,
+ struct bt_gatt_result *result,
+ void *user_data)
+{
+ struct async_op *op = user_data;
+ struct bt_gatt_client *client = op->client;
+ struct bt_gatt_iter iter;
+ uint16_t start, end;
+ uint8_t uuid[16];
+ char uuid_str[MAX_LEN_UUID_STR];
+ bt_uuid_t tmp;
+
+ if (!success) {
+ util_debug(client->debug_callback, client->debug_data,
+ "Primary service discovery failed."
+ " ATT ECODE: 0x%02x", att_ecode);
+ goto done;
+ }
+
+ if (!result || !bt_gatt_iter_init(&iter, result)) {
+ success = false;
+ att_ecode = 0;
+ goto done;
+ }
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Primary services found: %u",
+ bt_gatt_result_service_count(result));
+
+ while (bt_gatt_iter_next_service(&iter, &start, &end, uuid)) {
+ /* TODO: discover characteristics and store the service. */
+
+ /* Log debug message. */
+ tmp.type = BT_UUID128;
+ memcpy(tmp.value.u128.data, uuid, sizeof(uuid));
+ bt_uuid_to_string(&tmp, 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);
+ }
+
+done:
+ client->in_init = false;
+
+ if (client->ready_callback)
+ client->ready_callback(success, att_ecode, client->ready_data);
+}
+
+static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
+{
+ struct async_op *op = user_data;
+ struct bt_gatt_client *client = op->client;
+
+ if (!success) {
+ util_debug(client->debug_callback, client->debug_data,
+ "MTU Exchange failed. ATT ECODE: 0x%02x",
+ att_ecode);
+
+ client->in_init = false;
+
+ if (client->ready_callback)
+ client->ready_callback(success, att_ecode,
+ client->ready_data);
+
+ return;
+ }
+
+ util_debug(client->debug_callback, client->debug_data,
+ "MTU exchange complete, with MTU: %u",
+ bt_att_get_mtu(client->att));
+
+ if (bt_gatt_discover_primary_services(client->att, NULL,
+ discover_primary_cb,
+ async_op_ref(op),
+ async_op_unref))
+ return;
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Failed to initiate primary service discovery");
+
+ client->in_init = false;
+
+ if (client->ready_callback)
+ client->ready_callback(success, att_ecode, client->ready_data);
+
+ async_op_unref(op);
+}
+
+static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
+{
+ struct async_op *op;
+
+ if (client->in_init)
+ return false;
+
+ op = new0(struct async_op, 1);
+ if (!op)
+ return false;
+
+ op->client = client;
+
+ /* Configure the MTU */
+ if (!bt_gatt_exchange_mtu(client->att, MAX(BT_ATT_DEFAULT_LE_MTU, mtu),
+ exchange_mtu_cb,
+ async_op_ref(op),
+ async_op_unref)) {
+ if (client->ready_callback)
+ client->ready_callback(false, 0, client->ready_data);
+
+ free(op);
+ }
+
+ client->in_init = true;
+
+ return true;
+}
+
struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu)
{
struct bt_gatt_client *client;
@@ -47,7 +198,7 @@ struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu)
client->att = bt_att_ref(att);
- // TODO: Initiate MTU exchange and service discovery.
+ gatt_client_init(client, mtu);
return bt_gatt_client_ref(client);
}
@@ -70,6 +221,9 @@ void bt_gatt_client_unref(struct bt_gatt_client *client)
if (__sync_sub_and_fetch(&client->ref_count, 1))
return;
+ if (client->ready_destroy)
+ client->ready_destroy(client->ready_data);
+
if (client->debug_destroy)
client->debug_destroy(client->debug_data);
@@ -82,7 +236,17 @@ bool bt_gatt_client_set_ready_handler(struct bt_gatt_client *client,
void *user_data,
bt_gatt_client_destroy_func_t destroy)
{
- // TODO
+ if (!client)
+ return false;
+
+ if (client->ready_destroy)
+ client->ready_destroy(client->ready_data);
+
+ client->ready_callback = callback;
+ client->ready_destroy = destroy;
+ client->ready_data = user_data;
+
+ return true;
}
bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
--
2.1.0.rc2.206.gedb03e5
Fixed a small typo in one of the callback arguments.
---
src/shared/gatt-helpers.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/shared/gatt-helpers.h b/src/shared/gatt-helpers.h
index d63fac1..443e6c5 100644
--- a/src/shared/gatt-helpers.h
+++ b/src/shared/gatt-helpers.h
@@ -54,7 +54,7 @@ typedef void (*bt_gatt_destroy_func_t)(void *user_data);
typedef void (*bt_gatt_result_callback_t)(bool success, uint8_t att_ecode,
void *user_data);
-typedef void (*bt_gatt_discovery_callback_t)(bool success, uint8_t att_code,
+typedef void (*bt_gatt_discovery_callback_t)(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data);
typedef void (*bt_gatt_read_callback_t)(bool success, uint8_t att_ecode,
--
2.1.0.rc2.206.gedb03e5
Added functions that return the number of services, characteristics, and
descriptors contained in a bt_gatt_result.
---
src/shared/gatt-helpers.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++
src/shared/gatt-helpers.h | 4 ++++
2 files changed, 53 insertions(+)
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index ff49867..00813f5 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -88,6 +88,55 @@ static void result_destroy(struct bt_gatt_result *result)
}
}
+static unsigned int result_element_count(struct bt_gatt_result *result)
+{
+ unsigned int count = 0;
+ struct bt_gatt_result *cur;
+
+ cur = result;
+
+ while (cur) {
+ count += cur->pdu_len / cur->data_len;
+ cur = cur->next;
+ }
+
+ return count;
+}
+
+unsigned int bt_gatt_result_service_count(struct bt_gatt_result *result)
+{
+ if (!result)
+ return 0;
+
+ if (result->opcode != BT_ATT_OP_READ_BY_GRP_TYPE_RSP &&
+ result->opcode != BT_ATT_OP_FIND_BY_TYPE_VAL_RSP)
+ return 0;
+
+ return result_element_count(result);
+}
+
+unsigned int bt_gatt_result_characteristic_count(struct bt_gatt_result *result)
+{
+ if (!result)
+ return 0;
+
+ if (result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP)
+ return 0;
+
+ return result_element_count(result);
+}
+
+unsigned int bt_gatt_result_descriptor_count(struct bt_gatt_result *result)
+{
+ if (!result)
+ return 0;
+
+ if (result->opcode != BT_ATT_OP_FIND_INFO_RSP)
+ return 0;
+
+ return result_element_count(result);
+}
+
bool bt_gatt_iter_init(struct bt_gatt_iter *iter, struct bt_gatt_result *result)
{
if (!iter || !result)
diff --git a/src/shared/gatt-helpers.h b/src/shared/gatt-helpers.h
index cdf83ba..d63fac1 100644
--- a/src/shared/gatt-helpers.h
+++ b/src/shared/gatt-helpers.h
@@ -35,6 +35,10 @@ struct bt_gatt_iter {
uint16_t pos;
};
+unsigned int bt_gatt_result_service_count(struct bt_gatt_result *result);
+unsigned int bt_gatt_result_characteristic_count(struct bt_gatt_result *result);
+unsigned int bt_gatt_result_descriptor_count(struct bt_gatt_result *result);
+
bool bt_gatt_iter_init(struct bt_gatt_iter *iter, struct bt_gatt_result *result);
bool bt_gatt_iter_next_service(struct bt_gatt_iter *iter,
uint16_t *start_handle, uint16_t *end_handle,
--
2.1.0.rc2.206.gedb03e5
This patch adds support for registering multiple disconnect handlers with an
instance of struct bt_att. Unregistering is achieved via a new function AND
through bt_att_unregister_all and all disconnect callbacks get automatically
unregistered after a single disconnect event.
---
src/shared/att.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++---------
src/shared/att.h | 11 ++--
2 files changed, 139 insertions(+), 27 deletions(-)
diff --git a/src/shared/att.c b/src/shared/att.c
index b5ab742..73f5552 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -60,6 +60,10 @@ struct bt_att {
bool in_notify;
bool need_notify_cleanup;
+ struct queue *disconn_list; /* List of disconnect handlers */
+ bool in_disconn;
+ bool need_disconn_cleanup;
+
uint8_t *buf;
uint16_t mtu;
@@ -73,10 +77,6 @@ struct bt_att {
bt_att_debug_func_t debug_callback;
bt_att_destroy_func_t debug_destroy;
void *debug_data;
-
- bt_att_disconnect_func_t disconn_callback;
- bt_att_destroy_func_t disconn_destroy;
- void *disconn_data;
};
enum att_op_type {
@@ -233,6 +233,46 @@ static void mark_notify_removed(void *data, void *user_data)
notify->removed = true;
}
+struct att_disconn {
+ unsigned int id;
+ bool removed;
+ bt_att_disconnect_func_t callback;
+ bt_att_destroy_func_t destroy;
+ void *user_data;
+};
+
+static void destroy_att_disconn(void *data)
+{
+ struct att_disconn *disconn = data;
+
+ if (disconn->destroy)
+ disconn->destroy(disconn->user_data);
+
+ free(disconn);
+}
+
+static bool match_disconn_id(const void *a, const void *b)
+{
+ const struct att_disconn *disconn = a;
+ unsigned int id = PTR_TO_UINT(b);
+
+ return disconn->id == id;
+}
+
+static bool match_disconn_removed(const void *a, const void *b)
+{
+ const struct att_disconn *disconn = a;
+
+ return disconn->removed;
+}
+
+static void mark_disconn_removed(void *data, void *user_data)
+{
+ struct att_disconn *disconn = data;
+
+ disconn->removed = true;
+}
+
static bool encode_pdu(struct att_send_op *op, const void *pdu,
uint16_t length, uint16_t mtu)
{
@@ -605,21 +645,42 @@ static bool can_read_data(struct io *io, void *user_data)
return true;
}
+static void disconn_handler(void *data, void *user_data)
+{
+ struct att_disconn *disconn = data;
+
+ if (disconn->removed)
+ return;
+
+ if (disconn->callback)
+ disconn->callback(disconn->user_data);
+}
+
static bool disconnect_cb(struct io *io, void *user_data)
{
struct bt_att *att = user_data;
- bt_att_cancel_all(att);
- bt_att_unregister_all(att);
-
io_destroy(att->io);
att->io = NULL;
util_debug(att->debug_callback, att->debug_data,
"Physical link disconnected");
- if (att->disconn_callback)
- att->disconn_callback(att->disconn_data);
+ bt_att_ref(att);
+ att->in_disconn = true;
+ queue_foreach(att->disconn_list, disconn_handler, NULL);
+ att->in_disconn = false;
+
+ if (att->need_disconn_cleanup) {
+ queue_remove_all(att->disconn_list, match_disconn_removed, NULL,
+ destroy_att_disconn);
+ att->need_disconn_cleanup = false;
+ }
+
+ bt_att_cancel_all(att);
+ bt_att_unregister_all(att);
+
+ bt_att_unref(att);
return false;
}
@@ -662,6 +723,10 @@ struct bt_att *bt_att_new(int fd)
if (!att->notify_list)
goto fail;
+ att->disconn_list = queue_new();
+ if (!att->disconn_list)
+ goto fail;
+
if (!io_set_read_handler(att->io, can_read_data, att, NULL))
goto fail;
@@ -674,6 +739,8 @@ fail:
queue_destroy(att->req_queue, NULL);
queue_destroy(att->ind_queue, NULL);
queue_destroy(att->write_queue, NULL);
+ queue_destroy(att->notify_list, NULL);
+ queue_destroy(att->disconn_list, NULL);
io_destroy(att->io);
free(att->buf);
free(att);
@@ -709,6 +776,7 @@ void bt_att_unref(struct bt_att *att)
queue_destroy(att->ind_queue, NULL);
queue_destroy(att->write_queue, NULL);
queue_destroy(att->notify_list, NULL);
+ queue_destroy(att->disconn_list, NULL);
att->req_queue = NULL;
att->ind_queue = NULL;
att->write_queue = NULL;
@@ -720,9 +788,6 @@ void bt_att_unref(struct bt_att *att)
if (att->debug_destroy)
att->debug_destroy(att->debug_data);
- if (att->disconn_destroy)
- att->disconn_destroy(att->disconn_data);
-
free(att->buf);
att->buf = NULL;
@@ -800,20 +865,57 @@ bool bt_att_set_timeout_cb(struct bt_att *att, bt_att_timeout_func_t callback,
return true;
}
-bool bt_att_set_disconnect_cb(struct bt_att *att,
+unsigned int bt_att_register_disconnect(struct bt_att *att,
bt_att_disconnect_func_t callback,
void *user_data,
bt_att_destroy_func_t destroy)
{
- if (!att)
+ struct att_disconn *disconn;
+
+ if (!att || !att->io)
+ return 0;
+
+ disconn = new0(struct att_disconn, 1);
+ if (!disconn)
+ return 0;
+
+ disconn->callback = callback;
+ disconn->destroy = destroy;
+ disconn->user_data = user_data;
+
+ if (att->next_reg_id < 1)
+ att->next_reg_id = 1;
+
+ disconn->id = att->next_reg_id++;
+
+ if (!queue_push_tail(att->disconn_list, disconn)) {
+ free(disconn);
+ return 0;
+ }
+
+ return disconn->id;
+}
+
+bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id)
+{
+ struct att_disconn *disconn;
+
+ if (!att || !id)
return false;
- if (att->disconn_destroy)
- att->disconn_destroy(att->disconn_data);
+ disconn = queue_find(att->disconn_list, match_disconn_id,
+ UINT_TO_PTR(id));
+ if (!disconn)
+ return false;
+
+ if (!att->in_disconn) {
+ queue_remove(att->disconn_list, disconn);
+ destroy_att_disconn(disconn);
+ return true;
+ }
- att->disconn_callback = callback;
- att->disconn_destroy = destroy;
- att->disconn_data = user_data;
+ disconn->removed = true;
+ att->need_disconn_cleanup = true;
return true;
}
@@ -996,14 +1098,21 @@ bool bt_att_unregister_all(struct bt_att *att)
if (!att)
return false;
- if (!att->in_notify) {
+ if (att->in_notify) {
+ queue_foreach(att->notify_list, mark_notify_removed, NULL);
+ att->need_notify_cleanup = true;
+ } else {
queue_remove_all(att->notify_list, NULL, NULL,
destroy_att_notify);
- return true;
}
- queue_foreach(att->notify_list, mark_notify_removed, NULL);
- att->need_notify_cleanup = true;
+ if (att->in_disconn) {
+ queue_foreach(att->disconn_list, mark_disconn_removed, NULL);
+ att->need_disconn_cleanup = true;
+ } else {
+ queue_remove_all(att->disconn_list, NULL, NULL,
+ destroy_att_disconn);
+ }
return true;
}
diff --git a/src/shared/att.h b/src/shared/att.h
index cf44704..1063021 100644
--- a/src/shared/att.h
+++ b/src/shared/att.h
@@ -54,10 +54,6 @@ bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu);
bool bt_att_set_timeout_cb(struct bt_att *att, bt_att_timeout_func_t callback,
void *user_data,
bt_att_destroy_func_t destroy);
-bool bt_att_set_disconnect_cb(struct bt_att *att,
- bt_att_disconnect_func_t callback,
- void *user_data,
- bt_att_destroy_func_t destroy);
unsigned int bt_att_send(struct bt_att *att, uint8_t opcode,
const void *pdu, uint16_t length,
@@ -72,4 +68,11 @@ unsigned int bt_att_register(struct bt_att *att, uint8_t opcode,
void *user_data,
bt_att_destroy_func_t destroy);
bool bt_att_unregister(struct bt_att *att, unsigned int id);
+
+unsigned int bt_att_register_disconnect(struct bt_att *att,
+ bt_att_disconnect_func_t callback,
+ void *user_data,
+ bt_att_destroy_func_t destroy);
+bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id);
+
bool bt_att_unregister_all(struct bt_att *att);
--
2.1.0.rc2.206.gedb03e5
This patch removes the service, characteristic, and descriptor structures
declared in gatt-helpers.h. These aren't really necessary, especially since
there will be another higher-level version of these for shared/gatt-client.
---
src/shared/gatt-helpers.c | 51 +++++++++++++++++++++++++----------------------
src/shared/gatt-helpers.h | 30 +++++++---------------------
2 files changed, 34 insertions(+), 47 deletions(-)
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index 047f64c..ff49867 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -134,13 +134,14 @@ struct discovery_op {
};
bool bt_gatt_iter_next_service(struct bt_gatt_iter *iter,
- struct bt_gatt_service *service)
+ uint16_t *start_handle, uint16_t *end_handle,
+ uint8_t uuid[16])
{
struct discovery_op *op;
const void *pdu_ptr;
- bt_uuid_t uuid;
+ bt_uuid_t tmp;
- if (!iter->result || !service)
+ if (!iter || !iter->result || !start_handle || !end_handle || !uuid)
return false;
op = iter->result->op;
@@ -148,17 +149,16 @@ bool bt_gatt_iter_next_service(struct bt_gatt_iter *iter,
switch (iter->result->opcode) {
case BT_ATT_OP_READ_BY_GRP_TYPE_RSP:
- service->start = get_le16(pdu_ptr);
- service->end = get_le16(pdu_ptr + 2);
- convert_uuid_le(pdu_ptr + 4, iter->result->data_len - 4,
- service->uuid);
+ *start_handle = get_le16(pdu_ptr);
+ *end_handle = get_le16(pdu_ptr + 2);
+ convert_uuid_le(pdu_ptr + 4, iter->result->data_len - 4, uuid);
break;
case BT_ATT_OP_FIND_BY_TYPE_VAL_RSP:
- service->start = get_le16(pdu_ptr);
- service->end = get_le16(pdu_ptr + 2);
+ *start_handle = get_le16(pdu_ptr);
+ *end_handle = get_le16(pdu_ptr + 2);
- bt_uuid_to_uuid128(&op->uuid, &uuid);
- memcpy(service->uuid, uuid.value.u128.data, 16);
+ bt_uuid_to_uuid128(&op->uuid, &tmp);
+ memcpy(uuid, tmp.value.u128.data, 16);
break;
default:
return false;
@@ -175,12 +175,15 @@ bool bt_gatt_iter_next_service(struct bt_gatt_iter *iter,
}
bool bt_gatt_iter_next_characteristic(struct bt_gatt_iter *iter,
- struct bt_gatt_characteristic *chrc)
+ uint16_t *start_handle, uint16_t *end_handle,
+ uint16_t *value_handle, uint8_t *properties,
+ uint8_t uuid[16])
{
struct discovery_op *op;
const void *pdu_ptr;
- if (!iter->result || !chrc)
+ if (!iter || !iter->result || !start_handle || !end_handle ||
+ !value_handle || !properties || !uuid)
return false;
if (iter->result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP)
@@ -189,10 +192,10 @@ bool bt_gatt_iter_next_characteristic(struct bt_gatt_iter *iter,
op = iter->result->op;
pdu_ptr = iter->result->pdu + iter->pos;
- chrc->start = get_le16(pdu_ptr);
- chrc->properties = ((uint8_t *) pdu_ptr)[2];
- chrc->value = get_le16(pdu_ptr + 3);
- convert_uuid_le(pdu_ptr + 5, iter->result->data_len - 5, chrc->uuid);
+ *start_handle = get_le16(pdu_ptr);
+ *properties = ((uint8_t *) pdu_ptr)[2];
+ *value_handle = get_le16(pdu_ptr + 3);
+ convert_uuid_le(pdu_ptr + 5, iter->result->data_len - 5, uuid);
iter->pos += iter->result->data_len;
if (iter->pos == iter->result->pdu_len) {
@@ -201,21 +204,21 @@ bool bt_gatt_iter_next_characteristic(struct bt_gatt_iter *iter,
}
if (!iter->result) {
- chrc->end = op->end_handle;
+ *end_handle = op->end_handle;
return true;
}
- chrc->end = get_le16(iter->result->pdu + iter->pos) - 1;
+ *end_handle = get_le16(iter->result->pdu + iter->pos) - 1;
return true;
}
-bool bt_gatt_iter_next_descriptor(struct bt_gatt_iter *iter,
- struct bt_gatt_descriptor *desc)
+bool bt_gatt_iter_next_descriptor(struct bt_gatt_iter *iter, uint16_t *handle,
+ uint8_t uuid[16])
{
const void *pdu_ptr;
- if (!iter->result || !desc)
+ if (!iter || !iter->result || !handle || !uuid)
return false;
if (iter->result->opcode != BT_ATT_OP_FIND_INFO_RSP)
@@ -223,8 +226,8 @@ bool bt_gatt_iter_next_descriptor(struct bt_gatt_iter *iter,
pdu_ptr = iter->result->pdu + iter->pos;
- desc->handle = get_le16(pdu_ptr);
- convert_uuid_le(pdu_ptr + 2, iter->result->data_len - 2, desc->uuid);
+ *handle = get_le16(pdu_ptr);
+ convert_uuid_le(pdu_ptr + 2, iter->result->data_len - 2, uuid);
iter->pos += iter->result->data_len;
if (iter->pos == iter->result->pdu_len) {
diff --git a/src/shared/gatt-helpers.h b/src/shared/gatt-helpers.h
index 95dd8b7..cdf83ba 100644
--- a/src/shared/gatt-helpers.h
+++ b/src/shared/gatt-helpers.h
@@ -28,25 +28,6 @@
#include <stdbool.h>
#include <stdint.h>
-struct bt_gatt_service {
- uint16_t start;
- uint16_t end;
- uint8_t uuid[16];
-};
-
-struct bt_gatt_characteristic {
- uint16_t start;
- uint16_t end;
- uint16_t value;
- uint8_t properties;
- uint8_t uuid[16];
-};
-
-struct bt_gatt_descriptor {
- uint16_t handle;
- uint8_t uuid[16];
-};
-
struct bt_gatt_result;
struct bt_gatt_iter {
@@ -56,11 +37,14 @@ struct bt_gatt_iter {
bool bt_gatt_iter_init(struct bt_gatt_iter *iter, struct bt_gatt_result *result);
bool bt_gatt_iter_next_service(struct bt_gatt_iter *iter,
- struct bt_gatt_service *service);
+ uint16_t *start_handle, uint16_t *end_handle,
+ uint8_t uuid[16]);
bool bt_gatt_iter_next_characteristic(struct bt_gatt_iter *iter,
- struct bt_gatt_characteristic *characteristic);
-bool bt_gatt_iter_next_descriptor(struct bt_gatt_iter *iter,
- struct bt_gatt_descriptor *descriptor);
+ uint16_t *start_handle, uint16_t *end_handle,
+ uint16_t *value_handle, uint8_t *properties,
+ uint8_t uuid[16]);
+bool bt_gatt_iter_next_descriptor(struct bt_gatt_iter *iter, uint16_t *handle,
+ uint8_t uuid[16]);
typedef void (*bt_gatt_destroy_func_t)(void *user_data);
--
2.1.0.rc2.206.gedb03e5
This patch introduces shared/gatt-client, which provides a central/client side
implementation of the Generic Attribute Profile. An instance of bt_gatt_client
will provide a central database of all GATT services, characteristics, and
descriptors that have been discovered on a peripheral and will provide API
functions to obtain information about discovered attributes, registering
handlers for "Service Changed" indications, as well as providing
reference-counted access to "Client Characteristic Configuration" descriptors.
---
Makefile.am | 3 +-
src/shared/gatt-client.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++
src/shared/gatt-client.h | 42 +++++++++++++++++++++++++
3 files changed, 123 insertions(+), 1 deletion(-)
create mode 100644 src/shared/gatt-client.c
create mode 100644 src/shared/gatt-client.h
diff --git a/Makefile.am b/Makefile.am
index 88fcb49..6aed8e2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -158,7 +158,8 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
src/shared/util.h src/shared/util.c \
src/shared/mgmt.h src/shared/mgmt.c \
src/shared/att-types.h src/shared/att.h src/shared/att.c \
- src/shared/gatt-helpers.h src/shared/gatt-helpers.c
+ src/shared/gatt-helpers.h src/shared/gatt-helpers.c \
+ src/shared/gatt-client.h src/shared/gatt-client.c
src_bluetoothd_LDADD = lib/libbluetooth-internal.la gdbus/libgdbus-internal.la \
@GLIB_LIBS@ @DBUS_LIBS@ -ldl -lrt
src_bluetoothd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic \
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
new file mode 100644
index 0000000..8d5c126
--- /dev/null
+++ b/src/shared/gatt-client.c
@@ -0,0 +1,79 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Google Inc.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "src/shared/att.h"
+#include "src/shared/gatt-client.h"
+#include "src/shared/util.h"
+
+struct bt_gatt_client {
+ struct bt_att *att;
+ int ref_count;
+};
+
+struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu)
+{
+ struct bt_gatt_client *client;
+
+ if (!att)
+ return NULL;
+
+ client = new0(struct bt_gatt_client, 1);
+ if (!client)
+ return NULL;
+
+ client->att = bt_att_ref(att);
+
+ // TODO: Initiate MTU exchange and service discovery.
+
+ return bt_gatt_client_ref(client);
+}
+
+struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client)
+{
+ if (!client)
+ return NULL;
+
+ __sync_fetch_and_add(&client->ref_count, 1);
+
+ return client;
+}
+
+void bt_gatt_client_unref(struct bt_gatt_client *client)
+{
+ if (!client)
+ return;
+
+ if (__sync_sub_and_fetch(&client->ref_count, 1))
+ return;
+
+ bt_att_unref(client->att);
+ free(client);
+}
+
+bool bt_gatt_client_set_ready_handler(struct bt_gatt_client *client,
+ bt_gatt_client_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy)
+{
+ // TODO
+}
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
new file mode 100644
index 0000000..757db65
--- /dev/null
+++ b/src/shared/gatt-client.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Google Inc.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct bt_gatt_client;
+
+struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu);
+
+struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client);
+void bt_gatt_client_unref(struct bt_gatt_client *client);
+
+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);
+
+bool bt_gatt_client_set_ready_handler(struct bt_gatt_client *client,
+ bt_gatt_client_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy);
+
--
2.1.0.rc2.206.gedb03e5
---
TODO | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 104 insertions(+), 2 deletions(-)
diff --git a/TODO b/TODO
index 50678d3..39a1773 100644
--- a/TODO
+++ b/TODO
@@ -136,8 +136,110 @@ Low Energy
Priority: Low
Complexity: C2
-ATT/GATT
-========
+ATT/GATT (new shared stack)
+===========================
+
+- shared/att currently doesn't handle signed writes. It should be extended to
+ support signing outgoing and verify incoming ATT PDUs.
+
+ Priority: Medium
+ Complexity: C1
+
+- Introduce shared/gatt-client as a standalone client implementation that can
+ operate on a single instance of bt_att as the ATT protocol transport. The
+ basic API should include functions for reference counting, setting a handler
+ for util_debug, and a high-level abstraction for GATT services,
+ characteristics, and descriptors.
+
+ Priority: Medium
+ Complexity: C1
+
+- Implement attribute discovery for shared/gatt-client. All services,
+ characteristics, and descriptors should be discovered and cached during the
+ set up process, which should also involve the initial MTU exchange.
+
+ Priority: Medium
+ Complexity: C1
+
+- Merge functions from shared/gatt-helpers involving value reads and writes into
+ shared/gatt-client. These functions should accept an instance of
+ bt_gatt_client instead of taking a bt_att directly.
+
+ Priority: Medium
+ Complexity: C1
+
+- Properly handle indications from the "Service Changed" characteristic.
+ shared/gatt-client should automatically rediscover all changed GATT services
+ and notify the upper layer using a specially assigned handler.
+
+ Priority: Medium
+ Complexity: C1
+
+- Define API functions in shared/gatt-client for enabling/disabling
+ characteristic handle-value notifications/indications on a reference counted
+ basis. Code that is no longer interested in notifications from a
+ characteristic should be able to unregister any callbacks without writing to
+ the "Client Characteristic Configuration" descriptor if there are others who
+ are still interested.
+
+ Priority: Medium
+ Complexity: C2
+
+- Introduce a handler interface to shared/gatt-client which can be used by the
+ upper layer to determine when the link has been disconnected or an ATT
+ protocol request times out.
+
+ Priority: Medium
+ Complexity: C2
+
+- Introduce long-term caching of attributes to shared/gatt-client, such that the
+ services, characteristics, and descriptors obtained from a peripheral are
+ remembered in the case of bonding. This may involve storing data about GATT
+ services to disk.
+
+ Priority: Low
+ Complexity: C4
+
+- Introduce a new GATT client command-line tool to test and use a single
+ instance of bt_gatt_client.
+
+ Priority: Low
+ Complexity: C1
+
+- Move all daemon plugins and profiles that are GATT based to use
+ shared/gatt-client instead of attrib/*. This is a complicated task that
+ potentially needs a new plugin/profile probing interface and a lot of
+ rewriting that can cause regressions in existing functionality. The biggest
+ challenge here is that an instance of bt_att (shared/att) and GAttrib
+ (attrib/gattrib) cannot coexist on the same file descriptor, since they will
+ cause ATT protocol violations by breaking the sequential request-response
+ structure. A special shared/gatt-client-gattrib implementation may be
+ necessary to move each profile/plugin to the new API before actually switching
+ to the shared/att based implementation.
+
+ Priority: Medium
+ Complexity: C4
+
+- Implement the client portion of doc/gatt-api.txt using shared/gatt-client once
+ plugin/profile code uses it.
+
+ Priority: Medium
+ Complexity: C2
+
+- Introduce shared/gatt-server, which combined with shared/gatt-db, can be used
+ as a GATT server implementation.
+
+ Priority: Medium
+ Complexity: C2
+
+- Implement the server portion of doc/gatt-api.txt using shared/gatt-server once
+ it exists.
+
+ Priority: Medium
+ Complexity: C4
+
+ATT/GATT (old/outdated)
+=======================
- At the moment authentication and authorization is not supported at the
same time, read/write requirements in the attribute server needs to
--
2.1.0.rc2.206.gedb03e5