[PATCH v4 BlueZ 1/4] gatt: Add support for find included services

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Some services like HID over LE can reference another service using
included services.

See Vol 3, Part G, section 2.6.3 of Core specification for more
details.
---
 attrib/gatt.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 attrib/gatt.h |   9 +++
 2 files changed, 201 insertions(+)

diff --git a/attrib/gatt.c b/attrib/gatt.c
index 882f3c1..d55e7fe 100644
--- a/attrib/gatt.c
+++ b/attrib/gatt.c
@@ -45,6 +45,22 @@ struct discover_primary {
 	void *user_data;
 };
 
+/* Used for the Included Services Discovery (ISD) procedure */
+struct included_discovery {
+	GAttrib		*attrib;
+	int		refs;
+	int		err;
+	uint16_t	end_handle;
+	GSList		*includes;
+	gatt_cb_t	cb;
+	void		*user_data;
+};
+
+struct included_uuid_query {
+	struct included_discovery	*isd;
+	struct gatt_included		*included;
+};
+
 struct discover_char {
 	GAttrib *attrib;
 	bt_uuid_t *uuid;
@@ -61,6 +77,25 @@ static void discover_primary_free(struct discover_primary *dp)
 	g_free(dp);
 }
 
+static struct included_discovery *isd_ref(struct included_discovery *isd)
+{
+	g_atomic_int_inc(&isd->refs);
+
+	return isd;
+}
+
+static void isd_unref(struct included_discovery *isd)
+{
+	if (g_atomic_int_dec_and_test(&isd->refs) == FALSE)
+		return;
+
+	isd->cb(isd->includes, isd->err, isd->user_data);
+
+	g_slist_free_full(isd->includes, g_free);
+	g_attrib_unref(isd->attrib);
+	g_free(isd);
+}
+
 static void discover_char_free(struct discover_char *dc)
 {
 	g_slist_free_full(dc->characteristics, g_free);
@@ -248,6 +283,163 @@ guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
 	return g_attrib_send(attrib, 0, buf[0], buf, plen, cb, dp, NULL);
 }
 
