This patch set introduces service added/removed events to gatt-db.
gatt-db now allows callbacks to be registered, which are called when a
service is marked as active/inactive or when they get removed from the
database.
Also included is a small fix to gatt-server so that it holds a reference
to its associated gatt-db.
Arman Uguray (4):
shared/gatt-server: Hold a reference to gatt-db.
shared/gatt-db: Add service added/removed events.
tools/btgatt-client: Observe service events.
TODO: gatt-db service events added.
TODO | 7 --
src/shared/gatt-db.c | 195 +++++++++++++++++++++++++++++++++++++++++++----
src/shared/gatt-db.h | 39 ++++++----
src/shared/gatt-server.c | 5 +-
tools/btgatt-client.c | 28 +++++++
5 files changed, 234 insertions(+), 40 deletions(-)
--
2.2.0.rc0.207.ga3a616c
This patch adds support to gatt-db for sending events when services are
added to and removed from the database:
1. Added the gatt_db_register function which can be used to register
service_added and service_removed callbacks.
2. Added the gatt_db_unregister function which can be used to
unregister previously added callbacks. This function can be safely
called from within a service added/removed callback.
3. The service added/removed callbacks are tied to
gatt_db_service_set_active. The added callback is called when a
service gets marked as active and vice versa.
4. The service_removed callback will get called if a service is
removed from the database.
---
src/shared/gatt-db.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++-----
src/shared/gatt-db.h | 39 +++++++----
2 files changed, 203 insertions(+), 31 deletions(-)
diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index 238872c..f96131d 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -51,6 +51,20 @@ struct gatt_db {
int ref_count;
uint16_t next_handle;
struct queue *services;
+
+ struct queue *notify_list;
+ bool in_notify;
+ bool need_notify_cleanup;
+ unsigned int next_notify_id;
+};
+
+struct notify {
+ unsigned int id;
+ bool removed;
+ gatt_db_attribute_cb_t service_added;
+ gatt_db_attribute_cb_t service_removed;
+ gatt_db_destroy_func_t destroy;
+ void *user_data;
};
struct pending_read {
@@ -89,6 +103,7 @@ struct gatt_db_attribute {
};
struct gatt_db_service {
+ struct gatt_db *db;
bool active;
uint16_t num_handles;
struct gatt_db_attribute **attributes;
@@ -168,16 +183,97 @@ struct gatt_db *gatt_db_new(void)
return NULL;
}
+ db->notify_list = queue_new();
+ if (!db->notify_list) {
+ queue_destroy(db->services, NULL);
+ free(db);
+ return NULL;
+ }
+
db->next_handle = 0x0001;
return gatt_db_ref(db);
}
+static void notify_destroy(void *data)
+{
+ struct notify *notify = data;
+
+ if (notify->destroy)
+ notify->destroy(notify->user_data);
+
+ free(notify);
+}
+
+static bool match_notify_id(const void *a, const void *b)
+{
+ const struct notify *notify = a;
+ unsigned int id = PTR_TO_UINT(b);
+
+ return notify->id == id;
+}
+
+static bool match_notify_removed(const void *a, const void *b)
+{
+ const struct notify *notify = a;
+
+ return notify->removed;
+}
+
+struct notify_data {
+ struct gatt_db_attribute *attr;
+ bool added;
+};
+
+static void handle_notify(void *data, void *user_data)
+{
+ struct notify *notify = data;
+ struct notify_data *notify_data = user_data;
+
+ if (notify->removed)
+ return;
+
+ if (notify_data->added)
+ notify->service_added(notify_data->attr, notify->user_data);
+ else
+ notify->service_removed(notify_data->attr, notify->user_data);
+}
+
+static void notify_service_changed(struct gatt_db *db,
+ struct gatt_db_service *service,
+ bool added)
+{
+ struct notify_data data;
+
+ if (queue_isempty(db->notify_list))
+ return;
+
+ data.attr = service->attributes[0];
+ data.added = added;
+
+ gatt_db_ref(db);
+
+ db->in_notify = true;
+ queue_foreach(db->notify_list, handle_notify, &data);
+ db->in_notify = false;
+
+ if (db->need_notify_cleanup) {
+ queue_remove_all(db->notify_list, match_notify_removed, NULL,
+ notify_destroy);
+ db->need_notify_cleanup = false;
+ }
+
+ gatt_db_unref(db);
+}
+
static void gatt_db_service_destroy(void *data)
{
struct gatt_db_service *service = data;
int i;
+ if (service->active)
+ notify_service_changed(service->db, service, false);
+
for (i = 0; i < service->num_handles; i++)
attribute_destroy(service->attributes[i]);
@@ -190,6 +286,11 @@ static void gatt_db_destroy(struct gatt_db *db)
if (!db)
return;
+ /*
+ * Clear the notify list before clearing the services to prevent the
+ * latter from sending service_removed events.
+ */
+ queue_destroy(db->notify_list, notify_destroy);
queue_destroy(db->services, gatt_db_service_destroy);
free(db);
}
@@ -433,6 +534,7 @@ struct gatt_db_attribute *gatt_db_insert_service(struct gatt_db *db,
goto fail;
}
+ service->db = db;
service->attributes[0]->handle = handle;
service->num_handles = num_handles;
@@ -455,6 +557,62 @@ struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db,
num_handles);
}
+unsigned int gatt_db_register(struct gatt_db *db,
+ gatt_db_attribute_cb_t service_added,
+ gatt_db_attribute_cb_t service_removed,
+ void *user_data,
+ gatt_db_destroy_func_t destroy)
+{
+ struct notify *notify;
+
+ if (!db || !(service_added || service_removed))
+ return 0;
+
+ notify = new0(struct notify, 1);
+ if (!notify)
+ return 0;
+
+ notify->service_added = service_added;
+ notify->service_removed = service_removed;
+ notify->destroy = destroy;
+ notify->user_data = user_data;
+
+ if (db->next_notify_id < 1)
+ db->next_notify_id = 1;
+
+ notify->id = db->next_notify_id++;
+
+ if (!queue_push_tail(db->notify_list, notify)) {
+ free(notify);
+ return 0;
+ }
+
+ return notify->id;
+}
+
+bool gatt_db_unregister(struct gatt_db *db, unsigned int id)
+{
+ struct notify *notify;
+
+ if (!db || !id)
+ return false;
+
+ notify = queue_find(db->notify_list, match_notify_id, UINT_TO_PTR(id));
+ if (!notify)
+ return false;
+
+ if (!db->in_notify) {
+ queue_remove(db->notify_list, notify);
+ notify_destroy(notify);
+ return true;
+ }
+
+ notify->removed = true;
+ db->need_notify_cleanup = true;
+
+ return true;
+}
+
static uint16_t get_attribute_index(struct gatt_db_service *service,
int end_offset)
{
@@ -636,10 +794,15 @@ gatt_db_service_add_included(struct gatt_db_attribute *attrib,
bool gatt_db_service_set_active(struct gatt_db_attribute *attrib, bool active)
{
+ struct gatt_db_service *service;
+
if (!attrib)
return false;
- attrib->service->active = active;
+ service = attrib->service;
+ service->active = active;
+
+ notify_service_changed(service->db, service, active);
return true;
}
@@ -858,14 +1021,14 @@ void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,
queue_foreach(db->services, find_information, &data);
}
-void gatt_db_foreach_service(struct gatt_db *db, gatt_db_foreach_t func,
+void gatt_db_foreach_service(struct gatt_db *db, gatt_db_attribute_cb_t func,
void *user_data)
{
gatt_db_foreach_service_in_range(db, func, user_data, 0x0001, 0xffff);
}
struct foreach_data {
- gatt_db_foreach_t func;
+ gatt_db_attribute_cb_t func;
void *user_data;
uint16_t start, end;
};
@@ -885,10 +1048,10 @@ static void foreach_service_in_range(void *data, void *user_data)
}
void gatt_db_foreach_service_in_range(struct gatt_db *db,
- gatt_db_foreach_t func,
- void *user_data,
- uint16_t start_handle,
- uint16_t end_handle)
+ gatt_db_attribute_cb_t func,
+ void *user_data,
+ uint16_t start_handle,
+ uint16_t end_handle)
{
struct foreach_data data;
@@ -904,9 +1067,9 @@ void gatt_db_foreach_service_in_range(struct gatt_db *db,
}
void gatt_db_service_foreach(struct gatt_db_attribute *attrib,
- const bt_uuid_t *uuid,
- gatt_db_foreach_t func,
- void *user_data)
+ const bt_uuid_t *uuid,
+ gatt_db_attribute_cb_t func,
+ void *user_data)
{
struct gatt_db_service *service;
struct gatt_db_attribute *attr;
@@ -930,15 +1093,15 @@ void gatt_db_service_foreach(struct gatt_db_attribute *attrib,
}
void gatt_db_service_foreach_char(struct gatt_db_attribute *attrib,
- gatt_db_foreach_t func,
- void *user_data)
+ gatt_db_attribute_cb_t func,
+ void *user_data)
{
gatt_db_service_foreach(attrib, &characteristic_uuid, func, user_data);
}
void gatt_db_service_foreach_desc(struct gatt_db_attribute *attrib,
- gatt_db_foreach_t func,
- void *user_data)
+ gatt_db_attribute_cb_t func,
+ void *user_data)
{
struct gatt_db_service *service;
struct gatt_db_attribute *attr;
@@ -970,8 +1133,8 @@ void gatt_db_service_foreach_desc(struct gatt_db_attribute *attrib,
}
void gatt_db_service_foreach_incl(struct gatt_db_attribute *attrib,
- gatt_db_foreach_t func,
- void *user_data)
+ gatt_db_attribute_cb_t func,
+ void *user_data)
{
gatt_db_service_foreach(attrib, &included_service_uuid, func,
user_data);
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 5db9f9b..987ccf4 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -102,30 +102,39 @@ void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,
struct queue *queue);
-typedef void (*gatt_db_foreach_t)(struct gatt_db_attribute *attrib,
+typedef void (*gatt_db_attribute_cb_t)(struct gatt_db_attribute *attrib,
void *user_data);
-void gatt_db_foreach_service(struct gatt_db *db, gatt_db_foreach_t func,
+
+void gatt_db_foreach_service(struct gatt_db *db, gatt_db_attribute_cb_t func,
void *user_data);
void gatt_db_foreach_service_in_range(struct gatt_db *db,
- gatt_db_foreach_t func,
- void *user_data,
- uint16_t start_handle,
- uint16_t end_handle);
+ gatt_db_attribute_cb_t func,
+ void *user_data,
+ uint16_t start_handle,
+ uint16_t end_handle);
void gatt_db_service_foreach(struct gatt_db_attribute *attrib,
- const bt_uuid_t *uuid,
- gatt_db_foreach_t func,
- void *user_data);
+ const bt_uuid_t *uuid,
+ gatt_db_attribute_cb_t func,
+ void *user_data);
void gatt_db_service_foreach_char(struct gatt_db_attribute *attrib,
- gatt_db_foreach_t func,
- void *user_data);
+ gatt_db_attribute_cb_t func,
+ void *user_data);
void gatt_db_service_foreach_desc(struct gatt_db_attribute *attrib,
- gatt_db_foreach_t func,
- void *user_data);
+ gatt_db_attribute_cb_t func,
+ void *user_data);
void gatt_db_service_foreach_incl(struct gatt_db_attribute *attrib,
- gatt_db_foreach_t func,
- void *user_data);
+ gatt_db_attribute_cb_t func,
+ void *user_data);
+
+typedef void (*gatt_db_destroy_func_t)(void *user_data);
+unsigned int gatt_db_register(struct gatt_db *db,
+ gatt_db_attribute_cb_t service_added,
+ gatt_db_attribute_cb_t service_removed,
+ void *user_data,
+ gatt_db_destroy_func_t destroy);
+bool gatt_db_unregister(struct gatt_db *db, unsigned int id);
struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *db,
uint16_t handle);
--
2.2.0.rc0.207.ga3a616c
---
TODO | 7 -------
1 file changed, 7 deletions(-)
diff --git a/TODO b/TODO
index e8b0ab1..ca3779b 100644
--- a/TODO
+++ b/TODO
@@ -155,13 +155,6 @@ ATT/GATT (new shared stack)
Priority: Medium
Complexity: C4
-- Add service_added and service_removed callbacks to shared/gatt-db which should
- get triggered via bt_gatt_service_set_active and when services get add or
- removed.
-
- Priority: Medium
- Complexity: C2
-
- Implement the client portion of doc/gatt-api.txt using shared/gatt-client once
plugin/profile code uses it.
--
2.2.0.rc0.207.ga3a616c
tools/btgatt-client now registers service added/removed callbacks in
which it logs a message about the affected service.
---
tools/btgatt-client.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 2a2067b..fe94ae8 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -99,6 +99,31 @@ static void ready_cb(bool success, uint8_t att_ecode, void *user_data);
static void service_changed_cb(uint16_t start_handle, uint16_t end_handle,
void *user_data);
+static void log_service_event(struct gatt_db_attribute *attr, const char *str)
+{
+ char uuid_str[MAX_LEN_UUID_STR];
+ bt_uuid_t uuid;
+ uint16_t start, end;
+
+ gatt_db_attribute_get_service_uuid(attr, &uuid);
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+
+ gatt_db_attribute_get_service_handles(attr, &start, &end);
+
+ PRLOG("%s - UUID: %s start: 0x%04x end: 0x%04x\n", str, uuid_str,
+ start, end);
+}
+
+static void service_added_cb(struct gatt_db_attribute *attr, void *user_data)
+{
+ log_service_event(attr, "Service Added");
+}
+
+static void service_removed_cb(struct gatt_db_attribute *attr, void *user_data)
+{
+ log_service_event(attr, "Service Removed");
+}
+
static struct client *client_create(int fd, uint16_t mtu)
{
struct client *cli;
@@ -150,6 +175,9 @@ static struct client *client_create(int fd, uint16_t mtu)
return NULL;
}
+ gatt_db_register(cli->db, service_added_cb, service_removed_cb,
+ NULL, NULL);
+
if (verbose) {
bt_att_set_debug(att, att_debug_cb, "att: ", NULL);
bt_gatt_client_set_debug(cli->gatt, gatt_debug_cb, "gatt: ",
--
2.2.0.rc0.207.ga3a616c
Now that gatt-db is reference counted, gatt-server should hold a
reference to it.
---
src/shared/gatt-server.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index ef91289..3f3db56 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -129,6 +129,7 @@ static void bt_gatt_server_free(struct bt_gatt_server *server)
queue_destroy(server->prep_queue, prep_write_data_destroy);
+ gatt_db_unref(server->db);
bt_att_unref(server->att);
free(server);
}
@@ -1177,14 +1178,14 @@ struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
{
struct bt_gatt_server *server;
- if (!att)
+ if (!att || !db)
return NULL;
server = new0(struct bt_gatt_server, 1);
if (!server)
return NULL;
- server->db = db;
+ server->db = gatt_db_ref(db);
server->att = bt_att_ref(att);
server->mtu = MAX(mtu, BT_ATT_DEFAULT_LE_MTU);
server->max_prep_queue_len = DEFAULT_MAX_PREP_QUEUE_LEN;
--
2.2.0.rc0.207.ga3a616c