All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH BlueZ 0/5] shared/gatt-db: Add support for client role.
@ 2014-11-26  5:26 Arman Uguray
  2014-11-26  5:26 ` [PATCH BlueZ 1/5] shared/gatt-db: Add high-level functions for client Arman Uguray
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Arman Uguray @ 2014-11-26  5:26 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Arman Uguray

This patch introduces new API functions to shared/gatt-db to prepare it for
GATT client-role usage. This is the first step before rewriting
shared/gatt-client using gatt-db.

This patch set adds the following:
  - Functions for extracting service, characteristic, descriptor, and include
    definition information from appropriate attributes.
  - foreach functions that allows iterating through all services, listing the
    characteristics of a particular service, etc.
  - Database clear functions to remove services from that database as needed.
  - A new service insertion function for inserting a service with a pre-defined
    handle into the database.
  - A new queue_insert_after function for shared/queue to enable the previous
    point.

Comments are welcome. I have implemented a trial version of the new gatt-client
using the above additions and this is the basic set of functionality that should
enable most use cases for client role. I think this gives us enough of a
starting point that we can easily change later if we find that these could be
done better.

Arman Uguray (5):
  shared/gatt-db: Add high-level functions for client
  shared: Add function to insert element after entry
  unit/test-queue: Add /queue/insert_after test
  shared/gatt-db: Add gatt_db_insert_service function
  shared/gatt-db: Add clear functions

 src/shared/gatt-db.c | 446 +++++++++++++++++++++++++++++++++++++++++++++++++--
 src/shared/gatt-db.h |  46 ++++++
 src/shared/queue.c   |  30 ++++
 src/shared/queue.h   |   1 +
 unit/test-queue.c    |  41 +++++
 5 files changed, 553 insertions(+), 11 deletions(-)

-- 
2.2.0.rc0.207.ga3a616c


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH BlueZ 1/5] shared/gatt-db: Add high-level functions for client
  2014-11-26  5:26 [PATCH BlueZ 0/5] shared/gatt-db: Add support for client role Arman Uguray
@ 2014-11-26  5:26 ` Arman Uguray
  2014-11-26 10:37   ` Luiz Augusto von Dentz
  2014-11-26  5:26 ` [PATCH BlueZ 2/5] shared: Add function to insert element after entry Arman Uguray
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Arman Uguray @ 2014-11-26  5:26 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Arman Uguray

This patch introduces foreach functions to gatt-db for enumerating
service, characteristic, and decriptors stored in the database as
gatt_db_attribute pointers. This patch also adds functions for
extracting service, characteristic, and include declaration data out of
matching attributes.

This is in preparation for using gatt-db as the attribute cache in
shared/gatt-client.
---
 src/shared/gatt-db.c | 279 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/shared/gatt-db.h |  37 +++++++
 2 files changed, 316 insertions(+)

diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index ab08c69..272ca31 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -193,6 +193,29 @@ static int uuid_to_le(const bt_uuid_t *uuid, uint8_t *dst)
 	return bt_uuid_len(&uuid128);
 }
 
+static bool le_to_uuid(const uint8_t *src, size_t len, bt_uuid_t *uuid)
+{
+	uint128_t u128;
+
+	if (len == 2) {
+		bt_uuid16_create(uuid, get_le16(src));
+		return true;
+	}
+
+	if (len == 4) {
+		bt_uuid32_create(uuid, get_le32(src));
+		return true;
+	}
+
+	if (len != 16)
+		return false;
+
+	bswap_128(src, &u128);
+	bt_uuid128_create(uuid, u128);
+
+	return true;
+}
+
 struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db,
 						const bt_uuid_t *uuid,
 						bool primary,
@@ -665,6 +688,155 @@ void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,
 	queue_foreach(db->services, find_information, &data);
 }
 