+static void resolve_included_uuid_cb(uint8_t status, const uint8_t *pdu,
+					uint16_t len, gpointer user_data)
+{
+	struct included_uuid_query *query = user_data;
+	struct included_discovery *isd = query->isd;
+	struct gatt_included *incl = query->included;
+	unsigned int err = status;
+	bt_uuid_t uuid;
+	size_t buflen;
+	uint8_t *buf;
+
+	if (err)
+		goto done;
+
+	buf = g_attrib_get_buffer(isd->attrib, &buflen);
+	if (dec_read_resp(pdu, len, buf, buflen) != 16) {
+		err = ATT_ECODE_IO;
+		goto done;
+	}
+
+	uuid = att_get_uuid128(buf);
+	bt_uuid_to_string(&uuid, incl->uuid, sizeof(incl->uuid));
+	isd->includes = g_slist_append(isd->includes, incl);
+
+done:
+	if (err)
+		g_free(incl);
+
+	if (isd->err == 0)
+		isd->err = err;
+
+	isd_unref(isd);
+
+	g_free(query);
+}
+
+static guint resolve_included_uuid(struct included_discovery *isd,
+					struct gatt_included *incl)
+{
+	struct included_uuid_query *query;
+	size_t buflen;
+	uint8_t *buf = g_attrib_get_buffer(isd->attrib, &buflen);
+	guint16 oplen = enc_read_req(incl->range.start, buf, buflen);
+
+	query = g_new0(struct included_uuid_query, 1);
+	query->isd = isd_ref(isd);
+	query->included = incl;
+
+	return g_attrib_send(isd->attrib, 0, buf[0], buf, oplen,
+				resolve_included_uuid_cb, query, NULL);
+}
+
+static struct gatt_included *included_from_buf(const uint8_t *buf, gsize len)
+{
+	struct gatt_included *incl = g_new0(struct gatt_included, 1);
+
+	incl->handle = att_get_u16(&buf[0]);
+	incl->range.start = att_get_u16(&buf[2]);
+	incl->range.end = att_get_u16(&buf[4]);
+
+	if (len == 8) {
+		bt_uuid_t uuid128;
+		bt_uuid_t uuid16 = att_get_uuid16(&buf[6]);
+
+		bt_uuid_to_uuid128(&uuid16, &uuid128);
+		bt_uuid_to_string(&uuid128, incl->uuid, sizeof(incl->uuid));
+	}
+
+	return incl;
+}
+
+static void find_included_cb(uint8_t status, const uint8_t *pdu, uint16_t len,
+							gpointer user_data);
+
+static guint find_included(struct included_discovery *isd, uint16_t start)
+{
+	bt_uuid_t uuid;
+	size_t buflen;
+	uint8_t *buf = g_attrib_get_buffer(isd->attrib, &buflen);
+	guint16 oplen;
+
+	bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
+	oplen = enc_read_by_type_req(start, isd->end_handle, &uuid,
+							buf, buflen);
+
+	return g_attrib_send(isd->attrib, 0, buf[0], buf, oplen,
+					find_included_cb, isd_ref(isd), NULL);
+}
+
+static void find_included_cb(uint8_t status, const uint8_t *pdu, uint16_t len,
+							gpointer user_data)
+{
+	struct included_discovery *isd = user_data;
+	uint16_t last_handle = isd->end_handle;
+	unsigned int err = status;
+	struct att_data_list *list;
+	int i;
+
+	if (err == ATT_ECODE_ATTR_NOT_FOUND)
+		err = 0;
+
+	if (status)
+		goto done;
+
+	list = dec_read_by_type_resp(pdu, len);
+	if (list == NULL) {
+		err = ATT_ECODE_IO;
+		goto done;
+	}
+
+	if (list->len != 6 && list->len != 8) {
+		err = ATT_ECODE_IO;
+		att_data_list_free(list);
+		goto done;
+	}
+
+	for (i = 0; i < list->num; i++) {
+		struct gatt_included *incl;
+
+		incl = included_from_buf(list->data[i], list->len);
+		last_handle = incl->handle;
+
+		/* 128 bit UUID, needs resolving */
+		if (list->len == 6) {
+			resolve_included_uuid(isd, incl);
+			continue;
+		}
+
+		isd->includes = g_slist_append(isd->includes, incl);
+	}
+
+	att_data_list_free(list);
+
+	if (last_handle < isd->end_handle)
+		find_included(isd, last_handle + 1);
+
+done:
+	if (isd->err == 0)
+		isd->err = err;
+
+	isd_unref(isd);
+}
+
+unsigned int gatt_find_included(GAttrib *attrib, uint16_t start, uint16_t end,
+					gatt_cb_t func, gpointer user_data)
+{
+	struct included_discovery *isd;
+
+	isd = g_new0(struct included_discovery, 1);
+	isd->attrib = g_attrib_ref(attrib);
+	isd->end_handle = end;
+	isd->cb = func;
+	isd->user_data = user_data;
+
+	return find_included(isd, start);
+}
+
 static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
 							gpointer user_data)
 {
diff --git a/attrib/gatt.h b/attrib/gatt.h
index 3862011..305b4c6 100644
--- a/attrib/gatt.h
+++ b/attrib/gatt.h
@@ -61,6 +61,12 @@ struct gatt_primary {
 	struct att_range range;
 };
 
+struct gatt_included {
+	char uuid[MAX_LEN_UUID_STR + 1];
+	uint16_t handle;
+	struct att_range range;
+};
+
 struct gatt_char {
 	char uuid[MAX_LEN_UUID_STR + 1];
 	uint16_t handle;
@@ -71,6 +77,9 @@ struct gatt_char {
 guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
 							gpointer user_data);
 
+unsigned int gatt_find_included(GAttrib *attrib, uint16_t start, uint16_t end,
+					gatt_cb_t func, gpointer user_data);
+
 guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
 					bt_uuid_t *uuid, gatt_cb_t func,
 					gpointer user_data);
-- 
1.7.12.2

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux