[PATCHv2 BlueZ 1/2] 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 |  198 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 attrib/gatt.h |    9 +++
 2 files changed, 207 insertions(+)

diff --git a/attrib/gatt.c b/attrib/gatt.c
index 1c3ff78..8abbc39 100644
--- a/attrib/gatt.c
+++ b/attrib/gatt.c
@@ -47,6 +47,22 @@ struct discover_primary {
 	void *user_data;
 };
 
+struct find_included {
+	GAttrib		*attrib;
+	uint16_t	end;
+	GSList		*includes;
+	gatt_cb_t	cb;
+	void		*user_data;
+	uint16_t	missing_uuids;	/* number of missing 128-bit UUID */
+	gboolean	final;		/* final data flag */
+	unsigned int	err;
+};
+
+struct find_include_data {
+	struct find_included *find_incl;
+	struct gatt_included *gatt_incl;
+};
+
 struct discover_char {
 	GAttrib *attrib;
 	bt_uuid_t *uuid;
@@ -63,6 +79,13 @@ static void discover_primary_free(struct discover_primary *dp)
 	g_free(dp);
 }
 
+static void find_included_free(struct find_included *fi)
+{
+	g_slist_free_full(fi->includes, g_free);
+	g_attrib_unref(fi->attrib);
+	g_free(fi);
+}
+
 static void discover_char_free(struct discover_char *dc)
 {
 	g_slist_free_full(dc->characteristics, g_free);
@@ -107,6 +130,16 @@ static guint16 encode_discover_primary(uint16_t start, uint16_t end,
 	return plen;
 }
 
+static size_t encode_find_included(uint16_t start, uint16_t end, uint8_t *pdu,
+								size_t len)
+{
+	bt_uuid_t uuid;
+
+	bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
+
+	return enc_read_by_type_req(start, end, &uuid, pdu, len);
+}
+
 static void primary_by_uuid_cb(guint8 status, const guint8 *ipdu,
 					guint16 iplen, gpointer user_data)
 
@@ -219,6 +252,149 @@ done:
 	discover_primary_free(dp);
 }
 
+static void find_included_helper(uint8_t status, const uint8_t *pdu,
+					uint16_t len, gpointer user_data)
+{
+	struct find_include_data *fid = user_data;
+	struct find_included *fi = fid->find_incl;
+	int buflen;
+	uint8_t *buf = g_attrib_get_buffer(fi->attrib, &buflen);
+
+	fi->missing_uuids--;
+
+	if (status) {
+		fi->err = status;
+		goto done;
+	}
+
+	if (dec_read_resp(pdu, len, buf, &buflen) != len) {
+		fi->err = ATT_ECODE_IO;
+		goto done;
+	}
+
+	if (buflen >= 16) {
+		bt_uuid_t uuid = att_get_uuid128(buf);
+		bt_uuid_to_string(&uuid, fid->gatt_incl->uuid,
+						sizeof(fid->gatt_incl->uuid));
+	}
+
+done:
+	if (fi->final && fi->missing_uuids == 0) {
+		fi->cb(fi->includes, fi->err, fi->user_data);
+		find_included_free(fi);
+	}
+
+	g_free(fid);
+}
+
+static void parse_included_uuid16(const struct att_data_list *list,
+						struct find_included *fi,
+						uint16_t *last_handle)
+{
+	unsigned int i;
+
+	for (i = 0; i < list->num; i++) {
+		struct gatt_included *include;
+		const uint8_t *data = list->data[i];
+		bt_uuid_t uuid128, uuid16;
+
+		include = g_new0(struct gatt_included, 1);
+		include->handle = att_get_u16(&data[0]);
+		include->range.start = att_get_u16(&data[2]);
+		include->range.end = att_get_u16(&data[4]);
+		fi->includes = g_slist_append(fi->includes, include);
+
+		uuid16 = att_get_uuid16(&data[6]);
+		bt_uuid_to_uuid128(&uuid16, &uuid128);
+		bt_uuid_to_string(&uuid128, include->uuid,
+							sizeof(include->uuid));
+
+		*last_handle = include->handle;
+	}
+}
+
+static void parse_included_uuid128(const struct att_data_list *list,
+						struct find_included *fi,
+						uint16_t *last_handle)
+{
+	unsigned int i;
+
+	for (i = 0; i < list->num; i++) {
+		const uint8_t *data = list->data[i];
+		struct gatt_included *include;
+		struct find_include_data *fid;
+		uint8_t *buf;
+		int buflen;
+		uint16_t plen;
+
+		include = g_new0(struct gatt_included, 1);
+		include->handle = att_get_u16(&data[0]);
+		include->range.start = att_get_u16(&data[2]);
+		include->range.end = att_get_u16(&data[4]);
+		fi->includes = g_slist_append(fi->includes, include);
+
+		fid = g_new0(struct find_include_data, 1);
+		fid->find_incl = fi;
+		fid->gatt_incl = include;
+
+		/* 128-bit UUID, we need to "resolve" it */
+		buf = g_attrib_get_buffer(fi->attrib, &buflen);
+		plen = enc_read_req(include->range.start, buf, buflen);
+		fi->missing_uuids++;
+		g_attrib_send(fi->attrib, 0, buf[0], buf, plen,
+					find_included_helper, fid, NULL);
+
+		*last_handle = include->handle;
+	}
+}
+
+static void included_cb(uint8_t status, const uint8_t *pdu, uint16_t len,
+							gpointer user_data)
+{
+	struct find_included *fi = user_data;
+	struct att_data_list *list;
+	uint16_t last_handle = fi->end;
+
+	if (status) {
+		if (status != ATT_ECODE_ATTR_NOT_FOUND)
+			fi->err = status;
+		fi->final = TRUE;
+		goto done;
+	}
+
+	list = dec_read_by_type_resp(pdu, len);
+	if (list == NULL) {
+		fi->err = ATT_ECODE_IO;
+		fi->final = TRUE;
+		goto done;
+	}
+
+	/* parse valid data only */
+	if (list->len == 6)
+		parse_included_uuid128(list, fi, &last_handle);
+	else if (list->len == 8)
+		parse_included_uuid16(list, fi, &last_handle);
+
+	att_data_list_free(list);
+
+	if (last_handle != fi->end) {
+		int buflen = 0;
+		uint8_t *buf = g_attrib_get_buffer(fi->attrib, &buflen);
+		size_t oplen = encode_find_included(last_handle + 1, fi->end,
+								buf, buflen);
+
+		g_attrib_send(fi->attrib, 0, buf[0], buf, oplen, included_cb,
+								fi, NULL);
+	} else
+		fi->final = TRUE;
+
+done:
+	if (fi->final && fi->missing_uuids == 0) {
+		fi->cb(fi->includes, fi->err, fi->user_data);
+		find_included_free(fi);
+	}
+}
+
 guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
 							gpointer user_data)
 {
@@ -249,6 +425,28 @@ 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);
 }
 
+unsigned int gatt_find_included(GAttrib *attrib, uint16_t start, uint16_t end,
+					gatt_cb_t func, gpointer user_data)
+{
+	struct find_included *fi;
+	int buflen;
+	uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
+	size_t plen;
+
+	plen = encode_find_included(start, end, buf, buflen);
+	if (plen == 0)
+		return 0;
+
+	fi = g_new0(struct find_included, 1);
+	fi->attrib = g_attrib_ref(attrib);
+	fi->end = end;
+	fi->cb = func;
+	fi->user_data = user_data;
+
+	return g_attrib_send(attrib, 0, buf[0], buf, plen, included_cb, fi,
+									NULL);
+}
+
 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 1732270..b420711 100644
--- a/attrib/gatt.h
+++ b/attrib/gatt.h
@@ -30,6 +30,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;
@@ -40,6 +46,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.10

--
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