+struct foreach_data {
+	gatt_db_foreach_t func;
+	void *user_data;
+};
+
+static void foreach_service(void *data, void *user_data)
+{
+	struct gatt_db_service *service = data;
+	struct foreach_data *foreach_data = user_data;
+
+	foreach_data->func(service->attributes[0], foreach_data->user_data);
+}
+
+void gatt_db_foreach_service(struct gatt_db *db, gatt_db_foreach_t func,
+							void *user_data)
+{
+	struct foreach_data data;
+
+	if (!db || !func)
+		return;
+
+	data.func = func;
+	data.user_data = user_data;
+
+	queue_foreach(db->services, foreach_service, &data);
+}
+
+struct foreach_in_range_data {
+	gatt_db_foreach_t func;
+	void *user_data;
+	uint16_t start, end;
+};
+
+static void foreach_service_in_range(void *data, void *user_data)
+{
+	struct gatt_db_service *service = data;
+	struct foreach_in_range_data *foreach_data = user_data;
+	uint16_t svc_start;
+
+	svc_start = get_handle_at_index(service, 0);
+
+	if (svc_start > foreach_data->end || svc_start < foreach_data->start)
+		return;
+
+	foreach_data->func(service->attributes[0], foreach_data->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)
+{
+	struct foreach_in_range_data data;
+
+	if (!db || !func || start_handle > end_handle)
+		return;
+
+	data.func = func;
+	data.user_data = user_data;
+	data.start = start_handle;
+	data.end = end_handle;
+
+	queue_foreach(db->services, foreach_service_in_range, &data);
+}
+
+void gatt_db_attribute_foreach_charac(struct gatt_db_attribute *attrib,
+							gatt_db_foreach_t func,
+							void *user_data)
+{
+	struct gatt_db_service *service;
+	struct gatt_db_attribute *attr;
+	uint16_t i;
+
+	if (!attrib || !func)
+		return;
+
+	service = attrib->service;
+
+	for (i = 0; i < service->num_handles; i++) {
+		attr = service->attributes[i];
+		if (!attr)
+			continue;
+
+		if (bt_uuid_cmp(&characteristic_uuid, &attr->uuid))
+			continue;
+
+		func(attr, user_data);
+	}
+}
+
+void gatt_db_attribute_foreach_descr(struct gatt_db_attribute *attrib,
+							gatt_db_foreach_t func,
+							void *user_data)
+{
+	struct gatt_db_service *service;
+	struct gatt_db_attribute *attr;
+	uint16_t i;
+
+	if (!attrib || !func)
+		return;
+
+	/* Return if this attribute is not a characteristic declaration */
+	if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid))
+		return;
+
+	service = attrib->service;
+
+	/* Start from the attribute following the value handle */
+	i = attrib->handle - service->attributes[0]->handle + 2;
+	for (; i < service->num_handles; i++) {
+		attr = service->attributes[i];
+		if (!attr)
+			continue;
+
+		/* Return if we reached the end of this characteristic */
+		if (!bt_uuid_cmp(&characteristic_uuid, &attr->uuid) ||
+			!bt_uuid_cmp(&included_service_uuid, &attr->uuid))
+			return;
+
+		func(attr, user_data);
+	}
+}
+
+void gatt_db_attribute_foreach_incl(struct gatt_db_attribute *attrib,
+							gatt_db_foreach_t func,
+							void *user_data)
+{
+	struct gatt_db_service *service;
+	struct gatt_db_attribute *attr;
+	uint16_t i;
+
+	if (!attrib || !func)
+		return;
+
+	service = attrib->service;
+
+	for (i = 0; i < service->num_handles; i++) {
+		attr = service->attributes[i];
+		if (!attr)
+			continue;
+
+		if (bt_uuid_cmp(&included_service_uuid, &attr->uuid))
+			continue;
+
+		func(attr, user_data);
+	}
+}
+
 static bool find_service_for_handle(const void *data, const void *user_data)
 {
 	const struct gatt_db_service *service = data;
@@ -769,6 +941,113 @@ bool gatt_db_attribute_get_service_handles(struct gatt_db_attribute *attrib,
 	return true;
 }
 
+bool gatt_db_attribute_get_service_data(struct gatt_db_attribute *attrib,
+							uint16_t *start_handle,
+							uint16_t *end_handle,
+							bool *primary,
+							bt_uuid_t *uuid)
+{
+	struct gatt_db_service *service;
+	struct gatt_db_attribute *decl;
+
+	if (!attrib)
+		return false;
+
+	service = attrib->service;
+	decl = service->attributes[0];
+
+	if (start_handle)
+		*start_handle = decl->handle;
+
+	if (end_handle)
+		*end_handle = decl->handle + service->num_handles - 1;
+
+	if (primary)
+		*primary = bt_uuid_cmp(&decl->uuid, &secondary_service_uuid);
+
+	if (!uuid)
+		return true;
+
+	/*
+	 * The service declaration attribute value is the 16 or 128 bit service
+	 * UUID.
+	 */
+	return le_to_uuid(decl->value, decl->value_len, uuid);
+}
+
+bool gatt_db_attribute_get_characteristic_data(struct gatt_db_attribute *attrib,
+							uint16_t *handle,
+							uint16_t *value_handle,
+							uint8_t *properties,
+							bt_uuid_t *uuid)
+{
+	if (!attrib)
+		return false;
+
+	if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid))
+		return false;
+
+	/*
+	 * Characteristic declaration value:
+	 * 1 octet: Characteristic properties
+	 * 2 octets: Characteristic value handle
+	 * 2 or 16 octets: characteristic UUID
+	 */
+	if (!attrib->value || (attrib->value_len != 5 &&
+						attrib->value_len != 19))
+		return false;
+
+	if (handle)
+		*handle = attrib->handle;
+
+	if (properties)
+		*properties = attrib->value[0];
+
+	if (value_handle)
+		*value_handle = get_le16(attrib->value + 1);
+
+	if (!uuid)
+		return true;
+
+	return le_to_uuid(attrib->value + 3, attrib->value_len - 3, uuid);
+}
+
+bool gatt_db_attribute_get_include_data(struct gatt_db_attribute *attrib,
+							uint16_t *handle,
+							uint16_t *start_handle,
+							uint16_t *end_handle)
+{
+	if (!attrib)
+		return false;
+
+	if (bt_uuid_cmp(&included_service_uuid, &attrib->uuid))
+		return false;
+
+	/*
+	 * Include definition value:
+	 * 2 octets: start handle of included service
+	 * 2 octets: end handle of included service
+	 * optional 2 octets: 16-bit Bluetooth UUID
+	 */
+	if (!attrib->value || attrib->value_len < 4 || attrib->value_len > 6)
+		return false;
+
+	/*
+	 * We only return the handles since the UUID can be easily obtained
+	 * from the corresponding attribute.
+	 */
+	if (handle)
+		*handle = attrib->handle;
+
+	if (start_handle)
+		*start_handle = get_le16(attrib->value);
+
+	if (end_handle)
+		*end_handle = get_le16(attrib->value + 2);
+
+	return true;
+}
+
 bool gatt_db_attribute_get_permissions(struct gatt_db_attribute *attrib,
 							uint32_t *permissions)
 {
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 9c71814..73bb8d9 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -89,6 +89,26 @@ 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,
+							void *user_data);
+void gatt_db_foreach_service(struct gatt_db *db, gatt_db_foreach_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);
+void gatt_db_attribute_foreach_charac(struct gatt_db_attribute *attrib,
+							gatt_db_foreach_t func,
+							void *user_data);
+void gatt_db_attribute_foreach_descr(struct gatt_db_attribute *attrib,
+							gatt_db_foreach_t func,
+							void *user_data);
+void gatt_db_attribute_foreach_incl(struct gatt_db_attribute *attrib,
+							gatt_db_foreach_t func,
+							void *user_data);
+
+
 struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *db,
 							uint16_t handle);
 
@@ -103,6 +123,23 @@ bool gatt_db_attribute_get_service_handles(struct gatt_db_attribute *attrib,
 						uint16_t *start_handle,
 						uint16_t *end_handle);
 
+bool gatt_db_attribute_get_service_data(struct gatt_db_attribute *attrib,
+							uint16_t *start_handle,
+							uint16_t *end_handle,
+							bool *primary,
+							bt_uuid_t *uuid);
+
+bool gatt_db_attribute_get_characteristic_data(struct gatt_db_attribute *attrib,
+							uint16_t *handle,
+							uint16_t *value_handle,
+							uint8_t *properties,
+							bt_uuid_t *uuid);
+
+bool gatt_db_attribute_get_include_data(struct gatt_db_attribute *attrib,
+							uint16_t *handle,
+							uint16_t *start_handle,
+							uint16_t *end_handle);
+
 bool gatt_db_attribute_get_permissions(struct gatt_db_attribute *attrib,
 							uint32_t *permissions);
 
-- 
2.2.0.rc0.207.ga3a616c


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ 2/5] shared: Add function to insert element after entry
  2014-11-26  5:26 [PATCH BlueZ 0/5] shared/gatt-db: Add support for client role Arman Uguray
  2014-11-26  5:26 ` [PATCH BlueZ 1/5] shared/gatt-db: Add high-level functions for client Arman Uguray
@ 2014-11-26  5:26 ` Arman Uguray
  2014-11-26 10:15   ` Luiz Augusto von Dentz
  2014-11-26  5:26 ` [PATCH BlueZ 3/5] unit/test-queue: Add /queue/insert_after test Arman Uguray
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Arman Uguray @ 2014-11-26  5:26 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Arman Uguray

This patch adds queue_insert_after, which inserts the given "data"
after the first occurence of element "entry" in a queue.
---
 src/shared/queue.c | 30 ++++++++++++++++++++++++++++++
 src/shared/queue.h |  1 +
 2 files changed, 31 insertions(+)

diff --git a/src/shared/queue.c b/src/shared/queue.c
index 5329a80..1373d0d 100644
--- a/src/shared/queue.c
+++ b/src/shared/queue.c
@@ -134,6 +134,36 @@ bool queue_push_head(struct queue *queue, void *data)
 	return true;
 }
 
+bool queue_insert_after(struct queue *queue, void *entry, void *data)
+{
+	struct queue_entry *qentry = NULL, *tmp, *new_entry;
+
+	if (!queue)
+		return false;
+
+	for (tmp = queue->head; tmp; tmp = tmp->next)
+		if (tmp->data == entry)
+			qentry = tmp;
+
+	if (!qentry)
+		return false;
+
+	new_entry = new0(struct queue_entry, 1);
+	if (!new_entry)
+		return false;
+
+	new_entry->data = data;
+	new_entry->next = qentry->next;
+
+	if (!qentry->next)
+		queue->tail = new_entry;
+
+	qentry->next = new_entry;
+	queue->entries++;
+
+	return true;
+}
+
 void *queue_pop_head(struct queue *queue)
 {
 	struct queue_entry *entry;
diff --git a/src/shared/queue.h b/src/shared/queue.h
index 709590b..6ddc889 100644
--- a/src/shared/queue.h
+++ b/src/shared/queue.h
@@ -32,6 +32,7 @@ void queue_destroy(struct queue *queue, queue_destroy_func_t destroy);
 
 bool queue_push_tail(struct queue *queue, void *data);
 bool queue_push_head(struct queue *queue, void *data);
+bool queue_insert_after(struct queue *queue, void *entry, void *data);
 void *queue_pop_head(struct queue *queue);
 void *queue_peek_head(struct queue *queue);
 void *queue_peek_tail(struct queue *queue);
-- 
2.2.0.rc0.207.ga3a616c


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ 3/5] unit/test-queue: Add /queue/insert_after test
  2014-11-26  5:26 [PATCH BlueZ 0/5] shared/gatt-db: Add support for client role Arman Uguray
  2014-11-26  5:26 ` [PATCH BlueZ 1/5] shared/gatt-db: Add high-level functions for client Arman Uguray
  2014-11-26  5:26 ` [PATCH BlueZ 2/5] shared: Add function to insert element after entry Arman Uguray
@ 2014-11-26  5:26 ` Arman Uguray
  2014-11-26  5:26 ` [PATCH BlueZ 4/5] shared/gatt-db: Add gatt_db_insert_service function Arman Uguray
  2014-11-26  5:26 ` [PATCH BlueZ 5/5] shared/gatt-db: Add clear functions Arman Uguray
  4 siblings, 0 replies; 9+ messages in thread
From: Arman Uguray @ 2014-11-26  5:26 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Arman Uguray

This tests the queue_insert_after function.
---
 unit/test-queue.c | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/unit/test-queue.c b/unit/test-queue.c
index 9fe15ba..e5f9178 100644
--- a/unit/test-queue.c
+++ b/unit/test-queue.c
@@ -118,6 +118,46 @@ static void test_destroy_remove(void)
 	queue_destroy(static_queue, destroy_remove);
 }
 
+static void test_insert_after(void)
+{
+	struct queue *queue;
+	unsigned int len, i;
+
+	queue = queue_new();
+	g_assert(queue != NULL);
+
+	/*
+	 * Pre-populate queue. Initial elements are:
+	 *   [ NULL, 2, 5 ]
+	 */
+	g_assert(queue_push_tail(queue, NULL));
+	g_assert(queue_push_tail(queue, UINT_TO_PTR(2)));
+	g_assert(queue_push_tail(queue, UINT_TO_PTR(5)));
+	g_assert(queue_length(queue) == 3);
+
+	/* Invalid insertion */
+	g_assert(!queue_insert_after(queue, UINT_TO_PTR(6), UINT_TO_PTR(1)));
+
+	/* Valid insertions */
+	g_assert(queue_insert_after(queue, NULL, UINT_TO_PTR(1)));
+	g_assert(queue_insert_after(queue, UINT_TO_PTR(2), UINT_TO_PTR(3)));
+	g_assert(queue_insert_after(queue, UINT_TO_PTR(3), UINT_TO_PTR(4)));
+	g_assert(queue_insert_after(queue, UINT_TO_PTR(5), UINT_TO_PTR(6)));
+
+	g_assert(queue_peek_head(queue) == NULL);
+	g_assert(queue_peek_tail(queue) == UINT_TO_PTR(6));
+
+	/*
+	 * Queue should contain 7 elements:
+	 *   [ NULL, 1, 2, 3, 4, 5, 6 ]
+	 */
+	len = queue_length(queue);
+	g_assert(len == 7);
+
+	for (i = 0; i < 7; i++)
+		g_assert(queue_pop_head(queue) == UINT_TO_PTR(i));
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -126,6 +166,7 @@ int main(int argc, char *argv[])
 	g_test_add_func("/queue/foreach_destroy", test_foreach_destroy);
 	g_test_add_func("/queue/foreach_remove_all", test_foreach_remove_all);
 	g_test_add_func("/queue/destroy_remove", test_destroy_remove);
+	g_test_add_func("/queue/insert_after", test_insert_after);
 
 	return g_test_run();
 }
-- 
2.2.0.rc0.207.ga3a616c


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ 4/5] shared/gatt-db: Add gatt_db_insert_service function
  2014-11-26  5:26 [PATCH BlueZ 0/5] shared/gatt-db: Add support for client role Arman Uguray
                   ` (2 preceding siblings ...)
  2014-11-26  5:26 ` [PATCH BlueZ 3/5] unit/test-queue: Add /queue/insert_after test Arman Uguray
@ 2014-11-26  5:26 ` Arman Uguray
  2014-11-26  5:26 ` [PATCH BlueZ 5/5] shared/gatt-db: Add clear functions Arman Uguray
  4 siblings, 0 replies; 9+ messages in thread
From: Arman Uguray @ 2014-11-26  5:26 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Arman Uguray

This patch adds the gatt_db_insert_service function which inserts a
service with a pre-defined handle into the database if the desired
handle range is available.

This is primarily meant to be used by gatt-client, since the handles
in a client database need to perfectly match those returned from a
remote server. This also allows client to handle "Service Changed"
events in which a newly found service can be inserted at the correct
location within the database.
---
 src/shared/gatt-db.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++-----
 src/shared/gatt-db.h |   6 +++
 2 files changed, 117 insertions(+), 11 deletions(-)

diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index 272ca31..b5edd24 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -30,6 +30,10 @@
 #include "src/shared/timeout.h"
 #include "src/shared/gatt-db.h"
 
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
 #define MAX_CHAR_DECL_VALUE_LEN 19
 #define MAX_INCLUDED_VALUE_LEN 6
 #define ATTRIBUTE_TIMEOUT 1000
@@ -216,27 +220,23 @@ static bool le_to_uuid(const uint8_t *src, size_t len, bt_uuid_t *uuid)
 	return true;
 }
 
-struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db,
-						const bt_uuid_t *uuid,
-						bool primary,
-						uint16_t num_handles)
+static struct gatt_db_service *gatt_db_service_create(const bt_uuid_t *uuid,
+							bool primary,
+							uint16_t num_handles)
 {
 	struct gatt_db_service *service;
 	const bt_uuid_t *type;
 	uint8_t value[16];
 	uint16_t len;
 
-	if (num_handles < 1 || (num_handles + db->next_handle) > UINT16_MAX)
-		return 0;
-
 	service = new0(struct gatt_db_service, 1);
 	if (!service)
-		return 0;
+		return NULL;
 
 	service->attributes = new0(struct gatt_db_attribute *, num_handles);
 	if (!service->attributes) {
 		free(service);
-		return 0;
+		return NULL;
 	}
 
 	if (primary)
@@ -249,12 +249,30 @@ struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db,
 	service->attributes[0] = new_attribute(service, type, value, len);
 	if (!service->attributes[0]) {
 		gatt_db_service_destroy(service);
-		return 0;
+		return NULL;
 	}
 
+	return service;
+}
+
+struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db,
+						const bt_uuid_t *uuid,
+						bool primary,
+						uint16_t num_handles)
+{
+	struct gatt_db_service *service;
+
+	if (!db || num_handles < 1 ||
+				(num_handles + db->next_handle) > UINT16_MAX)
+		return NULL;
+
+	service = gatt_db_service_create(uuid, primary, num_handles);
+	if (!service)
+		return NULL;
+
 	if (!queue_push_tail(db->services, service)) {
 		gatt_db_service_destroy(service);
-		return 0;
+		return NULL;
 	}
 
 	/* TODO now we get next handle from database. We should first look
@@ -285,6 +303,88 @@ bool gatt_db_remove_service(struct gatt_db *db,
 	return true;
 }
 
+struct insert_loc_data {
+	struct gatt_db_service *cur;
+	uint16_t start, end;
+	bool fail;
+	bool done;
+};
+
+static void search_for_insert_loc(void *data, void *user_data)
+{
+	struct insert_loc_data *loc_data = user_data;
+	struct gatt_db_service *service = data;
+	uint16_t cur_start, cur_end;
+
+	if (loc_data->done)
+		return;
+
+	cur_start = service->attributes[0]->handle;
+	cur_end = service->attributes[0]->handle + service->num_handles - 1;
+
+	/* Abort if the requested range overlaps with an existing service. */
+	if ((loc_data->start >= cur_start && loc_data->start <= cur_end) ||
+		(loc_data->end >= cur_start && loc_data->end <= cur_end)) {
+		loc_data->fail = true;
+		loc_data->done = true;
+		return;
+	}
+
+	/* Check if this is where the service should be inserted. */
+	if (loc_data->end < cur_start) {
+		loc_data->done = true;
+		return;
+	}
+
+	loc_data->cur = service;
+}
+
+struct gatt_db_attribute *gatt_db_insert_service(struct gatt_db *db,
+							uint16_t handle,
+							const bt_uuid_t *uuid,
+							bool primary,
+							uint16_t num_handles)
+{
+	struct insert_loc_data data;
+	struct gatt_db_service *service;
+
+	if (!db || num_handles < 1 || handle + num_handles > UINT16_MAX)
+		return NULL;
+
+	memset(&data, 0, sizeof(data));
+
+	data.start = handle;
+	data.end = handle + num_handles - 1;
+
+	queue_foreach(db->services, search_for_insert_loc, &data);
+
+	if (data.fail)
+		return NULL;
+
+	service = gatt_db_service_create(uuid, primary, num_handles);
+	if (!service)
+		return NULL;
+
+	if (data.cur) {
+		if (!queue_insert_after(db->services, data.cur, service))
+			goto fail;
+	} else if (!queue_push_head(db->services, service)) {
+			goto fail;
+	}
+
+	service->attributes[0]->handle = handle;
+	service->num_handles = num_handles;
+
+	/* Fast-forward next_handle if the new service was added to the end */
+	db->next_handle = MAX(handle + num_handles + 1, db->next_handle);
+
+	return service->attributes[0];
+
+fail:
+	gatt_db_service_destroy(service);
+	return NULL;
+}
+
 static uint16_t get_attribute_index(struct gatt_db_service *service,
 							int end_offset)
 {
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 73bb8d9..4830cbd 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -35,6 +35,12 @@ struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db,
 bool gatt_db_remove_service(struct gatt_db *db,
 					struct gatt_db_attribute *attrib);
 
+struct gatt_db_attribute *gatt_db_insert_service(struct gatt_db *db,
+							uint16_t handle,
+							const bt_uuid_t *uuid,
+							bool primary,
+							uint16_t num_handles);
+
 typedef void (*gatt_db_read_t) (struct gatt_db_attribute *attrib,
 					unsigned int id, uint16_t offset,
 					uint8_t opcode, bdaddr_t *bdaddr,
-- 
2.2.0.rc0.207.ga3a616c


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ 5/5] shared/gatt-db: Add clear functions
  2014-11-26  5:26 [PATCH BlueZ 0/5] shared/gatt-db: Add support for client role Arman Uguray
                   ` (3 preceding siblings ...)
  2014-11-26  5:26 ` [PATCH BlueZ 4/5] shared/gatt-db: Add gatt_db_insert_service function Arman Uguray
@ 2014-11-26  5:26 ` Arman Uguray
  4 siblings, 0 replies; 9+ messages in thread
From: Arman Uguray @ 2014-11-26  5:26 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Arman Uguray

This patch introduces gatt_db_clear and gatt_db_clear_range functions
for removing services from the database.
---
 src/shared/gatt-db.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
 src/shared/gatt-db.h |  3 +++
 2 files changed, 48 insertions(+)

diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index b5edd24..4bd1a99 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -303,6 +303,51 @@ bool gatt_db_remove_service(struct gatt_db *db,
 	return true;
 }
 
+bool gatt_db_clear(struct gatt_db *db)
+{
+	if (!db)
+		return false;
+
+	queue_remove_all(db->services, NULL, NULL, gatt_db_service_destroy);
+
+	db->next_handle = 0;
+
+	return true;
+}
+
+struct clear_range {
+	uint16_t start, end;
+};
+
+static bool match_range(const void *a, const void *b)
+{
+	const struct gatt_db_service *service = a;
+	const struct clear_range *range = b;
+	uint16_t svc_start, svc_end;
+
+	svc_start = service->attributes[0]->handle;
+	svc_end = service->attributes[0]->handle + service->num_handles - 1;
+
+	return svc_start <= range->end && svc_end >= range->start;
+}
+
+bool gatt_db_clear_range(struct gatt_db *db, uint16_t start_handle,
+							uint16_t end_handle)
+{
+	struct clear_range range;
+
+	if (!db)
+		return false;
+
+	range.start = start_handle;
+	range.end = end_handle;
+
+	queue_remove_all(db->services, match_range, &range,
+						gatt_db_service_destroy);
+
+	return true;
+}
+
 struct insert_loc_data {
 	struct gatt_db_service *cur;
 	uint16_t start, end;
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 4830cbd..dfa641c 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -34,6 +34,9 @@ struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db,
 
 bool gatt_db_remove_service(struct gatt_db *db,
 					struct gatt_db_attribute *attrib);
+bool gatt_db_clear(struct gatt_db *db);
+bool gatt_db_clear_range(struct gatt_db *db, uint16_t start_handle,
+							uint16_t end_handle);
 
 struct gatt_db_attribute *gatt_db_insert_service(struct gatt_db *db,
 							uint16_t handle,
-- 
2.2.0.rc0.207.ga3a616c


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH BlueZ 2/5] shared: Add function to insert element after entry
  2014-11-26  5:26 ` [PATCH BlueZ 2/5] shared: Add function to insert element after entry Arman Uguray
@ 2014-11-26 10:15   ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2014-11-26 10:15 UTC (permalink / raw)
  To: Arman Uguray; +Cc: linux-bluetooth

Hi Arman,

On Wed, Nov 26, 2014 at 7:26 AM, Arman Uguray <armansito@chromium.org> wrote:
> This patch adds queue_insert_after, which inserts the given "data"
> after the first occurence of element "entry" in a queue.
> ---
>  src/shared/queue.c | 30 ++++++++++++++++++++++++++++++
>  src/shared/queue.h |  1 +
>  2 files changed, 31 insertions(+)
>
> diff --git a/src/shared/queue.c b/src/shared/queue.c
> index 5329a80..1373d0d 100644
> --- a/src/shared/queue.c
> +++ b/src/shared/queue.c
> @@ -134,6 +134,36 @@ bool queue_push_head(struct queue *queue, void *data)
>         return true;
>  }
>
> +bool queue_insert_after(struct queue *queue, void *entry, void *data)
> +{
> +       struct queue_entry *qentry = NULL, *tmp, *new_entry;
> +
> +       if (!queue)
> +               return false;
> +
> +       for (tmp = queue->head; tmp; tmp = tmp->next)
> +               if (tmp->data == entry)
> +                       qentry = tmp;
> +
> +       if (!qentry)
> +               return false;
> +
> +       new_entry = new0(struct queue_entry, 1);
> +       if (!new_entry)
> +               return false;
> +
> +       new_entry->data = data;
> +       new_entry->next = qentry->next;
> +
> +       if (!qentry->next)
> +               queue->tail = new_entry;
> +
> +       qentry->next = new_entry;
> +       queue->entries++;
> +
> +       return true;
> +}
> +
>  void *queue_pop_head(struct queue *queue)
>  {
>         struct queue_entry *entry;
> diff --git a/src/shared/queue.h b/src/shared/queue.h
> index 709590b..6ddc889 100644
> --- a/src/shared/queue.h
> +++ b/src/shared/queue.h
> @@ -32,6 +32,7 @@ void queue_destroy(struct queue *queue, queue_destroy_func_t destroy);
>
>  bool queue_push_tail(struct queue *queue, void *data);
>  bool queue_push_head(struct queue *queue, void *data);
> +bool queue_insert_after(struct queue *queue, void *entry, void *data);

I guess naming it queue_push_after would be better here.

>  void *queue_pop_head(struct queue *queue);
>  void *queue_peek_head(struct queue *queue);
>  void *queue_peek_tail(struct queue *queue);
> --
> 2.2.0.rc0.207.ga3a616c
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH BlueZ 1/5] shared/gatt-db: Add high-level functions for client
  2014-11-26  5:26 ` [PATCH BlueZ 1/5] shared/gatt-db: Add high-level functions for client Arman Uguray
@ 2014-11-26 10:37   ` Luiz Augusto von Dentz
  2014-11-26 21:48     ` Arman Uguray
  0 siblings, 1 reply; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2014-11-26 10:37 UTC (permalink / raw)
  To: Arman Uguray; +Cc: linux-bluetooth

Hi Arman,

On Wed, Nov 26, 2014 at 7:26 AM, Arman Uguray <armansito@chromium.org> wrote:
> This patch introduces foreach functions to gatt-db for enumerating
> service, characteristic, and decriptors stored in the database as
> gatt_db_attribute pointers. This patch also adds functions for
> extracting service, characteristic, and include declaration data out of
> matching attributes.
>
> This is in preparation for using gatt-db as the attribute cache in
> shared/gatt-client.
> ---
>  src/shared/gatt-db.c | 279 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  src/shared/gatt-db.h |  37 +++++++
>  2 files changed, 316 insertions(+)
>
> diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
> index ab08c69..272ca31 100644
> --- a/src/shared/gatt-db.c
> +++ b/src/shared/gatt-db.c
> @@ -193,6 +193,29 @@ static int uuid_to_le(const bt_uuid_t *uuid, uint8_t *dst)
>         return bt_uuid_len(&uuid128);
>  }
>
> +static bool le_to_uuid(const uint8_t *src, size_t len, bt_uuid_t *uuid)
> +{
> +       uint128_t u128;
> +
> +       if (len == 2) {
> +               bt_uuid16_create(uuid, get_le16(src));
> +               return true;
> +       }
> +
> +       if (len == 4) {
> +               bt_uuid32_create(uuid, get_le32(src));
> +               return true;
> +       }
> +
> +       if (len != 16)
> +               return false;
> +
> +       bswap_128(src, &u128);
> +       bt_uuid128_create(uuid, u128);
> +
> +       return true;
> +}
> +
>  struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db,
>                                                 const bt_uuid_t *uuid,
>                                                 bool primary,
> @@ -665,6 +688,155 @@ void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,
>         queue_foreach(db->services, find_information, &data);
>  }
>
> +struct foreach_data {
> +       gatt_db_foreach_t func;
> +       void *user_data;
> +};
> +
> +static void foreach_service(void *data, void *user_data)
> +{
> +       struct gatt_db_service *service = data;
> +       struct foreach_data *foreach_data = user_data;
> +
> +       foreach_data->func(service->attributes[0], foreach_data->user_data);
> +}
> +
> +void gatt_db_foreach_service(struct gatt_db *db, gatt_db_foreach_t func,
> +                                                       void *user_data)
> +{
> +       struct foreach_data data;
> +
> +       if (!db || !func)
> +               return;
> +
> +       data.func = func;
> +       data.user_data = user_data;
> +
> +       queue_foreach(db->services, foreach_service, &data);
> +}

