diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 033cba1..c891516 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
*/
#include "src/shared/att.h"
-#include "src/shared/gatt-client.h"
#include "lib/uuid.h"
#include "src/shared/gatt-helpers.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-client.h"
#include <assert.h>
#include <limits.h>
unsigned int ccc_write_id;
};
-struct service_list {
- bt_gatt_service_t service;
- struct chrc_data *chrcs;
- size_t num_chrcs;
- bt_gatt_included_service_t *includes;
- size_t num_includes;
- struct service_list *next;
-};
-
struct bt_gatt_client {
struct bt_att *att;
int ref_count;
bt_gatt_client_destroy_func_t debug_destroy;
void *debug_data;
- struct service_list *svc_head, *svc_tail;
+ struct gatt_db *db;
bool in_init;
bool ready;
* value handle. These will have the value 0 if they are not present on
* the remote peripheral.
*/
- uint16_t gatt_svc_handle;
- uint16_t svc_chngd_val_handle;
- uint16_t svc_chngd_ccc_handle;
unsigned int svc_chngd_ind_id;
struct queue *svc_chngd_queue; /* Queued service changed events */
bool in_svc_chngd;
notify_data->invalid = true;
}
-static struct service_list *new_service_list(uint16_t start, uint16_t end,
- bool primary,
- uint8_t uuid[BT_GATT_UUID_SIZE])
-{
- struct service_list *list;
-
- list = new0(struct service_list, 1);
- if (!list)
- return NULL;
-
- list->service.primary = primary;
- list->service.start_handle = start;
- list->service.end_handle = end;
- memcpy(list->service.uuid, uuid, UUID_BYTES);
-
- return list;
-}
-
-static bool service_list_add_service(struct service_list **head,
- struct service_list **tail,
- bool primary, uint16_t start,
- uint16_t end,
- uint8_t uuid[BT_GATT_UUID_SIZE])
-{
- struct service_list *list;
-
- list = new_service_list(start, end, primary, uuid);
- if (!list)
- return false;
-
- if (!(*head))
- *head = *tail = list;
- else {
- (*tail)->next = list;
- *tail = list;
- }
-
- return true;
-}
-
-static void service_destroy_characteristics(struct service_list *service)
-{
- unsigned int i;
-
- for (i = 0; i < service->num_chrcs; i++) {
- free(service->chrcs[i].descs);
- queue_destroy(service->chrcs[i].reg_notify_queue,
- notify_data_unref);
- }
-
- free(service->chrcs);
-}
-
-static void service_destroy_includes(struct service_list *service)
-{
- free(service->includes);
-
- service->includes = NULL;
- service->num_includes = 0;
-}
-
-static void service_list_clear(struct service_list **head,
- struct service_list **tail)
-{
- struct service_list *l, *tmp;
-
- if (!(*head) || !(*tail))
- return;
-
- l = *head;
-
- while (l) {
- service_destroy_characteristics(l);
- service_destroy_includes(l);
- tmp = l;
- l = tmp->next;
- free(tmp);
- }
-
- *head = *tail = NULL;
-}
-
-static void service_list_clear_range(struct service_list **head,
- struct service_list **tail,
- uint16_t start, uint16_t end)
-{
- struct service_list *cur, *prev, *tmp;
-
- if (!(*head) || !(*tail))
- return;
-
- prev = NULL;
- cur = *head;
- while (cur) {
- if (cur->service.end_handle < start ||
- cur->service.start_handle > end) {
- prev = cur;
- cur = cur->next;
- continue;
- }
-
- service_destroy_characteristics(cur);
- service_destroy_includes(cur);
-
- if (!prev)
- *head = cur->next;
- else
- prev->next = cur->next;
-
- if (*tail == cur)
- *tail = prev;
-
- tmp = cur;
- cur = cur->next;
- free(tmp);
- }
-}
-
-static void service_list_insert_services(struct service_list **head,
- struct service_list **tail,
- struct service_list *svc_head,
- struct service_list *svc_tail)
-{
- struct service_list *cur, *prev;
-
- if (!(*head) || !(*tail)) {
- *head = svc_head;
- *tail = svc_tail;
- return;
- }
-
- prev = NULL;
- cur = *head;
- while (cur) {
- if (svc_tail->service.end_handle < cur->service.start_handle) {
- if (!prev)
- *head = svc_head;
- else
- prev->next = svc_head;
-
- svc_tail->next = cur;
- return;
- }
-
- prev = cur;
- cur = cur->next;
- }
-
- if (prev != *tail)
- return;
-
- prev->next = svc_head;
- *tail = svc_tail;
-}
-
static void gatt_client_remove_all_notify_in_range(
struct bt_gatt_client *client,
uint16_t start_handle, uint16_t end_handle)
&range, notify_data_unref);
}
-static void gatt_client_clear_services(struct bt_gatt_client *client)
-{
+struct discovery_op;
- gatt_client_remove_all_notify_in_range(client, 0x0001, 0xffff);
- service_list_clear(&client->svc_head, &client->svc_tail);
-}
+typedef void (*discovery_op_complete_func_t)(struct discovery_op *op,
+ bool success,
+ uint8_t att_ecode);
+typedef void (*discovery_op_fail_func_t)(struct discovery_op *op);
struct discovery_op {
struct bt_gatt_client *client;
- struct service_list *result_head, *result_tail, *cur_service;
- struct chrc_data *cur_chrc;
+ struct queue *pending_svcs;
+ struct queue *pending_chrcs;
+ struct queue *tmp_queue;
+ struct gatt_db_attribute *cur_svc;
+ bool success;
uint16_t start;
uint16_t end;
- int cur_chrc_index;
int ref_count;
- void (*complete_func)(struct discovery_op *op, bool success,
- uint8_t att_ecode);
+ discovery_op_complete_func_t complete_func;
+ discovery_op_fail_func_t failure_func;
};
+static void discovery_op_free(struct discovery_op *op)
+{
+ queue_destroy(op->pending_svcs, NULL);
+ queue_destroy(op->pending_chrcs, free);
+ queue_destroy(op->tmp_queue, NULL);
+ free(op);
+}
+
+static struct discovery_op *discovery_op_create(struct bt_gatt_client *client,
+ uint16_t start, uint16_t end,
+ discovery_op_complete_func_t complete_func,
+ discovery_op_fail_func_t failure_func)
+{
+ struct discovery_op *op;
+
+ op = new0(struct discovery_op, 1);
+ if (!op)
+ return NULL;
+
+ op->pending_svcs = queue_new();
+ if (!op->pending_svcs)
+ goto fail;
+
+ op->pending_chrcs = queue_new();
+ if (!op->pending_chrcs)
+ goto fail;
+
+ op->tmp_queue = queue_new();
+ if (!op->tmp_queue)
+ goto fail;
+
+ op->client = client;
+ op->complete_func = complete_func;
+ op->failure_func = failure_func;
+ op->start = start;
+ op->end = end;
+
+ return op;
+
+fail:
+ discovery_op_free(op);
+ return NULL;
+}
+
static struct discovery_op *discovery_op_ref(struct discovery_op *op)
{
__sync_fetch_and_add(&op->ref_count, 1);
if (__sync_sub_and_fetch(&op->ref_count, 1))
return;
- service_list_clear(&op->result_head, &op->result_tail);
-
- free(data);
-}
-
-static void uuid_to_string(const uint8_t uuid[BT_GATT_UUID_SIZE],
- char str[MAX_LEN_UUID_STR])
-{
- bt_uuid_t tmp;
+ if (!op->success)
+ op->failure_func(op);
- tmp.type = BT_UUID128;
- memcpy(tmp.value.u128.data, uuid, UUID_BYTES);
- bt_uuid_to_string(&tmp, str, MAX_LEN_UUID_STR * sizeof(char));
+ discovery_op_free(op);
}
static void discover_chrcs_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data);
-static int uuid_cmp(const uint8_t uuid128[16], uint16_t uuid16)
-{
- uint8_t rhs_uuid[16] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
- 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
- };
-
- put_be16(uuid16, rhs_uuid + 2);
-
- return memcmp(uuid128, rhs_uuid, sizeof(rhs_uuid));
-}
-
static void discover_incl_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result, void *user_data)
{
struct discovery_op *op = user_data;
struct bt_gatt_client *client = op->client;
struct bt_gatt_iter iter;
+ struct gatt_db_attribute *attr, *tmp;
+ uint16_t handle, start, end;
+ uint128_t u128;
+ bt_uuid_t uuid;
char uuid_str[MAX_LEN_UUID_STR];
- bt_gatt_included_service_t *includes;
unsigned int includes_count, i;
if (!success) {
goto failed;
}
+ /* Get the currently processed service */
+ attr = op->cur_svc;
+ if (!attr)
+ goto failed;
+
if (!result || !bt_gatt_iter_init(&iter, result))
goto failed;
if (includes_count == 0)
goto failed;
- includes = new0(bt_gatt_included_service_t, includes_count);
- if (!includes)
- goto failed;
-
util_debug(client->debug_callback, client->debug_data,
"Included services found: %u",
includes_count);
for (i = 0; i < includes_count; i++) {
- if (!bt_gatt_iter_next_included_service(&iter,
- &includes[i].handle,
- &includes[i].start_handle,
- &includes[i].end_handle,
- includes[i].uuid))
+ if (!bt_gatt_iter_next_included_service(&iter, &handle, &start,
+ &end, u128.data))
break;
- uuid_to_string(includes[i].uuid, uuid_str);
+ bt_uuid128_create(&uuid, u128);
+
+ /* Log debug message */
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
util_debug(client->debug_callback, client->debug_data,
"handle: 0x%04x, start: 0x%04x, end: 0x%04x,"
- "uuid: %s", includes[i].handle,
- includes[i].start_handle,
- includes[i].end_handle, uuid_str);
- }
+ "uuid: %s", handle, start, end, uuid_str);
+
+ tmp = gatt_db_get_attribute(client->db, start);
+ if (!tmp)
+ goto failed;
- op->cur_service->includes = includes;
- op->cur_service->num_includes = includes_count;
+ tmp = gatt_db_service_add_included(attr, tmp);
+ if (!tmp)
+ goto failed;
+
+ /*
+ * GATT requires that all include definitions precede
+ * characteristic declarations. Based on the order we're adding
+ * these entries, the correct handle must be assigned to the new
+ * attribute.
+ */
+ if (gatt_db_attribute_get_handle(tmp) != handle)
+ goto failed;
+ }
next:
- if (!op->cur_service->next) {
- op->cur_service = op->result_head;
+ /* Move on to the next service */
+ attr = queue_pop_head(op->pending_svcs);
+ if (!attr) {
+ struct queue *tmp_queue;
+
+ tmp_queue = op->pending_svcs;
+ op->pending_svcs = op->tmp_queue;
+ op->tmp_queue = tmp_queue;
+
+ /*
+ * We have processed all include definitions. Move on to
+ * characteristics.
+ */
+ attr = queue_pop_head(op->pending_svcs);
+ if (!attr)
+ goto failed;
+
+ if (!gatt_db_attribute_get_service_handles(attr, &start, &end))
+ goto failed;
+
+ op->cur_svc = attr;
if (bt_gatt_discover_characteristics(client->att,
- op->cur_service->service.start_handle,
- op->cur_service->service.end_handle,
- discover_chrcs_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ start, end,
+ discover_chrcs_cb,
+ discovery_op_ref(op),
+ discovery_op_unref))
return;
util_debug(client->debug_callback, client->debug_data,
goto failed;
}
- op->cur_service = op->cur_service->next;
- if (bt_gatt_discover_included_services(client->att,
- op->cur_service->service.start_handle,
- op->cur_service->service.end_handle,
- discover_incl_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ queue_push_tail(op->tmp_queue, attr);
+ op->cur_svc = attr;
+ if (!gatt_db_attribute_get_service_handles(attr, &start, &end))
+ goto failed;
+
+ if (bt_gatt_discover_included_services(client->att, start, end,
+ discover_incl_cb,
+ discovery_op_ref(op),
+ discovery_op_unref))
return;
util_debug(client->debug_callback, client->debug_data,
discovery_op_unref(op);
failed:
+ op->success = false;
op->complete_func(op, false, att_ecode);
}
+struct chrc {
+ uint16_t start_handle;
+ uint16_t end_handle;
+ uint16_t value_handle;
+ uint8_t properties;
+ bt_uuid_t uuid;
+};
+
+static void discover_descs_cb(bool success, uint8_t att_ecode,
+ struct bt_gatt_result *result,
+ void *user_data);
+
+static bool discover_descs(struct discovery_op *op, bool *discovering)
+{
+ struct bt_gatt_client *client = op->client;
+ struct gatt_db_attribute *attr;
+ struct chrc *chrc_data;
+ uint16_t desc_start;
+
+ *discovering = false;
+
+ while ((chrc_data = queue_pop_head(op->pending_chrcs))) {
+ attr = gatt_db_service_add_characteristic(op->cur_svc,
+ &chrc_data->uuid, 0,
+ chrc_data->properties,
+ NULL, NULL, NULL);
+
+ if (!attr)
+ goto failed;
+
+ if (gatt_db_attribute_get_handle(attr) !=
+ chrc_data->value_handle)
+ goto failed;
+
+ desc_start = chrc_data->value_handle + 1;
+
+ if (desc_start > chrc_data->end_handle)
+ continue;
+
+ if (bt_gatt_discover_descriptors(client->att, desc_start,
+ chrc_data->end_handle,
+ discover_descs_cb,
+ discovery_op_ref(op),
+ discovery_op_unref)) {
+ *discovering = true;
+ goto done;
+ }
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Failed to start descriptor discovery");
+ discovery_op_unref(op);
+
+ goto failed;
+ }
+
+done:
+ free(chrc_data);
+ return true;
+
+failed:
+ free(chrc_data);
+ return false;
+}
+
static void discover_descs_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data)
struct discovery_op *op = user_data;
struct bt_gatt_client *client = op->client;
struct bt_gatt_iter iter;
+ struct gatt_db_attribute *attr;
+ uint16_t handle, start, end;
+ uint128_t u128;
+ bt_uuid_t uuid;
char uuid_str[MAX_LEN_UUID_STR];
unsigned int desc_count;
- uint16_t desc_start;
- unsigned int i;
- bt_gatt_descriptor_t *descs;
+ bool discovering;
if (!success) {
if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
goto done;
}
- if (!result || !bt_gatt_iter_init(&iter, result)) {
- success = false;
- goto done;
- }
+ if (!result || !bt_gatt_iter_init(&iter, result))
+ goto failed;
desc_count = bt_gatt_result_descriptor_count(result);
- if (desc_count == 0) {
- success = false;
- goto done;
- }
+ if (desc_count == 0)
+ goto failed;
util_debug(client->debug_callback, client->debug_data,
"Descriptors found: %u", desc_count);
- descs = new0(bt_gatt_descriptor_t, desc_count);
- if (!descs) {
- success = false;
- goto done;
- }
+ while (bt_gatt_iter_next_descriptor(&iter, &handle, u128.data)) {
+ bt_uuid128_create(&uuid, u128);
- i = 0;
- while (bt_gatt_iter_next_descriptor(&iter, &descs[i].handle,
- descs[i].uuid)) {
- uuid_to_string(descs[i].uuid, uuid_str);
+ /* Log debug message */
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
util_debug(client->debug_callback, client->debug_data,
"handle: 0x%04x, uuid: %s",
- descs[i].handle, uuid_str);
+ handle, uuid_str);
- if (uuid_cmp(descs[i].uuid, GATT_CLIENT_CHARAC_CFG_UUID) == 0) {
- op->cur_chrc->ccc_handle = descs[i].handle;
-
- if (uuid_cmp(op->cur_chrc->chrc_external.uuid,
- SVC_CHNGD_UUID) == 0)
- client->svc_chngd_ccc_handle = descs[i].handle;
- }
+ attr = gatt_db_service_add_descriptor(op->cur_svc, &uuid, 0,
+ NULL, NULL, NULL);
+ if (!attr)
+ goto failed;
- i++;
+ if (gatt_db_attribute_get_handle(attr) != handle)
+ goto failed;
}
- op->cur_chrc->chrc_external.num_descs = desc_count;
- op->cur_chrc->descs = descs;
- op->cur_chrc->chrc_external.descs = descs;
-
- for (i = op->cur_chrc_index + 1; i < op->cur_service->num_chrcs; i++) {
- op->cur_chrc_index = i;
- op->cur_chrc++;
- desc_start = op->cur_chrc->chrc_external.value_handle + 1;
- if (desc_start > op->cur_chrc->chrc_external.end_handle)
- continue;
+ if (!discover_descs(op, &discovering))
+ goto failed;
- if (bt_gatt_discover_descriptors(client->att, desc_start,
- op->cur_chrc->chrc_external.end_handle,
- discover_descs_cb, discovery_op_ref(op),
- discovery_op_unref))
- return;
+ if (discovering)
+ return;
- util_debug(client->debug_callback, client->debug_data,
- "Failed to start descriptor discovery");
- discovery_op_unref(op);
- success = false;
+next:
+ /* Done with the current service */
+ gatt_db_service_set_active(op->cur_svc, true);
+ attr = queue_pop_head(op->pending_svcs);
+ if (!attr)
goto done;
- }
-next:
- if (!op->cur_service->next)
- goto done;
+ if (!gatt_db_attribute_get_service_handles(attr, &start, &end))
+ goto failed;
/* Move on to the next service */
- op->cur_service = op->cur_service->next;
- if (bt_gatt_discover_characteristics(client->att,
- op->cur_service->service.start_handle,
- op->cur_service->service.end_handle,
- discover_chrcs_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ op->cur_svc = attr;
+ if (bt_gatt_discover_characteristics(client->att, start, end,
+ discover_chrcs_cb,
+ discovery_op_ref(op),
+ discovery_op_unref))
return;
util_debug(client->debug_callback, client->debug_data,
"Failed to start characteristic discovery");
discovery_op_unref(op);
+
+failed:
success = false;
done:
+ op->success = success;
op->complete_func(op, success, att_ecode);
}
-
static void discover_chrcs_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data)
struct discovery_op *op = user_data;
struct bt_gatt_client *client = op->client;
struct bt_gatt_iter iter;
+ struct gatt_db_attribute *attr;
+ struct chrc *chrc_data;
+ uint16_t start, end, value;
+ uint8_t properties;
+ uint128_t u128;
+ bt_uuid_t uuid;
char uuid_str[MAX_LEN_UUID_STR];
unsigned int chrc_count;
- unsigned int i;
- uint16_t desc_start;
- struct chrc_data *chrcs;
+ bool discovering;
if (!success) {
if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
goto done;
}
- if (!result || !bt_gatt_iter_init(&iter, result)) {
- success = false;
- goto done;
- }
+ if (!op->cur_svc || !result || !bt_gatt_iter_init(&iter, result))
+ goto failed;
chrc_count = bt_gatt_result_characteristic_count(result);
util_debug(client->debug_callback, client->debug_data,
"Characteristics found: %u", chrc_count);
if (chrc_count == 0)
- goto next;
+ goto failed;
- chrcs = new0(struct chrc_data, chrc_count);
- if (!chrcs) {
- success = false;
- goto done;
- }
+ while (bt_gatt_iter_next_characteristic(&iter, &start, &end, &value,
+ &properties, u128.data)) {
+ bt_uuid128_create(&uuid, u128);
- i = 0;
- while (bt_gatt_iter_next_characteristic(&iter,
- &chrcs[i].chrc_external.start_handle,
- &chrcs[i].chrc_external.end_handle,
- &chrcs[i].chrc_external.value_handle,
- &chrcs[i].chrc_external.properties,
- chrcs[i].chrc_external.uuid)) {
- uuid_to_string(chrcs[i].chrc_external.uuid, uuid_str);
+ /* Log debug message */
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
util_debug(client->debug_callback, client->debug_data,
"start: 0x%04x, end: 0x%04x, value: 0x%04x, "
"props: 0x%02x, uuid: %s",
- chrcs[i].chrc_external.start_handle,
- chrcs[i].chrc_external.end_handle,
- chrcs[i].chrc_external.value_handle,
- chrcs[i].chrc_external.properties,
- uuid_str);
-
- chrcs[i].reg_notify_queue = queue_new();
- if (!chrcs[i].reg_notify_queue) {
- success = false;
- goto done;
- }
+ start, end, value, properties, uuid_str);
- if (uuid_cmp(chrcs[i].chrc_external.uuid, SVC_CHNGD_UUID) == 0)
- client->svc_chngd_val_handle =
- chrcs[i].chrc_external.value_handle;
+ chrc_data = new0(struct chrc, 1);
+ if (!chrc_data)
+ goto failed;
- i++;
- }
+ chrc_data->start_handle = start;
+ chrc_data->end_handle = end;
+ chrc_data->value_handle = value;
+ chrc_data->properties = properties;
+ chrc_data->uuid = uuid;
- op->cur_service->chrcs = chrcs;
- op->cur_service->num_chrcs = chrc_count;
+ queue_push_tail(op->pending_chrcs, chrc_data);
+ }
- for (i = 0; i < chrc_count; i++) {
- op->cur_chrc_index = i;
- op->cur_chrc = chrcs + i;
- desc_start = chrcs[i].chrc_external.value_handle;
- if (desc_start++ == chrcs[i].chrc_external.end_handle)
- continue;
+ /*
+ * Sequentially discover descriptors for each characteristic and insert
+ * the characteristics into the database as we proceed.
+ */
+ if (!discover_descs(op, &discovering))
+ goto failed;
- if (bt_gatt_discover_descriptors(client->att, desc_start,
- chrcs[i].chrc_external.end_handle,
- discover_descs_cb, discovery_op_ref(op),
- discovery_op_unref))
- return;
+ if (discovering)
+ return;
- util_debug(client->debug_callback, client->debug_data,
- "Failed to start descriptor discovery");
- discovery_op_unref(op);
- success = false;
+next:
+ /* Done with the current service */
+ gatt_db_service_set_active(op->cur_svc, true);
+ attr = queue_pop_head(op->pending_svcs);
+ if (!attr)
goto done;
- }
-next:
- if (!op->cur_service->next)
- goto done;
+ if (!gatt_db_attribute_get_service_handles(attr, &start, &end))
+ goto failed;
/* Move on to the next service */
- op->cur_service = op->cur_service->next;
- if (bt_gatt_discover_characteristics(client->att,
- op->cur_service->service.start_handle,
- op->cur_service->service.end_handle,
- discover_chrcs_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ op->cur_svc = attr;
+ if (bt_gatt_discover_characteristics(client->att, start, end,
+ discover_chrcs_cb,
+ discovery_op_ref(op),
+ discovery_op_unref))
return;
util_debug(client->debug_callback, client->debug_data,
"Failed to start characteristic discovery");
discovery_op_unref(op);
+
+failed:
success = false;
done:
+ op->success = success;
op->complete_func(op, success, att_ecode);
}
struct discovery_op *op = user_data;
struct bt_gatt_client *client = op->client;
struct bt_gatt_iter iter;
+ struct gatt_db_attribute *attr;
uint16_t start, end;
- uint8_t uuid[BT_GATT_UUID_SIZE];
+ uint128_t u128;
+ bt_uuid_t uuid;
char uuid_str[MAX_LEN_UUID_STR];
- struct service_list *service;
if (!success) {
util_debug(client->debug_callback, client->debug_data,
}
}
- if (!result || !bt_gatt_iter_init(&iter, result))
+ if (!result || !bt_gatt_iter_init(&iter, result)) {
+ success = false;
goto done;
+ }
util_debug(client->debug_callback, client->debug_data,
"Secondary services found: %u",
bt_gatt_result_service_count(result));
- while (bt_gatt_iter_next_service(&iter, &start, &end, uuid)) {
- uuid_to_string(uuid, uuid_str);
+ while (bt_gatt_iter_next_service(&iter, &start, &end, u128.data)) {
+ bt_uuid128_create(&uuid, u128);
+
+ /* Log debug message */
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
util_debug(client->debug_callback, client->debug_data,
"start: 0x%04x, end: 0x%04x, uuid: %s",
start, end, uuid_str);
/* Store the service */
- service = new_service_list(start, end, false, uuid);
- if (!service) {
+ attr = gatt_db_insert_service(client->db, start, &uuid, false,
+ end - start + 1);
+ if (!attr) {
util_debug(client->debug_callback, client->debug_data,
"Failed to create service");
+ success = false;
goto done;
}
- service_list_insert_services(&op->result_head, &op->result_tail,
- service, service);
+ queue_push_tail(op->pending_svcs, attr);
}
next:
/* Sequentially discover included services */
- op->cur_service = op->result_head;
- if (bt_gatt_discover_included_services(client->att,
- op->cur_service->service.start_handle,
- op->cur_service->service.end_handle,
- discover_incl_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ attr = queue_pop_head(op->pending_svcs);
+
+ /* Complete with success if queue is empty */
+ if (!attr)
+ goto done;
+
+ /*
+ * Store the service in the tmp queue to be reused during
+ * characteristics discovery later.
+ */
+ queue_push_tail(op->tmp_queue, attr);
+ op->cur_svc = attr;
+
+ if (!gatt_db_attribute_get_service_handles(attr, &start, &end)) {
+ success = false;
+ goto done;
+ }
+
+ if (bt_gatt_discover_included_services(client->att, start, end,
+ discover_incl_cb,
+ discovery_op_ref(op),
+ discovery_op_unref))
return;
util_debug(client->debug_callback, client->debug_data,
discovery_op_unref(op);
done:
- op->complete_func(op, false, att_ecode);
+ op->success = success;
+ op->complete_func(op, success, att_ecode);
}
static void discover_primary_cb(bool success, uint8_t att_ecode,
struct discovery_op *op = user_data;
struct bt_gatt_client *client = op->client;
struct bt_gatt_iter iter;
+ struct gatt_db_attribute *attr;
uint16_t start, end;
- uint8_t uuid[BT_GATT_UUID_SIZE];
+ uint128_t u128;
+ bt_uuid_t uuid;
char uuid_str[MAX_LEN_UUID_STR];
if (!success) {
"Primary services found: %u",
bt_gatt_result_service_count(result));
- while (bt_gatt_iter_next_service(&iter, &start, &end, uuid)) {
+ while (bt_gatt_iter_next_service(&iter, &start, &end, u128.data)) {
+ bt_uuid128_create(&uuid, u128);
+
/* Log debug message. */
- uuid_to_string(uuid, uuid_str);
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
util_debug(client->debug_callback, client->debug_data,
"start: 0x%04x, end: 0x%04x, uuid: %s",
start, end, uuid_str);
- /* Store the service */
- if (!service_list_add_service(&op->result_head,
- &op->result_tail, true, start, end,
- uuid)) {
+ attr = gatt_db_insert_service(client->db, start, &uuid, true,
+ end - start + 1);
+ if (!attr) {
util_debug(client->debug_callback, client->debug_data,
"Failed to store service");
success = false;
goto done;
}
- if (uuid_cmp(uuid, GATT_SVC_UUID) == 0)
- client->gatt_svc_handle = start;
+ queue_push_tail(op->pending_svcs, attr);
}
- /* Complete the process if the service list is empty */
- if (!op->result_head)
- goto done;
-
/* Discover secondary services */
- op->cur_service = op->result_head;
if (bt_gatt_discover_secondary_services(client->att, NULL,
op->start, op->end,
discover_secondary_cb,
success = false;
done:
+ op->success = success;
op->complete_func(op, success, att_ecode);
}
"MTU exchange complete, with MTU: %u",
bt_att_get_mtu(client->att));
+ /* Don't do discovery if the database was pre-populated */
+ if (!gatt_db_isempty(client->db)) {
+ op->complete_func(op, true, 0);
+ return;
+ }
+
if (bt_gatt_discover_all_primary_services(client->att, NULL,
discover_primary_cb,
discovery_op_ref(op),
client->in_init = false;
if (client->ready_callback)
- client->ready_callback(success, att_ecode, client->ready_data);
+ client->ready_callback(false, att_ecode, client->ready_data);
discovery_op_unref(op);
}
struct service_changed_op *next_sc_op;
uint16_t start_handle = op->start;
uint16_t end_handle = op->end;
- bool services_found = false;
+ struct gatt_db_attribute *attr;
+ bt_uuid_t uuid;
+ struct queue *q;
client->in_svc_chngd = false;
util_debug(client->debug_callback, client->debug_data,
"Failed to discover services within changed range - "
"error: 0x%02x", att_ecode);
- goto next;
- }
- /* No new services in the modified range */
- if (!op->result_head || !op->result_tail)
- goto next;
-
- services_found = true;
-
- /* Insert all newly discovered services in their correct place as a
- * contiguous chunk */
- service_list_insert_services(&client->svc_head, &client->svc_tail,
- op->result_head, op->result_tail);
-
- /* Relinquish ownership of services, as the client now owns them */
- op->result_head = NULL;
- op->result_tail = NULL;
+ gatt_db_clear_range(client->db, start_handle, end_handle);
+ }
-next:
/* Notify the upper layer of changed services */
if (client->svc_chngd_callback)
client->svc_chngd_callback(start_handle, end_handle,
return;
}
- /* Check if the GATT service is not present or has remained unchanged */
- if (!services_found || !client->svc_chngd_val_handle ||
- client->svc_chngd_val_handle < start_handle ||
- client->svc_chngd_val_handle > end_handle)
+ /* Check if the GATT service was among the changed services */
+ q = queue_new();
+ if (!q)
return;
+ bt_uuid16_create(&uuid, SVC_CHNGD_UUID);
+
+ gatt_db_find_by_type(client->db, start_handle, end_handle, &uuid, q);
+ if (queue_isempty(q)) {
+ queue_destroy(q, NULL);
+ return;
+ }
+
+ attr = queue_pop_head(q);
+ queue_destroy(q, NULL);
+
/* The GATT service was modified. Re-register the handler for
* indications from the "Service Changed" characteristic.
*/
if (bt_gatt_client_register_notify(client,
- client->svc_chngd_val_handle,
- service_changed_reregister_cb,
- service_changed_cb,
- client, NULL))
+ gatt_db_attribute_get_handle(attr),
+ service_changed_reregister_cb,
+ service_changed_cb,
+ client, NULL))
return;
util_debug(client->debug_callback, client->debug_data,
"Failed to re-register handler for \"Service Changed\"");
}
+static void service_changed_failure(struct discovery_op *op)
+{
+ gatt_db_clear_range(op->client->db, op->start, op->end);
+}
+
static void process_service_changed(struct bt_gatt_client *client,
uint16_t start_handle,
uint16_t end_handle)
/* Remove all services that overlap the modified range since we'll
* rediscover them
*/
- service_list_clear_range(&client->svc_head, &client->svc_tail,
- start_handle, end_handle);
+ gatt_db_clear_range(client->db, start_handle, end_handle);
- op = new0(struct discovery_op, 1);
- if (!op) {
- util_debug(client->debug_callback, client->debug_data,
- "Failed to initiate primary service discovery"
- " after Service Changed");
- return;
- }
-
- if (client->gatt_svc_handle >= start_handle &&
- client->gatt_svc_handle <= end_handle) {
- client->gatt_svc_handle = 0;
- client->svc_chngd_val_handle = 0;
- client->svc_chngd_ind_id = 0;
- }
-
- op->client = client;
- op->complete_func = service_changed_complete;
- op->start = start_handle;
- op->end = end_handle;
+ op = discovery_op_create(client, start_handle, end_handle,
+ service_changed_complete,
+ service_changed_failure);
+ if (!op)
+ goto fail;
- if (!bt_gatt_discover_primary_services(client->att, NULL,
+ if (bt_gatt_discover_primary_services(client->att, NULL,
start_handle, end_handle,
discover_primary_cb,
discovery_op_ref(op),
discovery_op_unref)) {
- util_debug(client->debug_callback, client->debug_data,
- "Failed to initiate primary service discovery"
- " after Service Changed");
- free(op);
+ client->in_svc_chngd = true;
return;
}
- client->in_svc_chngd = true;
+fail:
+ util_debug(client->debug_callback, client->debug_data,
+ "Failed to initiate service discovery"
+ " after Service Changed");
+ discovery_op_free(op);
}
static void service_changed_cb(uint16_t value_handle, const uint8_t *value,
struct service_changed_op *op;
uint16_t start, end;
- if (value_handle != client->svc_chngd_val_handle || length != 4)
+ if (length != 4)
return;
start = get_le16(value);
{
struct bt_gatt_client *client = op->client;
bool registered;
+ struct gatt_db_attribute *attr;
+ bt_uuid_t uuid;
+ struct queue *q;
client->in_init = false;
if (!success)
goto fail;
- client->svc_head = op->result_head;
- client->svc_tail = op->result_tail;
+ q = queue_new();
+ if (!q)
+ goto fail;
- /* Relinquish ownership of services, as the client now owns them */
- op->result_head = NULL;
- op->result_tail = NULL;
+ bt_uuid16_create(&uuid, SVC_CHNGD_UUID);
- if (!client->svc_chngd_val_handle || !client->svc_chngd_ccc_handle) {
+ gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid, q);
+ if (queue_isempty(q)) {
+ queue_destroy(q, NULL);
client->ready = true;
goto done;
}
+ attr = queue_pop_head(q);
+ queue_destroy(q, NULL);
+
/* Register an indication handler for the "Service Changed"
* characteristic and report ready only if the handler is registered
* successfully. Temporarily set "ready" to true so that we can register
*/
client->ready = true;
registered = bt_gatt_client_register_notify(client,
- client->svc_chngd_val_handle,
- service_changed_register_cb,
- service_changed_cb,
- client, NULL);
+ gatt_db_attribute_get_handle(attr),
+ service_changed_register_cb,
+ service_changed_cb,
+ client, NULL);
client->ready = false;
if (registered)
fail:
util_debug(client->debug_callback, client->debug_data,
"Failed to initialize gatt-client");
- service_list_clear(&client->svc_head, &client->svc_head);
done:
if (client->ready_callback)
client->ready_callback(success, att_ecode, client->ready_data);
}
+static void init_fail(struct discovery_op *op)
+{
+ gatt_db_clear(op->client->db);
+}
+
static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
{
struct discovery_op *op;
if (client->in_init || client->ready)
return false;
- op = new0(struct discovery_op, 1);
+ op = discovery_op_create(client, 0x0001, 0xffff, init_complete,
+ init_fail);
if (!op)
return false;
- op->client = client;
- op->complete_func = init_complete;
- op->start = 0x0001;
- op->end = 0xffff;
-
/* Configure the MTU */
if (!bt_gatt_exchange_mtu(client->att, MAX(BT_ATT_DEFAULT_LE_MTU, mtu),
exchange_mtu_cb,
discovery_op_ref(op),
discovery_op_unref)) {
- free(op);
+ discovery_op_free(op);
return false;
}
bt_att_unref(client->att);
}
- gatt_client_clear_services(client);
+ gatt_db_unref(client->db);
queue_destroy(client->svc_chngd_queue, free);
queue_destroy(client->long_write_queue, long_write_op_unref);
client->ready_callback(false, 0, client->ready_data);
}
-struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu)
+struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db,
+ struct bt_att *att,
+ uint16_t mtu)
{
struct bt_gatt_client *client;
- if (!att)
+ if (!att || !db)
return NULL;
client = new0(struct bt_gatt_client, 1);
goto fail;
client->att = bt_att_ref(att);
+ client->db = gatt_db_ref(db);
if (!gatt_client_init(client, mtu))
goto fail;
bool bt_gatt_service_iter_next(struct bt_gatt_service_iter *iter,
const bt_gatt_service_t **service)
{
- struct service_list *l;
+ /* TODO: Remove iterator functions */
- if (!iter || !service)
- return false;
-
- l = iter->ptr;
-
- if (!l)
- l = iter->client->svc_head;
- else
- l = l->next;
-
- if (!l)
- return false;
-
- *service = &l->service;
- iter->ptr = l;
-
- return true;
+ return false;
}
bool bt_gatt_service_iter_next_by_handle(struct bt_gatt_service_iter *iter,
bool bt_gatt_characteristic_iter_next(struct bt_gatt_characteristic_iter *iter,
const bt_gatt_characteristic_t **chrc)
{
- struct service_list *service;
+ /* TODO: Remove iterator functions */
- if (!iter || !chrc)
- return false;
-
- service = iter->service;
-
- if (iter->pos >= service->num_chrcs)
- return false;
-
- *chrc = &service->chrcs[iter->pos++].chrc_external;
-
- return true;
+ return false;
}
bool bt_gatt_include_service_iter_init(struct bt_gatt_incl_service_iter *iter,
bool bt_gatt_include_service_iter_next(struct bt_gatt_incl_service_iter *iter,
const bt_gatt_included_service_t **incl)
{
- struct service_list *service;
-
- if (!iter || !incl)
- return false;
-
- service = iter->service;
+ /* TODO: Remove iterator functions */
- if (iter->pos >= service->num_includes)
- return false;
-
- *incl = &service->includes[iter->pos++];
-
- return true;
+ return false;
}
struct read_op {
struct chrc_data *chrc = NULL;
struct bt_gatt_service_iter iter;
const bt_gatt_service_t *service;
- size_t i;
if (!client || !chrc_value_handle || !callback)
return false;
if (!svc_data)
return false;
- for (i = 0; i < svc_data->num_chrcs; i++) {
- if (svc_data->chrcs[i].chrc_external.value_handle ==
- chrc_value_handle) {
- chrc = svc_data->chrcs + i;
- break;
- }
- }
+ /*
+ * TODO: Lookup characteristic and CCC in database. Add entries for each
+ * characteristic to a list on demand.
+ */
+ return false;
/* Check that the characteristic supports notifications/indications */
if (!chrc || !chrc->ccc_handle || chrc->notify_count == INT_MAX)
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index 11b1f37..a09c3b8 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
struct bt_gatt_client;
-struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu);
+struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db,
+ struct bt_att *att,
+ uint16_t mtu);
struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client);
void bt_gatt_client_unref(struct bt_gatt_client *client);
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index dadfa37..c0306bd 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
#include "monitor/mainloop.h"
#include "src/shared/util.h"
#include "src/shared/att.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
#include "src/shared/gatt-client.h"
#define ATT_CID 4
struct client {
int fd;
+ struct gatt_db *db;
struct bt_gatt_client *gatt;
};
}
cli->fd = fd;
- cli->gatt = bt_gatt_client_new(att, mtu);
+ cli->db = gatt_db_new();
+ if (!cli->db) {
+ fprintf(stderr, "Failed to create GATT database\n");
+ bt_att_unref(att);
+ free(cli);
+ return NULL;
+ }
+
+ cli->gatt = bt_gatt_client_new(cli->db, att, mtu);
if (!cli->gatt) {
fprintf(stderr, "Failed to create GATT client\n");
+ gatt_db_unref(cli->db);
bt_att_unref(att);
free(cli);
return NULL;
/* bt_gatt_client already holds a reference */
bt_att_unref(att);
+ gatt_db_unref(cli->db);
return cli;
}
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 03a66b9..32bddac 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
#include "src/shared/util.h"
#include "src/shared/att.h"
#include "src/shared/gatt-helpers.h"
-#include "src/shared/gatt-client.h"
#include "src/shared/queue.h"
#include "src/shared/gatt-db.h"
#include "src/shared/gatt-server.h"
+#include "src/shared/gatt-client.h"
struct test_pdu {
bool valid;
struct bt_gatt_client *client;
struct bt_gatt_server *server;
struct bt_att *att;
- struct gatt_db *db;
+ struct gatt_db *client_db;
+ struct gatt_db *server_db;
guint source;
guint process;
int fd;
static void populate_db(struct context *context)
{
- struct gatt_db *db = context->db;
+ struct gatt_db *db = context->server_db;
struct gatt_db_attribute *attr;
bt_uuid_t uuid;
uint128_t u128 = {
bt_gatt_exchange_mtu(context->att, mtu, NULL, NULL, NULL);
break;
case SERVER:
- context->db = gatt_db_new();
- g_assert(context->db);
+ context->server_db = gatt_db_new();
+ g_assert(context->server_db);
- context->server = bt_gatt_server_new(context->db, att, mtu);
+ context->server = bt_gatt_server_new(context->server_db, att,
+ mtu);
g_assert(context->server);
populate_db(context);
bt_att_unref(att);
break;
case CLIENT:
- context->client = bt_gatt_client_new(att, mtu);
+ context->client_db = gatt_db_new();
+ g_assert(context->client_db);
+
+ context->client = bt_gatt_client_new(context->client_db, att,
+ mtu);
g_assert(context->client);
if (g_test_verbose())
bt_gatt_client_unref(context->client);
bt_gatt_server_unref(context->server);
- gatt_db_unref(context->db);
+ gatt_db_unref(context->client_db);
+ gatt_db_unref(context->server_db);
if (context->att)
bt_att_unref(context->att);