* v2:
- Rebased on top of master
- Fixed empty line at the end of file.
- Other minor style fixes
* 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 | 89 +++++++
src/shared/gatt-helpers.c | 100 +++++--
src/shared/gatt-helpers.h | 36 +--
9 files changed, 1063 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 Arman,
> * v2:
> - Rebased on top of master
> - Fixed empty line at the end of file.
> - Other minor style fixes
>
> * 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 | 89 +++++++
> src/shared/gatt-helpers.c | 100 +++++--
> src/shared/gatt-helpers.h | 36 +--
> 9 files changed, 1063 insertions(+), 81 deletions(-)
> create mode 100644 src/shared/gatt-client.c
> create mode 100644 src/shared/gatt-client.h
all 13 patches have been applied.
Regards
Marcel
---
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 64c598f..e2235c3 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 a4cd132..e4a4312 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -55,7 +55,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
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 e2235c3..afd84eb 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
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 | 42 ++++++++++++++
2 files changed, 186 insertions(+), 4 deletions(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 7a76234..64c598f 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 65da93c..a4cd132 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,
@@ -44,3 +48,41 @@ 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);
+
+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 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 7c2b7c6..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
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 | 5 +++++
2 files changed, 29 insertions(+)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 98fb456..7c2b7c6 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 73af7c9..65da93c 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -34,8 +34,13 @@ 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
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
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
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 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 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 | 41 +++++++++++++++++++++++++
3 files changed, 122 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..98fb456
--- /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..73af7c9
--- /dev/null
+++ b/src/shared/gatt-client.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * 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
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