Current implementation allow to discover included services with
16 bit UUID only.
---
src/shared/gatt-helpers.c | 108 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 106 insertions(+), 2 deletions(-)
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index 55e6992..d126a55 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -668,6 +668,82 @@ bool bt_gatt_discover_primary_services(struct bt_att *att, bt_uuid_t *uuid,
return result;
}
+static void discover_included_cb(uint8_t opcode, const void *pdu,
+ uint16_t length, void *user_data)
+{
+ struct bt_gatt_result *final_result = NULL;
+ struct discovery_op *op = user_data;
+ struct bt_gatt_result *cur_result;
+ uint8_t att_ecode = 0;
+ uint16_t last_handle;
+ size_t data_length;
+ bool success;
+
+ if (opcode == BT_ATT_OP_ERROR_RSP) {
+ success = false;
+ att_ecode = process_error(pdu, length);
+
+ if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND &&
+ op->result_head)
+ goto success;
+
+ goto done;
+ }
+
+ if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || !pdu || length < 6) {
+ success = false;
+ goto done;
+ }
+
+ data_length = ((uint8_t *) pdu)[0];
+
+ if ((length - 1) % data_length || data_length != 8) {
+ success = false;
+ goto done;
+ }
+
+ cur_result = result_create(opcode, pdu + 1, length - 1,
+ data_length, op);
+ if (!cur_result) {
+ success = false;
+ goto done;
+ }
+
+ if (!op->result_head)
+ op->result_head = op->result_tail = cur_result;
+ else {
+ op->result_tail->next = cur_result;
+ op->result_tail = cur_result;
+ }
+
+ last_handle = get_le16(pdu + length - data_length);
+ if (last_handle != op->end_handle) {
+ uint8_t pdu[6];
+
+ put_le16(last_handle + 1, pdu);
+ put_le16(op->end_handle, pdu + 2);
+ put_le16(GATT_INCLUDE_UUID, pdu + 4);
+
+ if (bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+ pdu, sizeof(pdu),
+ discover_included_cb,
+ discovery_op_ref(op),
+ discovery_op_unref))
+ return;
+
+ discovery_op_unref(op);
+ success = false;
+ }
+
+success:
+ success = true;
+ final_result = op->result_head;
+
+done:
+ if (op->callback)
+ op->callback(success, att_ecode, final_result, op->user_data);
+}
+
bool bt_gatt_discover_included_services(struct bt_att *att,
uint16_t start, uint16_t end,
bt_uuid_t *uuid,
@@ -675,8 +751,36 @@ bool bt_gatt_discover_included_services(struct bt_att *att,
void *user_data,
bt_gatt_destroy_func_t destroy)
{
- /* TODO */
- return false;
+ struct discovery_op *op;
+ uint8_t pdu[6];
+
+ if (!att)
+ return false;
+
+ op = new0(struct discovery_op, 1);
+ if (!op)
+ return false;
+
+ op->att = att;
+ op->callback = callback;
+ op->user_data = user_data;
+ op->destroy = destroy;
+ op->end_handle = end;
+
+ put_le16(start, pdu);
+ put_le16(end, pdu + 2);
+ put_le16(GATT_INCLUDE_UUID, pdu + 4);
+
+ if (!bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ,
+ pdu, sizeof(pdu),
+ discover_included_cb,
+ discovery_op_ref(op),
+ discovery_op_unref)) {
+ free(op);
+ return false;
+ }
+
+ return true;
}
static void discover_chrcs_cb(uint8_t opcode, const void *pdu,
--
1.9.3
According to SPEC 4.1 4.5.1 "Find Included Services" there is no need
to pass UUID to discover_included_services() function.
---
src/shared/gatt-helpers.c | 1 -
src/shared/gatt-helpers.h | 1 -
2 files changed, 2 deletions(-)
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index 6a2cc22..c23fd06 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -978,7 +978,6 @@ done:
bool bt_gatt_discover_included_services(struct bt_att *att,
uint16_t start, uint16_t end,
- bt_uuid_t *uuid,
bt_gatt_discovery_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy)
diff --git a/src/shared/gatt-helpers.h b/src/shared/gatt-helpers.h
index da409fa..abd218c 100644
--- a/src/shared/gatt-helpers.h
+++ b/src/shared/gatt-helpers.h
@@ -77,7 +77,6 @@ bool bt_gatt_discover_primary_services(struct bt_att *att, bt_uuid_t *uuid,
bt_gatt_destroy_func_t destroy);
bool bt_gatt_discover_included_services(struct bt_att *att,
uint16_t start, uint16_t end,
- bt_uuid_t *uuid,
bt_gatt_discovery_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy);
--
1.9.3
Avoid incorrect reading of included service discovery results.
---
src/shared/gatt-helpers.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index f4ca97c..1ccbc33 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -123,6 +123,9 @@ unsigned int bt_gatt_result_characteristic_count(struct bt_gatt_result *result)
if (result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP)
return 0;
+ if (result->data_len != 21 && result->data_len != 7)
+ return 0;
+
return result_element_count(result);
}
@@ -238,6 +241,9 @@ bool bt_gatt_iter_next_characteristic(struct bt_gatt_iter *iter,
if (iter->result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP)
return false;
+ if (iter->result->data_len != 21 && iter->result->data_len != 7)
+ return false;
+
op = iter->result->op;
pdu_ptr = iter->result->pdu + iter->pos;
--
1.9.3
It will fetch included services from result.
---
src/shared/gatt-helpers.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++
src/shared/gatt-helpers.h | 3 +++
2 files changed, 61 insertions(+)
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index 1ccbc33..6a2cc22 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -185,6 +185,64 @@ struct discovery_op {
bt_gatt_destroy_func_t destroy;
};
+bool bt_gatt_iter_next_included_service(struct bt_gatt_iter *iter,
+ uint16_t *handle, uint16_t *start_handle,
+ uint16_t *end_handle, uint8_t uuid[16])
+{
+ struct bt_gatt_result *read_result;
+ const void *pdu_ptr;
+ int i = 0;
+
+ if (!iter || !iter->result || !handle || !start_handle || !end_handle
+ || !uuid)
+ return false;
+
+ if (iter->result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP)
+ return false;
+
+ if (iter->result->data_len != 8 && iter->result->data_len != 6)
+ return false;
+
+ pdu_ptr = iter->result->pdu + iter->pos;
+
+ if (iter->result->data_len == 8) {
+ *handle = get_le16(pdu_ptr);
+ *start_handle = get_le16(pdu_ptr + 2);
+ *end_handle = get_le16(pdu_ptr + 4);
+ convert_uuid_le(pdu_ptr + 6, 2, uuid);
+
+ iter->pos += iter->result->data_len;
+
+ if (iter->pos == iter->result->pdu_len) {
+ iter->result = iter->result->next;
+ iter->pos = 0;
+ }
+
+ return true;
+ }
+
+ *handle = get_le16(pdu_ptr);
+ *start_handle = get_le16(pdu_ptr + 2);
+ *end_handle = get_le16(pdu_ptr + 4);
+ read_result = iter->result;
+
+ do {
+ read_result = read_result->next;
+ } while (read_result && i++ < (iter->pos / iter->result->data_len));
+
+ if (!read_result)
+ return false;
+
+ convert_uuid_le(read_result->pdu, read_result->data_len, uuid);
+ iter->pos += iter->result->data_len;
+ if (iter->pos == iter->result->pdu_len) {
+ iter->result = read_result->next;
+ iter->pos = 0;
+ }
+
+ return true;
+}
+
bool bt_gatt_iter_next_service(struct bt_gatt_iter *iter,
uint16_t *start_handle, uint16_t *end_handle,
uint8_t uuid[16])
diff --git a/src/shared/gatt-helpers.h b/src/shared/gatt-helpers.h
index f6f4b62..da409fa 100644
--- a/src/shared/gatt-helpers.h
+++ b/src/shared/gatt-helpers.h
@@ -49,6 +49,9 @@ bool bt_gatt_iter_next_characteristic(struct bt_gatt_iter *iter,
uint8_t uuid[16]);
bool bt_gatt_iter_next_descriptor(struct bt_gatt_iter *iter, uint16_t *handle,
uint8_t uuid[16]);
+bool bt_gatt_iter_next_included_service(struct bt_gatt_iter *iter,
+ uint16_t *handle, uint16_t *start_handle,
+ uint16_t *end_handle, uint8_t uuid[16]);
typedef void (*bt_gatt_destroy_func_t)(void *user_data);
--
1.9.3
It will return number of included services in result.
---
src/shared/gatt-helpers.c | 21 +++++++++++++++++++++
src/shared/gatt-helpers.h | 1 +
2 files changed, 22 insertions(+)
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index c23fd06..1157412 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -140,6 +140,27 @@ unsigned int bt_gatt_result_descriptor_count(struct bt_gatt_result *result)
return result_element_count(result);
}
+unsigned int bt_gatt_result_included_count(struct bt_gatt_result *result)
+{
+ struct bt_gatt_result *cur;
+ unsigned int count = 0;
+
+ if (!result)
+ return 0;
+
+ if (result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP)
+ return 0;
+
+ if (result->data_len != 6 && result->data_len != 8)
+ return 0;
+
+ for (cur = result; cur; cur = cur->next)
+ if (cur->opcode == BT_ATT_OP_READ_BY_TYPE_RSP)
+ count += cur->pdu_len / cur->data_len;
+
+ return count;
+}
+
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 abd218c..fb045fb 100644
--- a/src/shared/gatt-helpers.h
+++ b/src/shared/gatt-helpers.h
@@ -38,6 +38,7 @@ struct bt_gatt_iter {
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);
+unsigned int bt_gatt_result_included_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,
--
1.9.3
If included services has 128 bit UUID, it won't be returned in
READ_BY_TYPE_RSP. To get UUID READ_REQUEST is used.
This procedure is described in CORE SPEC 4.5.1 "Find Included Services".
---
src/shared/gatt-helpers.c | 170 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 169 insertions(+), 1 deletion(-)
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index d126a55..f4ca97c 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -668,6 +668,160 @@ bool bt_gatt_discover_primary_services(struct bt_att *att, bt_uuid_t *uuid,
return result;
}
+struct read_incl_data {
+ struct discovery_op *op;
+ struct bt_gatt_result *result;
+ int pos;
+ int ref_count;
+};
+
+static struct read_incl_data *new_read_included(struct bt_gatt_result *res)
+{
+ struct read_incl_data *data;
+
+ data = new0(struct read_incl_data, 1);
+ if (!data)
+ return NULL;
+
+ data->op = discovery_op_ref(res->op);
+ data->result = res;
+
+ return data;
+};
+
+static struct read_incl_data *read_included_ref(struct read_incl_data *data)
+{
+ __sync_fetch_and_add(&data->ref_count, 1);
+
+ return data;
+}
+
+static void read_included_unref(void *data)
+{
+ struct read_incl_data *read_data = data;
+
+ if (__sync_sub_and_fetch(&read_data->ref_count, 1))
+ return;
+
+ discovery_op_unref(read_data->op);
+
+ free(read_data);
+}
+
+static void discover_included_cb(uint8_t opcode, const void *pdu,
+ uint16_t length, void *user_data);
+
+static void read_included_cb(uint8_t opcode, const void *pdu,
+ uint16_t length, void *user_data)
+{
+ struct read_incl_data *data = user_data;
+ struct bt_gatt_result *final_result = NULL;
+ struct discovery_op *op = data->op;
+ struct bt_gatt_result *cur_result;
+ uint8_t att_ecode = 0;
+ uint16_t handle;
+ uint8_t read_pdu[2];
+ bool success;
+
+ if (opcode == BT_ATT_OP_ERROR_RSP) {
+ success = false;
+ att_ecode = process_error(pdu, length);
+ goto done;
+ }
+
+ if (opcode != BT_ATT_OP_READ_RSP || (!pdu && length)) {
+ success = false;
+ goto done;
+ }
+
+ if (length != 16) {
+ success = false;
+ goto done;
+ }
+ cur_result = result_create(opcode, pdu, length, length, op);
+ if (!cur_result) {
+ success = false;
+ goto done;
+ }
+
+ if (!op->result_head)
+ op->result_head = op->result_tail = cur_result;
+ else {
+ op->result_tail->next = cur_result;
+ op->result_tail = cur_result;
+ }
+
+ if (data->pos == data->result->pdu_len) {
+ uint16_t last_handle, data_len;
+ uint8_t pdu[6];
+
+ data_len = data->result->data_len;
+ last_handle = get_le16(data->result->pdu + data->pos -
+ data_len);
+ if (last_handle == op->end_handle) {
+ final_result = op->result_head;
+ success = true;
+ goto done;
+ }
+
+ put_le16(last_handle + 1, pdu);
+ put_le16(op->end_handle, pdu + 2);
+ put_le16(GATT_INCLUDE_UUID, pdu + 4);
+
+ if (bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+ pdu, sizeof(pdu),
+ discover_included_cb,
+ discovery_op_ref(op),
+ discovery_op_unref))
+ return;
+
+ discovery_op_unref(op);
+ success = false;
+ goto done;
+ }
+
+ handle = get_le16(data->result->pdu + data->pos + 2);
+ put_le16(handle, read_pdu);
+
+ data->pos += data->result->data_len;
+
+ if (bt_att_send(op->att, BT_ATT_OP_READ_REQ, read_pdu, sizeof(read_pdu),
+ read_included_cb,
+ read_included_ref(data),
+ read_included_unref))
+ return;
+
+ read_included_unref(data);
+ success = false;
+
+done:
+ if (op->callback)
+ op->callback(success, att_ecode, final_result, op->user_data);
+}
+
+static void read_included(struct read_incl_data *data)
+{
+ struct discovery_op *op = data->op;
+ uint16_t handle;
+ uint8_t pdu[2];
+
+ handle = get_le16(data->result->pdu + 2);
+ put_le16(handle, pdu);
+
+ data->pos += data->result->data_len;
+
+ if (bt_att_send(op->att, BT_ATT_OP_READ_REQ, pdu, sizeof(pdu),
+ read_included_cb,
+ read_included_ref(data),
+ read_included_unref))
+ return;
+
+ read_included_unref(data);
+
+ if (op->callback)
+ op->callback(false, 0, NULL, data->op->user_data);
+}
+
static void discover_included_cb(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
@@ -697,7 +851,8 @@ static void discover_included_cb(uint8_t opcode, const void *pdu,
data_length = ((uint8_t *) pdu)[0];
- if ((length - 1) % data_length || data_length != 8) {
+ if (((length - 1) % data_length) ||
+ (data_length != 8 && data_length != 6)) {
success = false;
goto done;
}
@@ -716,6 +871,19 @@ static void discover_included_cb(uint8_t opcode, const void *pdu,
op->result_tail = cur_result;
}
+ if (data_length == 6) {
+ struct read_incl_data *data;
+
+ data = new_read_included(cur_result);
+ if (!data) {
+ success = false;
+ goto done;
+ }
+
+ read_included(data);
+ return;
+ }
+
last_handle = get_le16(pdu + length - data_length);
if (last_handle != op->end_handle) {
uint8_t pdu[6];
--
1.9.3