You could actually make gatt_db_foreach_service call
gatt_db_foreach_service_in_range with the maximum range here so we
have less code to maintain.

> +struct foreach_in_range_data {
> +       gatt_db_foreach_t func;
> +       void *user_data;
> +       uint16_t start, end;
> +};
> +
> +static void foreach_service_in_range(void *data, void *user_data)
> +{
> +       struct gatt_db_service *service = data;
> +       struct foreach_in_range_data *foreach_data = user_data;
> +       uint16_t svc_start;
> +
> +       svc_start = get_handle_at_index(service, 0);
> +
> +       if (svc_start > foreach_data->end || svc_start < foreach_data->start)
> +               return;
> +
> +       foreach_data->func(service->attributes[0], foreach_data->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)
> +{
> +       struct foreach_in_range_data data;
> +
> +       if (!db || !func || start_handle > end_handle)
> +               return;
> +
> +       data.func = func;
> +       data.user_data = user_data;
> +       data.start = start_handle;
> +       data.end = end_handle;
> +
> +       queue_foreach(db->services, foreach_service_in_range, &data);
> +}
> +
> +void gatt_db_attribute_foreach_charac(struct gatt_db_attribute *attrib,
> +                                                       gatt_db_foreach_t func,
> +                                                       void *user_data)
> +{
> +       struct gatt_db_service *service;
> +       struct gatt_db_attribute *attr;
> +       uint16_t i;
> +
> +       if (!attrib || !func)
> +               return;
> +
> +       service = attrib->service;
> +
> +       for (i = 0; i < service->num_handles; i++) {
> +               attr = service->attributes[i];
> +               if (!attr)
> +                       continue;
> +
> +               if (bt_uuid_cmp(&characteristic_uuid, &attr->uuid))
> +                       continue;
> +
> +               func(attr, user_data);
> +       }
> +}
> +
> +void gatt_db_attribute_foreach_descr(struct gatt_db_attribute *attrib,
> +                                                       gatt_db_foreach_t func,
> +                                                       void *user_data)
> +{
> +       struct gatt_db_service *service;
> +       struct gatt_db_attribute *attr;
> +       uint16_t i;
> +
> +       if (!attrib || !func)
> +               return;
> +
> +       /* Return if this attribute is not a characteristic declaration */
> +       if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid))
> +               return;
> +
> +       service = attrib->service;
> +
> +       /* Start from the attribute following the value handle */
> +       i = attrib->handle - service->attributes[0]->handle + 2;
> +       for (; i < service->num_handles; i++) {
> +               attr = service->attributes[i];
> +               if (!attr)
> +                       continue;
> +
> +               /* Return if we reached the end of this characteristic */
> +               if (!bt_uuid_cmp(&characteristic_uuid, &attr->uuid) ||
> +                       !bt_uuid_cmp(&included_service_uuid, &attr->uuid))
> +                       return;
> +
> +               func(attr, user_data);
> +       }
> +}
> +
> +void gatt_db_attribute_foreach_incl(struct gatt_db_attribute *attrib,
> +                                                       gatt_db_foreach_t func,
> +                                                       void *user_data)
> +{
> +       struct gatt_db_service *service;
> +       struct gatt_db_attribute *attr;
> +       uint16_t i;
> +
> +       if (!attrib || !func)
> +               return;
> +
> +       service = attrib->service;
> +
> +       for (i = 0; i < service->num_handles; i++) {
> +               attr = service->attributes[i];
> +               if (!attr)
> +                       continue;
> +
> +               if (bt_uuid_cmp(&included_service_uuid, &attr->uuid))
> +                       continue;
> +
> +               func(attr, user_data);
> +       }
> +}

These also seems very similar, perhaps we could have
gatt_db_attribute_foreach that takes the uuid in addition to these
which btw could just call it with the right uuid well except for
descriptors which seems a bit different, actually we need something
that can replace gatt_db_find_by_type anyway since it exposes queues
and makes us iterate twice.

>  static bool find_service_for_handle(const void *data, const void *user_data)
>  {
>         const struct gatt_db_service *service = data;
> @@ -769,6 +941,113 @@ bool gatt_db_attribute_get_service_handles(struct gatt_db_attribute *attrib,
>         return true;
>  }
>
> +bool gatt_db_attribute_get_service_data(struct gatt_db_attribute *attrib,
> +                                                       uint16_t *start_handle,
> +                                                       uint16_t *end_handle,
> +                                                       bool *primary,
> +                                                       bt_uuid_t *uuid)
> +{
> +       struct gatt_db_service *service;
> +       struct gatt_db_attribute *decl;
> +
> +       if (!attrib)
> +               return false;
> +
> +       service = attrib->service;
> +       decl = service->attributes[0];
> +
> +       if (start_handle)
> +               *start_handle = decl->handle;
> +
> +       if (end_handle)
> +               *end_handle = decl->handle + service->num_handles - 1;
> +
> +       if (primary)
> +               *primary = bt_uuid_cmp(&decl->uuid, &secondary_service_uuid);
> +
> +       if (!uuid)
> +               return true;
> +
> +       /*
> +        * The service declaration attribute value is the 16 or 128 bit service
> +        * UUID.
> +        */
> +       return le_to_uuid(decl->value, decl->value_len, uuid);
> +}
> +
> +bool gatt_db_attribute_get_characteristic_data(struct gatt_db_attribute *attrib,
> +                                                       uint16_t *handle,
> +                                                       uint16_t *value_handle,
> +                                                       uint8_t *properties,
> +                                                       bt_uuid_t *uuid)
> +{
> +       if (!attrib)
> +               return false;
> +
> +       if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid))
> +               return false;
> +
> +       /*
> +        * Characteristic declaration value:
> +        * 1 octet: Characteristic properties
> +        * 2 octets: Characteristic value handle
> +        * 2 or 16 octets: characteristic UUID
> +        */
> +       if (!attrib->value || (attrib->value_len != 5 &&
> +                                               attrib->value_len != 19))
> +               return false;
> +
> +       if (handle)
> +               *handle = attrib->handle;
> +
> +       if (properties)
> +               *properties = attrib->value[0];
> +
> +       if (value_handle)
> +               *value_handle = get_le16(attrib->value + 1);
> +
> +       if (!uuid)
> +               return true;
> +
> +       return le_to_uuid(attrib->value + 3, attrib->value_len - 3, uuid);
> +}
> +
> +bool gatt_db_attribute_get_include_data(struct gatt_db_attribute *attrib,
> +                                                       uint16_t *handle,
> +                                                       uint16_t *start_handle,
> +                                                       uint16_t *end_handle)
> +{
> +       if (!attrib)
> +               return false;
> +
> +       if (bt_uuid_cmp(&included_service_uuid, &attrib->uuid))
> +               return false;
> +
> +       /*
> +        * Include definition value:
> +        * 2 octets: start handle of included service
> +        * 2 octets: end handle of included service
> +        * optional 2 octets: 16-bit Bluetooth UUID
> +        */
> +       if (!attrib->value || attrib->value_len < 4 || attrib->value_len > 6)
> +               return false;
> +
> +       /*
> +        * We only return the handles since the UUID can be easily obtained
> +        * from the corresponding attribute.
> +        */
> +       if (handle)
> +               *handle = attrib->handle;
> +
> +       if (start_handle)
> +               *start_handle = get_le16(attrib->value);
> +
> +       if (end_handle)
> +               *end_handle = get_le16(attrib->value + 2);
> +
> +       return true;
> +}

These Im fine

>  bool gatt_db_attribute_get_permissions(struct gatt_db_attribute *attrib,
>                                                         uint32_t *permissions)
>  {
> diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
> index 9c71814..73bb8d9 100644
> --- a/src/shared/gatt-db.h
> +++ b/src/shared/gatt-db.h
> @@ -89,6 +89,26 @@ 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,
> +                                                       void *user_data);
> +void gatt_db_foreach_service(struct gatt_db *db, gatt_db_foreach_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);
> +void gatt_db_attribute_foreach_charac(struct gatt_db_attribute *attrib,
> +                                                       gatt_db_foreach_t func,
> +                                                       void *user_data);
> +void gatt_db_attribute_foreach_descr(struct gatt_db_attribute *attrib,
> +                                                       gatt_db_foreach_t func,
> +                                                       void *user_data);
> +void gatt_db_attribute_foreach_incl(struct gatt_db_attribute *attrib,
> +                                                       gatt_db_foreach_t func,
> +                                                       void *user_data);
> +
> +
>  struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *db,
>                                                         uint16_t handle);
>
> @@ -103,6 +123,23 @@ bool gatt_db_attribute_get_service_handles(struct gatt_db_attribute *attrib,
>                                                 uint16_t *start_handle,
>                                                 uint16_t *end_handle);
>
> +bool gatt_db_attribute_get_service_data(struct gatt_db_attribute *attrib,
> +                                                       uint16_t *start_handle,
> +                                                       uint16_t *end_handle,
> +                                                       bool *primary,
> +                                                       bt_uuid_t *uuid);
> +
> +bool gatt_db_attribute_get_characteristic_data(struct gatt_db_attribute *attrib,
> +                                                       uint16_t *handle,
> +                                                       uint16_t *value_handle,
> +                                                       uint8_t *properties,
> +                                                       bt_uuid_t *uuid);
> +
> +bool gatt_db_attribute_get_include_data(struct gatt_db_attribute *attrib,
> +                                                       uint16_t *handle,
> +                                                       uint16_t *start_handle,
> +                                                       uint16_t *end_handle);
> +
>  bool gatt_db_attribute_get_permissions(struct gatt_db_attribute *attrib,
>                                                         uint32_t *permissions);
>
> --
> 2.2.0.rc0.207.ga3a616c
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH BlueZ 1/5] shared/gatt-db: Add high-level functions for client
  2014-11-26 10:37   ` Luiz Augusto von Dentz
@ 2014-11-26 21:48     ` Arman Uguray
  0 siblings, 0 replies; 9+ messages in thread
From: Arman Uguray @ 2014-11-26 21:48 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: Arman Uguray, linux-bluetooth

Hi Luiz,

> On Wed, Nov 26, 2014 at 2:37 AM, Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
> Hi Arman,
>
> On Wed, Nov 26, 2014 at 7:26 AM, Arman Uguray <armansito@chromium.org> wrote:
>> This patch introduces foreach functions to gatt-db for enumerating
>> service, characteristic, and decriptors stored in the database as
>> gatt_db_attribute pointers. This patch also adds functions for
>> extracting service, characteristic, and include declaration data out of
>> matching attributes.
>>
>> This is in preparation for using gatt-db as the attribute cache in
>> shared/gatt-client.
>> ---
>>  src/shared/gatt-db.c | 279 +++++++++++++++++++++++++++++++++++++++++++++++++++
>>  src/shared/gatt-db.h |  37 +++++++
>>  2 files changed, 316 insertions(+)
>>
>> diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
>> index ab08c69..272ca31 100644
>> --- a/src/shared/gatt-db.c
>> +++ b/src/shared/gatt-db.c
>> @@ -193,6 +193,29 @@ static int uuid_to_le(const bt_uuid_t *uuid, uint8_t *dst)
>>         return bt_uuid_len(&uuid128);
>>  }
>>
>> +static bool le_to_uuid(const uint8_t *src, size_t len, bt_uuid_t *uuid)
>> +{
>> +       uint128_t u128;
>> +
>> +       if (len == 2) {
>> +               bt_uuid16_create(uuid, get_le16(src));
>> +               return true;
>> +       }
>> +
>> +       if (len == 4) {
>> +               bt_uuid32_create(uuid, get_le32(src));
>> +               return true;
>> +       }
>> +
>> +       if (len != 16)
>> +               return false;
>> +
>> +       bswap_128(src, &u128);
>> +       bt_uuid128_create(uuid, u128);
>> +
>> +       return true;
>> +}
>> +
>>  struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db,
>>                                                 const bt_uuid_t *uuid,
>>                                                 bool primary,
>> @@ -665,6 +688,155 @@ void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,
>>         queue_foreach(db->services, find_information, &data);
>>  }
>>
>> +struct foreach_data {
>> +       gatt_db_foreach_t func;
>> +       void *user_data;
>> +};
>> +
>> +static void foreach_service(void *data, void *user_data)
>> +{
>> +       struct gatt_db_service *service = data;
>> +       struct foreach_data *foreach_data = user_data;
>> +
>> +       foreach_data->func(service->attributes[0], foreach_data->user_data);
>> +}
>> +
>> +void gatt_db_foreach_service(struct gatt_db *db, gatt_db_foreach_t func,
>> +                                                       void *user_data)
>> +{
>> +       struct foreach_data data;
>> +
>> +       if (!db || !func)
>> +               return;
>> +
>> +       data.func = func;
>> +       data.user_data = user_data;
>> +
>> +       queue_foreach(db->services, foreach_service, &data);
>> +}
>
> You could actually make gatt_db_foreach_service call
> gatt_db_foreach_service_in_range with the maximum range here so we
> have less code to maintain.
>
>> +struct foreach_in_range_data {
>> +       gatt_db_foreach_t func;
>> +       void *user_data;
>> +       uint16_t start, end;
>> +};
>> +
>> +static void foreach_service_in_range(void *data, void *user_data)
>> +{
>> +       struct gatt_db_service *service = data;
>> +       struct foreach_in_range_data *foreach_data = user_data;
>> +       uint16_t svc_start;
>> +
>> +       svc_start = get_handle_at_index(service, 0);
>> +
>> +       if (svc_start > foreach_data->end || svc_start < foreach_data->start)
>> +               return;
>> +
>> +       foreach_data->func(service->attributes[0], foreach_data->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)
>> +{
>> +       struct foreach_in_range_data data;
>> +
>> +       if (!db || !func || start_handle > end_handle)
>> +               return;
>> +
>> +       data.func = func;
>> +       data.user_data = user_data;
>> +       data.start = start_handle;
>> +       data.end = end_handle;
>> +
>> +       queue_foreach(db->services, foreach_service_in_range, &data);
>> +}
>> +
>> +void gatt_db_attribute_foreach_charac(struct gatt_db_attribute *attrib,
>> +                                                       gatt_db_foreach_t func,
>> +                                                       void *user_data)
>> +{
>> +       struct gatt_db_service *service;
>> +       struct gatt_db_attribute *attr;
>> +       uint16_t i;
>> +
>> +       if (!attrib || !func)
>> +               return;
>> +
>> +       service = attrib->service;
>> +
>> +       for (i = 0; i < service->num_handles; i++) {
>> +               attr = service->attributes[i];
>> +               if (!attr)
>> +                       continue;
>> +
>> +               if (bt_uuid_cmp(&characteristic_uuid, &attr->uuid))
>> +                       continue;
>> +
>> +               func(attr, user_data);
>> +       }
>> +}
>> +
>> +void gatt_db_attribute_foreach_descr(struct gatt_db_attribute *attrib,
>> +                                                       gatt_db_foreach_t func,
>> +                                                       void *user_data)
>> +{
>> +       struct gatt_db_service *service;
>> +       struct gatt_db_attribute *attr;
>> +       uint16_t i;
>> +
>> +       if (!attrib || !func)
>> +               return;
>> +
>> +       /* Return if this attribute is not a characteristic declaration */
>> +       if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid))
>> +               return;
>> +
>> +       service = attrib->service;
>> +
>> +       /* Start from the attribute following the value handle */
>> +       i = attrib->handle - service->attributes[0]->handle + 2;
>> +       for (; i < service->num_handles; i++) {
>> +               attr = service->attributes[i];
>> +               if (!attr)
>> +                       continue;
>> +
>> +               /* Return if we reached the end of this characteristic */
>> +               if (!bt_uuid_cmp(&characteristic_uuid, &attr->uuid) ||
>> +                       !bt_uuid_cmp(&included_service_uuid, &attr->uuid))
>> +                       return;
>> +
>> +               func(attr, user_data);
>> +       }
>> +}
>> +
>> +void gatt_db_attribute_foreach_incl(struct gatt_db_attribute *attrib,
>> +                                                       gatt_db_foreach_t func,
>> +                                                       void *user_data)
>> +{
>> +       struct gatt_db_service *service;
>> +       struct gatt_db_attribute *attr;
>> +       uint16_t i;
>> +
>> +       if (!attrib || !func)
>> +               return;
>> +
>> +       service = attrib->service;
>> +
>> +       for (i = 0; i < service->num_handles; i++) {
>> +               attr = service->attributes[i];
>> +               if (!attr)
>> +                       continue;
>> +
>> +               if (bt_uuid_cmp(&included_service_uuid, &attr->uuid))
>> +                       continue;
>> +
>> +               func(attr, user_data);
>> +       }
>> +}
>
> These also seems very similar, perhaps we could have
> gatt_db_attribute_foreach that takes the uuid in addition to these
> which btw could just call it with the right uuid well except for
> descriptors which seems a bit different, actually we need something
> that can replace gatt_db_find_by_type anyway since it exposes queues
> and makes us iterate twice.
>

And I guess for services we would need a gatt_db_find_by_type_value of
sorts too, though I'm digressing. A gatt_db_attribute_foreach with
uuid would probably just iterate over the whole database and we could
have a gatt_db_service_foreach_attribute with uuid which could then
iterate within that service only. I think the latter will be more
useful for client so I'll start with that at first.

As you said, for descriptors the logic is slightly different.

>>  static bool find_service_for_handle(const void *data, const void *user_data)
>>  {
>>         const struct gatt_db_service *service = data;
>> @@ -769,6 +941,113 @@ bool gatt_db_attribute_get_service_handles(struct gatt_db_attribute *attrib,
>>         return true;
>>  }
>>
>> +bool gatt_db_attribute_get_service_data(struct gatt_db_attribute *attrib,
>> +                                                       uint16_t *start_handle,
>> +                                                       uint16_t *end_handle,
>> +                                                       bool *primary,
>> +                                                       bt_uuid_t *uuid)
>> +{
>> +       struct gatt_db_service *service;
>> +       struct gatt_db_attribute *decl;
>> +
>> +       if (!attrib)
>> +               return false;
>> +
>> +       service = attrib->service;
>> +       decl = service->attributes[0];
>> +
>> +       if (start_handle)
>> +               *start_handle = decl->handle;
>> +
>> +       if (end_handle)
>> +               *end_handle = decl->handle + service->num_handles - 1;
>> +
>> +       if (primary)
>> +               *primary = bt_uuid_cmp(&decl->uuid, &secondary_service_uuid);
>> +
>> +       if (!uuid)
>> +               return true;
>> +
>> +       /*
>> +        * The service declaration attribute value is the 16 or 128 bit service
>> +        * UUID.
>> +        */
>> +       return le_to_uuid(decl->value, decl->value_len, uuid);
>> +}
>> +
>> +bool gatt_db_attribute_get_characteristic_data(struct gatt_db_attribute *attrib,
>> +                                                       uint16_t *handle,
>> +                                                       uint16_t *value_handle,
>> +                                                       uint8_t *properties,
>> +                                                       bt_uuid_t *uuid)
>> +{
>> +       if (!attrib)
>> +               return false;
>> +
>> +       if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid))
>> +               return false;
>> +
>> +       /*
>> +        * Characteristic declaration value:
>> +        * 1 octet: Characteristic properties
>> +        * 2 octets: Characteristic value handle
>> +        * 2 or 16 octets: characteristic UUID
>> +        */
>> +       if (!attrib->value || (attrib->value_len != 5 &&
>> +                                               attrib->value_len != 19))
>> +               return false;
>> +
>> +       if (handle)
>> +               *handle = attrib->handle;
>> +
>> +       if (properties)
>> +               *properties = attrib->value[0];
>> +
>> +       if (value_handle)
>> +               *value_handle = get_le16(attrib->value + 1);
>> +
>> +       if (!uuid)
>> +               return true;
>> +
>> +       return le_to_uuid(attrib->value + 3, attrib->value_len - 3, uuid);
>> +}
>> +
>> +bool gatt_db_attribute_get_include_data(struct gatt_db_attribute *attrib,
>> +                                                       uint16_t *handle,
>> +                                                       uint16_t *start_handle,
>> +                                                       uint16_t *end_handle)
>> +{
>> +       if (!attrib)
>> +               return false;
>> +
>> +       if (bt_uuid_cmp(&included_service_uuid, &attrib->uuid))
>> +               return false;
>> +
>> +       /*
>> +        * Include definition value:
>> +        * 2 octets: start handle of included service
>> +        * 2 octets: end handle of included service
>> +        * optional 2 octets: 16-bit Bluetooth UUID
>> +        */
>> +       if (!attrib->value || attrib->value_len < 4 || attrib->value_len > 6)
>> +               return false;
>> +
>> +       /*
>> +        * We only return the handles since the UUID can be easily obtained
>> +        * from the corresponding attribute.
>> +        */
>> +       if (handle)
>> +               *handle = attrib->handle;
>> +
>> +       if (start_handle)
>> +               *start_handle = get_le16(attrib->value);
>> +
>> +       if (end_handle)
>> +               *end_handle = get_le16(attrib->value + 2);
>> +
>> +       return true;
>> +}
>
> These Im fine
>
>>  bool gatt_db_attribute_get_permissions(struct gatt_db_attribute *attrib,
>>                                                         uint32_t *permissions)
>>  {
>> diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
>> index 9c71814..73bb8d9 100644
>> --- a/src/shared/gatt-db.h
>> +++ b/src/shared/gatt-db.h
>> @@ -89,6 +89,26 @@ 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,
>> +                                                       void *user_data);
>> +void gatt_db_foreach_service(struct gatt_db *db, gatt_db_foreach_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);
>> +void gatt_db_attribute_foreach_charac(struct gatt_db_attribute *attrib,
>> +                                                       gatt_db_foreach_t func,
>> +                                                       void *user_data);
>> +void gatt_db_attribute_foreach_descr(struct gatt_db_attribute *attrib,
>> +                                                       gatt_db_foreach_t func,
>> +                                                       void *user_data);
>> +void gatt_db_attribute_foreach_incl(struct gatt_db_attribute *attrib,
>> +                                                       gatt_db_foreach_t func,
>> +                                                       void *user_data);
>> +
>> +
>>  struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *db,
>>                                                         uint16_t handle);
>>
>> @@ -103,6 +123,23 @@ bool gatt_db_attribute_get_service_handles(struct gatt_db_attribute *attrib,
>>                                                 uint16_t *start_handle,
>>                                                 uint16_t *end_handle);
>>
>> +bool gatt_db_attribute_get_service_data(struct gatt_db_attribute *attrib,
>> +                                                       uint16_t *start_handle,
>> +                                                       uint16_t *end_handle,
>> +                                                       bool *primary,
>> +                                                       bt_uuid_t *uuid);
>> +
>> +bool gatt_db_attribute_get_characteristic_data(struct gatt_db_attribute *attrib,
>> +                                                       uint16_t *handle,
>> +                                                       uint16_t *value_handle,
>> +                                                       uint8_t *properties,
>> +                                                       bt_uuid_t *uuid);
>> +
>> +bool gatt_db_attribute_get_include_data(struct gatt_db_attribute *attrib,
>> +                                                       uint16_t *handle,
>> +                                                       uint16_t *start_handle,
>> +                                                       uint16_t *end_handle);
>> +
>>  bool gatt_db_attribute_get_permissions(struct gatt_db_attribute *attrib,
>>                                                         uint32_t *permissions);
>>
>> --
>> 2.2.0.rc0.207.ga3a616c
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
>
>
> --
> Luiz Augusto von Dentz

Arman

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2014-11-26 21:48 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-11-26  5:26 [PATCH BlueZ 0/5] shared/gatt-db: Add support for client role Arman Uguray
2014-11-26  5:26 ` [PATCH BlueZ 1/5] shared/gatt-db: Add high-level functions for client Arman Uguray
2014-11-26 10:37   ` Luiz Augusto von Dentz
2014-11-26 21:48     ` Arman Uguray
2014-11-26  5:26 ` [PATCH BlueZ 2/5] shared: Add function to insert element after entry Arman Uguray
2014-11-26 10:15   ` Luiz Augusto von Dentz
2014-11-26  5:26 ` [PATCH BlueZ 3/5] unit/test-queue: Add /queue/insert_after test Arman Uguray
2014-11-26  5:26 ` [PATCH BlueZ 4/5] shared/gatt-db: Add gatt_db_insert_service function Arman Uguray
2014-11-26  5:26 ` [PATCH BlueZ 5/5] shared/gatt-db: Add clear functions Arman Uguray

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.